# Συζήτηση Κυκλωμάτων > Ψηφιακά Κυκλώματα > Γενικά >  >  Πρόταση για λογικό κύκλωμα

## alefgr

Τον τελευταίο καιρό, προσπαθώ να αναπτύξω μια multi-nodes επικοινωνία μέσω του RS485 bus. Επειδή δεν θα υπάρχει master node και για να μειώσω τα conflicts, χρησιμοποιώ μία ακόμα γραμμή στο bus, που την έχω ονομάσει BUS-CTRL, και που δείχνει ανά πάσα στιγμή αν το bus είναι idle ή χρησιμοποιείται από κάποιο node.

Για να μειωθούν όσο γίνεται περισσότερο τα conflicts, θα πρέπει ο χρόνος ανάμεσα στον έλεγχο του BUS-CTRL και στην αλλαγή του σε high κατάσταση, να είναι όσο γίνεται μικρότερος. Αυτό που μπορώ να πετύχω μέσω software με τον 328 μ/ε και με κώδικα assembly, είναι 5 x 62,5nsec = 312,5nsec. Σε δοκιμές με 3 nodes, το baud rate στο bus ανάμεσα στα 100 με 200kb και με έντονη κίνηση πακέτων αναμεταξύ τους, ο ρυθμός των conflicts είναι 1 ανά 3-5 λεπτά. Για λιγότερα conflicts θα πρέπει να πετύχω μικρότερο χρόνο, οπότε θα πρέπει να στραφώ σε hardware υλοποίηση μέσω λογικού κυκλώματος.

temp.png

Σύμφωνα με το διάγραμμα, αυτό που θέλω είναι η έξοδος B να γίνεται high, μόνο όταν την στιγμή που το A γίνει high, το C να είναι low, αλλιώς αν το C έχει προλάβει να γίνει high, να παραμένει σε low κατάσταση το B, για να μπορώ να ελέγξω αν στο ενδιάμεσο κάποιο άλλο node πήρε τον έλεγχο, οπότε να ακυρώνω την αποστολή μηνύματος.

Προσπάθησα να σκεφτώ κάτι με flip-flop, αλλά δεν μπόρεσα να βρω κάτι έξυπνο που να ικανοποιεί την συνθήκη αυτή. Υπάρχει καμία καλή και έξυπνη ιδέα για την υλοποίηση του κυκλώματος;

----------


## nestoras

> ...αυτό που θέλω είναι η έξοδος B να γίνεται high, μόνο όταν την στιγμή που το A γίνει high, το C να είναι low, αλλιώς αν το C έχει προλάβει να γίνει high, να παραμένει σε low κατάσταση το B...



Μπορείς να ξαναγράψεις την παραπάνω πρόταση με πίνακα αληθείας;
Νομίζω ότι θα διευκολύνει αρκετό κόσμο για να σε βοηθήσει.

----------


## FILMAN

> temp.png
> 
> Σύμφωνα με το διάγραμμα, *αυτό που θέλω είναι η έξοδος B να γίνεται high, μόνο όταν την στιγμή που το A γίνει high, το C να είναι low, αλλιώς αν το C έχει προλάβει να γίνει high, να παραμένει σε low κατάσταση το B,* για να μπορώ να ελέγξω αν στο ενδιάμεσο κάποιο άλλο node πήρε τον έλεγχο, οπότε να ακυρώνω την αποστολή μηνύματος.
> 
> *Προσπάθησα να σκεφτώ κάτι με flip-flop, αλλά δεν μπόρεσα να βρω κάτι έξυπνο που να ικανοποιεί την συνθήκη αυτή.* Υπάρχει καμία καλή και έξυπνη ιδέα για την υλοποίηση του κυκλώματος;



 :W00t: 
Μα το μόνο που θες είναι ένα D flip - flop, το Α σου θα πάει στο CLK του flip - flop, το C σου θα πάει στο D του flip - flop, και από την ανάστροφη έξοδο Q' του flip - flop θα πάρεις το Β σου...

----------


## alefgr

Το πρόβλημα είναι ότι η είσοδος A έχει άμεση επίδραση στο C και αυτό μπερδεύει τα πράγματα. Πρέπει με κάποιο τρόπο το A αφού πάρει το OK να κάνει high το C, οπότε με ποιο τρόπο θα γίνει η σύνδεση μεταξύ τους χωρίς να επηρεαστεί το flip-flop; Το κακό είναι ότι έχω ξεμείνει από 4013 για να κάνω δοκιμές. Θα κάνω μια παραγγελία επ' ευκαιρία στο eBay, αλλά θα αργήσουν να έρθουν για να κάνω την δοκιμή. Φυσικά να κατεβαίνω Αθήνα μόνο για αυτά δεν λέει...

Πάντως με νεότερη βελτίωση στον κώδικα πέτυχα 4 κύκλους καθυστέρηση, δηλαδή πήγα στα 250nsec. Ποιο κάτω το βλέπω δύσκολα να το πηγαίνω με κώδικα. Ακόμα όμως και έτσι, πάλι υπάρχουν τα conflicts στο έντονο traffic.

