Μηχανήματα πεπερασμένης κατάστασης και μικροελεγκτές

ΕΚΕΙΝΟΣ ΚΑΙ ΕΚΕΙΝΟΣ - ΔΕ ΘΥΜΑΣΑΙ (Ενδέχεται 2019).

$config[ads_text] not found
Anonim

Μηχανήματα πεπερασμένης κατάστασης και μικροελεγκτές


Μηχανήματα πεπερασμένων κρατών: Ο καλύτερος τρόπος για να κωδικοποιήσετε!

Η χρήση μηχανών πεπερασμένων καταστάσεων (FSM) είναι μια κοινή βιομηχανική πρακτική στην κωδικοποίηση, αλλά η χρήση τους εκτείνεται πέρα ​​από το καθημερινό λογισμικό. Μπορούν εύκολα να χρησιμοποιηθούν σε έργα, να βοηθήσουν στην πρόληψη σφαλμάτων, να σταματήσουν άπειρες βρόχους και να χαλαρώσουν τον επεξεργαστή, και να διευκολύνουν το σφάλμα.

Το πρόβλημα

Ένα θέμα που οι περισσότεροι (αν όχι όλοι) χομπίστες και μηχανικοί θα πέσουν είναι ο φοβερός άπειρος βρόχος. Αυτό το σφάλμα εντοπίζεται συνήθως σε συστήματα όπου μια μικροεπεξεργαστή διαβάζει από τη μνήμη I2C, ελέγχει για διακοπές ή για να διαπιστώσει αν πληρούται μια συνθήκη. Πολλοί χρήστες αναρωτιούνται πώς είναι δυνατόν να αποφευχθούν τέτοια σφάλματα. Μια λύση είναι η χρήση χρονιστών και φύλαξης για την επαναφορά του συστήματος, αλλά αυτή είναι η κακή πρακτική, καθώς τα δεδομένα μπορούν να χαθούν, οι γραμμές λεωφορείων μπορούν να κλειδωθούν (το I2C είναι ιδιαίτερα κακό για αυτό) και μπορεί να προκύψουν απροσδόκητες συμπεριφορές στο σύστημα.

Η πραγματική απάντηση σε τέτοια προβλήματα είναι η χρήση μηχανών πεπερασμένων καταστάσεων που ανακάλυψα πριν από δύο μήνες κατά τον προγραμματισμό του ελεγκτή IO του ZBM. Οι μηχανές πεπερασμένης κατάστασης φαίνονται πολύ περίπλοκες και οι εξηγήσεις τους είναι εξίσου αποθαρρυντικές, αλλά εκπληκτικά είναι μία από τις ευκολότερες μεθόδους προγραμματισμού για ενσωμάτωση.

Σημείωση: Αυτό το άρθρο θα εξετάσει μηχανές πεπερασμένης κατάστασης στο C αλλά είναι δυνατές σε οποιαδήποτε γλώσσα (συμπεριλαμβανομένης της συναρμολόγησης).