Ασχολούμενος με την ανάλυση των conflicts, διαπίστωσα πως μια χρήσιμη δυνατότητα που δυστυχώς δεν έχει ο Rigol που διαθέτω, αλλά υποθέτω πρέπει να έχουν οι πιο ακριβοί ψηφιακοί παλμογράφοι, είναι το OR ή το AND ανάμεσα στα σήματα που ελέγχει το trigger. Δηλαδή να δηλώνεις 2 ή 3 channels σαν triggers και σε όποιο υπάρξει αλλαγή (στην περίπτωση του OR) να σου παγώνει την ένδειξη. Δεν δοκίμασα το πρόγραμμα του Saleae αν έχει τέτοιες δυνατότητες.

----------


## FILMAN

> Το πρόβλημα είναι ότι η είσοδος A έχει άμεση επίδραση στο C και αυτό μπερδεύει τα πράγματα. Πρέπει με κάποιο τρόπο το A αφού πάρει το OK να κάνει high το C, οπότε με ποιο τρόπο θα γίνει η σύνδεση μεταξύ τους χωρίς να επηρεαστεί το flip-flop; Το κακό είναι ότι έχω ξεμείνει από 4013 για να κάνω δοκιμές.



Εννοείς ότι το σήμα C είναι το αποτέλεσμα πράξης OR μεταξύ του Α και ενός άλλου σήματος; Αν είναι έτσι, ένα μικρό RC καθυστέρησης του σήματος C προτού αυτό εφαρμοστεί στο D του flip - flop θα σου λύσει το πρόβλημα.

Αν η τάση είναι 5V σου κάνει και το 7474 (αν το έχεις κι αυτό βέβαια), προσοχή όμως γιατί έχει διαφορετική διάταξη ακροδεκτών σε σχέση με το 4013 και επίσης το set και το reset του (συνήθως το λένε clear) είναι αρνητικής λογικής

4013 δεν θα έχει ο Τριδήμας;

----------


## alefgr

Βρήκα να έχω 3 από αυτά, τα 74LS74. Όσο για τον Τριδήμα έχει μείνει πίσω στα εξαρτήματα. Συν ότι πρέπει να του στείλω μια-δυό μέρες πριν ένα mail με τα υλικά που θέλω, για να μην κάθεται ο Ανδρέας να τα βγάζει την τελευταία στιγμή. Από την άλλη η συνήθεια είναι πολύ κακό πράγμα. Έχω καλοσυνηθείσει να μου έρχονται όλα στο σπίτι από το eBay, και μου φαίνεται "βουνό" να τρέχω για υλικά στην Αθήνα, ενώ κάποτε κατέβαινα για "ψίλου πήδημα" και μάλιστα με την συγκοινωνία.  :Sad: 

Anyway. Στο θέμα μας. Επειδή μια εικόνα ίσον χίλιες λέξεις, για να καταλάβεις τον τρόπο που δουλεύει το Bus-Ctrl, βάζω το σχέδιο που δουλεύω στα nodes.

sch.png

----------


## SProg

> Πάντως με νεότερη βελτίωση στον κώδικα πέτυχα 4 κύκλους καθυστέρηση, δηλαδή πήγα στα 250nsec. Ποιο κάτω το βλέπω δύσκολα να το πηγαίνω με κώδικα. .



Κατι δε βγαζει νοημα με το χρονο.Εκτος αν μεσα στη main ασχολησε μονο με αυτο το σημα.Ανεβασε το κωδικα σε ASM που λες.Ελπιζω να μην εκτελειται μεσω διακοπης.

----------


## alefgr

Ο κώδικας είναι αρκετά απλός και δεν εκτελείται μέσα από interrupt:




```
BOOL    RS485SerialBusCtrlHold()
{
    asm (
        "ldi r24, 0x04  \n"     // preload B00000100
        "sbic 0x03, 1   \n"     // read bus_state [D9 pin-15] (1 cycle if false)
        "rjmp _take_bus \n"     // (2 cycles)
        "ldi r24, 0x00  \n"     // return false
        "ret            \n"
        "_take_bus:     \n"
        "out 0x03, r24  \n"     // write bus_ctrl high [D10 pin-16] (1 cycle)
        "ldi r24, 0x01  \n"     // return true
    );
}
```


Επιστρέφοντας η ρουτίνα RS485SerialBusCtrlHold μας επιστρέφει αν πήρε τον έλεγχο του bus.

----------


## Fire Doger

Γιατί φορτώνεις 0x04 στον R24 και βγάζεις όλο τον R24 και δεν κάνεις απλώς SBI στο Port σου το control pin? "SBI 0x03, 2" (ή 3 δεν θυμάμαι αν το 1ο bit είναι 0 ή 1)
Έτσι κλείνεις τις pullup ή στέλνεις 0 στα υπόλοιπα 7 pin και έχεις και 1 κύκλο παραπάνω για το LDI

----------


## alefgr

Μα η πρώτη εντολή μας είναι αδιάφορο για τον ένα κύκλο καθυστέρησης που μας κάνει. Από την 2η εντολή που είναι ο έλεγχος αρχίζει να μετράει η καθυστέρηση μέχρι την 6η που είναι εντολή toggle για αλλαγή κατάστασης. Έτσι κι αλλιώς θα έχουμε 1 κύκλο με την εντολή ελέγχου και 1 κύκλο με την εντολή αλλαγή κατάστασης. Δυστυχώς την σημαντική καθυστέρηση την έχουμε με την rjmp που είναι 2 κύκλοι. Υπάρχει τρόπος να την αποφύγουμε αλλά ταυτόχρονα να έχουμε και την γνώση στο τελείωμα του κώδικα, αν όντως έχουμε κάνει high το pin ή αν ήταν ήδη high;

Μια ιδέα που μου ήρθε τώρα είναι να διαβάσουμε όλο το περιεχόμενο του port B σε έναν καταχωρητή πχ r24, να δώσουμε εντολή κατάστασης high στο D10 και μετά να κάνουμε τον έλεγχο το τι ήταν πριν το D9.

----------


## alefgr

Υπάρχουν νεότερα. Με τον ακόλουθο κώδικα πέτυχα 2 κύκλους οπότε πάμε στα 125nsec. Ποιο κάτω δεν πάει!  :Rolleyes:  Δεν υπάρχει εντολή που ταυτόχρονα να διαβάζει και να γράφει...




```
BOOL    RS485SerialBusCtrlHold()
{
    asm (
        "cli                    \n"
        "ldi    r25, 0b00000100 \n" // load bit 2
        "in     r24, 0x03       \n" // read PORTB (1 cycle)
        "out    0x03, r25       \n" // set bus_ctrl high [D10 pin-16] (1 cycle)
        "sei                    \n"
        "andi   r24, 0b00000010 \n" // check bus_state [D9 pin-15]
        "brne   _bus_ok         \n"
        "out    0x03, r25       \n" // set bus_ctrl low
        "ldi    r24, 0x00       \n" // not take it the bus
        "ret                    \n"
        "_bus_ok:               \n"
        "ldi    r24, 0x01       \n" // take it the bus
    );
}
```

----------


## alefgr

Τελικά η προηγούμενη ιδέα είναι τελείως λάθος. Τα errors αυξήθηκαν σημαντικά. Ο νέος κώδικας είναι μια μικρή παραλλαγή του πρώτου που δίνει καθυστέρηση από τους 4 στους 3 κύκλους.




```
BOOL    RS485SerialBusCtrlHold()
{
    asm (
        "cli                    \n"
        "ldi    r24, 0b00000100 \n" //      preload pin 2
        "sbis   0x03, 1         \n" // 2c   read bus_state [D9 pin-15]
        "rjmp   _busy_bus       \n"
        "out    0x03, r24       \n" // 1c   write bus_ctrl high [D10 pin-16]
        "ldi    r24, 0x01       \n" //      take it the bus
        "sei                    \n"
        "ret                    \n"
        "_busy_bus:             \n"
        "ldi    r24, 0x00       \n" //      not take it the bus
        "sei                    \n"
    );
}
```


Τα collisions έχουν μειωθεί σημαντικά. Κατά μέσω όρο σε κάθε node είναι στα 4,3 ανά ώρα.

Μιας και οι είσοδοι του Rigol δεν μου φτάνανε, ανέλυσα τα σήματα μέσω του Saleae Analyzer. Αν δείχνει σωστά και δεν δείχνει άλλα-αντί-άλλων, υπάρχει μια σημαντική καθυστέρηση περίπου στα 460 nsec ανάμεσα στην ενεργοποίηση του driver του RS485 tranceiver και στην αλλαγή κατάστασης του bus-ctrl. Μου είναι δύσκολο να πιστέψω ότι μία CMOS πύλη προκαλεί τόσο μεγάλη καθυστέρηση. Δυστυχώς στο θέμα καθυστέρησης τα TTL ολοκληρωμένα, υπερτερούν σημαντικά των CMOS. Αύριο θα κάνω αλλαγή στο κύκλωμα και αντί πυλών αντιστροφέων θα συνδέσω απλά transistors.

006.png

Τα 0-2ch είναι το σήμα στο DE του SN75176 από κάθε node, το 3ch είναι το bus-ctrl, στο 4ch είναι η συλλογή των timeout errors και των τριων nodes που είναι και το trigger, και τέλος 5-7ch είναι τα σήματα που λαμβάνουν τα nodes.

005.png

Εδώ είναι η χαρακτηριστική περίπτωση ενός collision ανάμεσα στο node 0 και 2. Η διαφορά χρόνου μόλις στα 125 nsec. Δηλαδή μόλις 2 κύκλοι του ATMEGA328.

----------