Τι είναι τα πεπερασμένα μηχανήματα "hljs"> STATE = "START"; Κενό κύριο (άκυρο) {do {if (STATE == "START") {startCode (); } else αν (STATE == "MIDDLE") {middleCode (); } else αν (STATE == "END") {endCode (); } αλλιώς {STATE = "START"; }} ενώ (1); } void startCode () {// Κάποιος κωδικός εδώ STATE = "ΜΕΣΑ"; } void middleCode () {// Κάποιος κωδικός εδώ STATE = "END"; } void endCode () {// Μερικός κωδικός εδώ STATE = "START"; }}

Όταν ο κώδικας αρχικοποιηθεί, η κατάσταση του συστήματος έχει οριστεί σε "εκκίνηση". Αυτό διασφαλίζει ότι έχουμε ένα σημείο εισόδου που θα πάρει το σύστημα πηγαίνει. Στη συνέχεια, όταν εκτελεστεί ο κώδικας στο startCode (), η κατάσταση θα αλλάξει σε "μεσαία". Αυτό σημαίνει ότι κατά την επόμενη επανάληψη του βρόχου do-while στο main θα εκτελεστεί η λειτουργία middleCode (). Όταν ολοκληρωθεί αυτή η λειτουργία, η κατάσταση θα ρυθμιστεί τώρα στο "τέλος". Σε άλλη επανάληψη του κύριου βρόχου, θα καλείται η λειτουργία endCode (). Μόλις εκτελεστεί αυτό, η κατάσταση αλλάζει σε "εκκίνηση". Αυτό επαναλαμβάνεται για πάντα, με κάθε λειτουργία να καλείται με τη σειρά που εμφανίζεται στον κώδικα.

Αυτό είναι μόνο χρήσιμο με τον κώδικα που βρίσκεται μέσα στις λειτουργίες κατάστασης (startCode, middleCode και endCode). Εάν ο κώδικας μέσα σε αυτές τις λειτουργίες ΔΕΝ είναι συμβατός με το FSM, μπορούν να αντιμετωπιστούν ακόμα άπειρες θηλιές και συνεπώς να καταστήσει όλο αυτό το κώδικα άσκοπο. Το πραγματικό τέχνασμα κάνει "πεπερασμένο κράτος συμβατό κώδικα". Πώς θα το κάνουμε αυτό; Ας ξεκινήσουμε ρίχνοντας μια ματιά στον παλιό κώδικα ατελείωτου βρόχου.

 sendI2CStartBit(); while(I2CSTARTWAIT); 

Αυτή η λειτουργία θα περιμένει μέχρι να αποσταλεί το bit έναρξης από τη μονάδα I2C που βρίσκεται πάνω στο PIC. Αλλά τι θα συνέβαινε αν δεν μπορούσε να ξεκινήσει το bit έναρξης;

Το PIC έχει συνήθεια σε μερικά σενάρια όπου ο δίαυλος I2C μπορεί να κλειδώσει και ο έλεγχος δεν μπορεί να αποκατασταθεί. Αυτό σημαίνει ότι ο κωδικός σας θα καθίσει για πάντα στο ότι ενώ βρόχο. Αυτό που είναι χειρότερο είναι ότι, ακόμη και αν προσπαθήσετε να ξαναρχίσετε το bit έναρξης, δεν υπάρχει καμία πιθανότητα να ξεκλειδώσει το λεωφορείο. Αυτό είναι όπου το πεπερασμένο μηχανή κατάσταση πραγματικά πέτρες!

Ας ρίξουμε μια ματιά σε μια γραφική διάταξη μιας μηχανής πεπερασμένων καταστάσεων I2C που πρόκειται να συμπεριληφθεί στο κύριο πρόγραμμα μας.

Προγραμματίστε ως FSM

Πρώτον, υπάρχουν δύο μηχανές πεπερασμένων καταστάσεων! Η πρώτη FSM είναι ο κύριος κώδικας που βγαίνει γύρω, καλώντας τις τρεις κύριες λειτουργίες. Η δεύτερη μηχανή πεπερασμένης κατάστασης είναι ο χειριστής I2C που μπορεί να βρίσκεται σε διάφορες πιθανές καταστάσεις, με τη σημαντικότερη κατάσταση να είναι η αδρανής κατάσταση.

Η κατάσταση αδράνειας είναι σημαντική επειδή θα μας πει ότι το σύστημα I2C δεν κάνει τίποτα - στο σημείο αυτό μπορούμε να το ζητήσουμε να στείλει bits εκκίνησης, να στείλει ένα byte, να λάβει ένα byte ή οποιαδήποτε άλλη λειτουργία που σχετίζεται με το I2C. Ας δούμε λοιπόν τα σπλάχνα του χειριστή I2C στην απλούστερη μορφή του!

 Void I2cupdate() { if(i2cState == “STARTBIT”) { sendI2CStartBit(); i2cState = “CHECKSTART”; } if(i2cState == “CHECKSTART”) { if(I2CSTARTWAIT == 0) { i2cState = “I2CIDLE” } } if(i2cState == “I2CIDLE”) { } } 

Λοιπόν, πώς παίρνουμε τον χειριστή I2C να στείλει ένα bit έναρξης; Απλός. Εάν ένα κομμάτι κώδικα χρειάζεται να χρησιμοποιήσει το I2C, ελέγχετε πρώτα ότι το I2C είναι αδρανές, τότε ρυθμίζετε το i2cState σε "startbit".

 SomeFunction() { if(i2cState == “I2cIDLE”) { i2cState = “STARTBIT”; } } 

Στη συνέχεια, καθώς το κύριο πρόγραμμα βρόχων συνεχώς, ο χειριστής I2C θα ενημερώνεται σε κάθε επανάληψη. Και αν ο δίαυλος I2C κλειδώσει ο χειριστής I2C θα κολλήσει σε μια κατάσταση, αλλά το υπόλοιπο του προγράμματος θα συνεχιστεί. Πώς γνωρίζουμε πότε γίνεται ο χειριστής I2C; Δοκιμάστε την τιμή i2cState αλλά ΔΕΝ εκτελείτε ένα βρόχο while:

 // This is OK: SomeFunction() { if(i2cState == “I2CIDLE”) { next state for this function; } } // This is NOT ok: SomeFunction() { while(i2cState != “I2CIDLE”); } 

Τώρα λοιπόν ας ασχοληθούμε με το ζήτημα κλειδώματος του I2C το οποίο εμπόδισε τον χειριστή I2C να λειτουργήσει σωστά. Τα καλά νέα είναι ότι ο χειριστής του I2C θα συνεχίσει να τρέχει μέσω των εσωτερικών κρατικών ελέγχων και θα περάσει ακόμα από την κατάσταση "checkstart". Αυτό σημαίνει ότι μπορούμε να συμπεριλάβουμε έναν μετρητή χρονοδιακόπτη ή βρόχο που θα αυξάνεται με ένα πέρασμα. Στη συνέχεια, αν αυτός ο μετρητής βρόχου φτάσει σε μια προκαθορισμένη τιμή (TIMEOUT), μπορούμε να συμπεράνουμε ότι έχει συμβεί κάτι κακό και επαναφέρεται έτσι η κατάσταση I2C να είναι αδρανής. Ταυτόχρονα, μπορούμε επίσης να ορίσουμε μια σημαία ώστε όλες οι άλλες λειτουργίες να μπορούν να προειδοποιηθούν για το σφάλμα.

Ας ρίξουμε μια ματιά στον τροποποιημένο κώδικα I2C:

 SomeFunction() { if(internalState == “STATE1”) { if(i2cState == “I2cIDLE”) { i2cState = “STARTBIT”; internalState = “STATE2”; } } else if(internalState == “STATE2”) { if(i2cState == “I2cIDLE”) { if(i2cError == 1) { // Error handling code here } else { // Send data, read data or any old code internalState = “STATE1”; } } } } 

Έτσι τώρα που μπορούμε να εκφράσουμε τις άπειρες λειτουργίες βρόχου μας ως μηχανές πεπερασμένων καταστάσεων, το πρόγραμμά μας αρχίζει να μοιάζει με το ακόλουθο (γραφική μορφή):

Σχεδιασμός FSM με λειτουργίες mini-FSM

Η κύρια λειτουργία είναι μια μηχανή πεπερασμένων καταστάσεων που καλεί κάθε μία από τις μηχανές δευτερεύουσας κατάστασης. Όταν καλούνται αυτές οι μηχανές υπο-κατάσταση, εκτελούν τους δικούς τους ελέγχους και τις εσωτερικές κρατικές αλλαγές. Κάθε μία από αυτές τις μηχανές υπο-κατάστασης μπορεί να ανιχνεύσει τις καταστάσεις άλλων μηχανών υπο-κατάστασης έτσι ώστε να μπορούν να επικοινωνούν και να ελέγχουν ο ένας τον άλλον. Συνήθως η ρύθμιση που θα πάρετε είναι μερικά στρώματα μηχανών κατάστασης με το ανώτατο στρώμα να είναι η κύρια λειτουργία που ακολουθείται από το δεύτερο στρώμα προγράμματος που θα ζητήσει δεδομένα I2C, θα στείλει byte UART και θα ζητήσει την είσοδο του χρήστη. Τα τελικά στρώματα (δεξιά στο κάτω μέρος που συνήθως ΔΕΝ επικοινωνούν με τίποτα εκτός από τον εαυτό τους) χειρίζονται IO και συσκευές όπως πρόσβαση I2C, SPI και IO.

Στρώση προβολής

Τώρα που έχετε διαβάσει αυτό το άρθρο, προχωρήστε και πείραμα! Ξεκινήστε με μια απλή λειτουργία που ενεργοποιεί και απενεργοποιεί μια ενδεικτική λυχνία ανάλογα με την εσωτερική της κατάσταση. Στη συνέχεια, όταν το κάνετε αυτό, πάρετε μια δευτερεύουσα μηχανή πεπερασμένης κατάστασης για να ελέγξετε τη μηχανή πεπερασμένων καταστάσεων LED έτσι ώστε να ενεργοποιείται και να απενεργοποιείται ανάλογα με κάποια άλλη εξωτερική συνθήκη που δεν έχει καμία σχέση με τη λυχνία LED.

Απλά θυμηθείτε: Ποτέ, ποτέ να χρησιμοποιήσετε ένα βρόχο ενώ δεν ξέρετε ότι θα βγει (π.χ. μετά από 5 επαναλήψεις). Τώρα για την ανταμοιβή σας: μια ολοκληρωμένη μηχανή πεπερασμένων καταστάσεων I2C! Όλες αυτές οι ανάγκες είναι I2C_INIT () που ονομάζεται στην αρχή και στη συνέχεια απλά καλέστε το i2c_update () στο κύριο βρόχο.

Λήψη κώδικα