- Τι είναι το Multitasking;
- Γιατί να παραλείψετε την καθυστέρηση () στο Arduino;
- Γιατί να χρησιμοποιήσετε millis ();
- Απαιτούμενα στοιχεία
- Διάγραμμα κυκλώματος
- Προγραμματισμός του Arduino UNO για πολλαπλές εργασίες
Το multitasking οδήγησε τους υπολογιστές σε μια επανάσταση όπου ένα ή περισσότερα προγράμματα μπορούν να εκτελούνται ταυτόχρονα, αυξάνοντας την αποδοτικότητα, την ευελιξία, την προσαρμοστικότητα και την παραγωγικότητα. Στα ενσωματωμένα συστήματα, οι μικροελεγκτές μπορούν επίσης να χειριστούν το Multitasking και εκτελεί ταυτόχρονα δύο ή περισσότερες εργασίες χωρίς να σταματήσει τις τρέχουσες οδηγίες.
Εδώ σε αυτό το σεμινάριο θα μάθουμε πώς εκτελεί το Arduino Multitasking με τη λειτουργία Arduino millis. Γενικά , η συνάρτηση καθυστέρησης () χρησιμοποιείται στο Arduino για μια περιοδική εργασία όπως το LED που αναβοσβήνει, αλλά αυτή η λειτουργία καθυστέρησης () σταματά το πρόγραμμα για ορισμένο χρόνο και δεν επιτρέπει την εκτέλεση άλλων λειτουργιών. Έτσι, αυτό το άρθρο εξηγεί πώς μπορούμε να αποφύγουμε τη χρήση της λειτουργίας καθυστέρησης () και να την αντικαταστήσουμε με χιλιοστά () για να εκτελέσουμε περισσότερες από μία εργασίες ταυτόχρονα και να κάνουμε το Arduino ένα χειριστήριο πολλαπλών εργασιών. Πριν προχωρήσουμε στη λεπτομέρεια, ας ξεκινήσουμε με την υποτίμηση του Multitasking.
Τι είναι το Multitasking;
Πολλαπλή εργασία σημαίνει απλώς την εκτέλεση περισσότερων από μία εργασιών ή προγραμμάτων ταυτόχρονα ταυτόχρονα. Σχεδόν όλα τα λειτουργικά συστήματα διαθέτουν πολλαπλές εργασίες. Αυτό το είδος λειτουργικών συστημάτων είναι γνωστό ως MOS (λειτουργικό σύστημα πολλαπλών εργασιών). Το MOS μπορεί να είναι λειτουργικό σύστημα για φορητούς ή επιτραπέζιους υπολογιστές. Το καλό παράδειγμα πολλαπλών εργασιών σε υπολογιστές είναι όταν οι χρήστες εκτελούν την εφαρμογή email, το πρόγραμμα περιήγησης Διαδικτύου, το πρόγραμμα αναπαραγωγής πολυμέσων, τα παιχνίδια, ταυτόχρονα και εάν οι χρήστες δεν θέλουν να χρησιμοποιήσουν την εφαρμογή, εκτελείται στο παρασκήνιο εάν δεν είναι κλειστή. Ο τελικός χρήστης χρησιμοποιεί όλες αυτές τις εφαρμογές ταυτόχρονα, αλλά το OS παίρνει αυτήν την ιδέα λίγο διαφορετική. Ας συζητήσουμε πώς το OS διαχειρίζεται πολλαπλές εργασίες.
Όπως φαίνεται στην εικόνα, η CPU διαιρεί τον χρόνο στα τρία ίσα μέρη και εκχωρεί κάθε μέρος σε κάθε εργασία / εφαρμογή. Έτσι γίνεται η πολλαπλή εργασία στα περισσότερα από τα συστήματα. Η ιδέα θα είναι σχεδόν ίδια για το Arduino Multitasking, εκτός από το ότι η κατανομή του χρόνου θα είναι λίγο διαφορετική. Δεδομένου ότι το Arduino λειτουργεί σε χαμηλή συχνότητα και RAM σε σύγκριση με Laptop / Mobile / PC, οπότε ο χρόνος που δίνεται σε κάθε εργασία θα είναι επίσης διαφορετικός. Το Arduino έχει επίσης μια λειτουργία καθυστέρησης () που χρησιμοποιείται ευρέως. Αλλά πριν ξεκινήσουμε ας συζητήσουμε για αυτό γιατί δεν πρέπει να χρησιμοποιήσουμε τη λειτουργία καθυστέρησης () σε κανένα έργο.
Γιατί να παραλείψετε την καθυστέρηση () στο Arduino;
Εάν ληφθεί υπόψη η τεκμηρίωση αναφοράς του Arduino, τότε υπάρχουν δύο τύποι συναρτήσεων καθυστέρησης, η πρώτη είναι η καθυστέρηση () και η δεύτερη είναι η καθυστέρηση Μικροδευτερόλεπτα (). Και οι δύο λειτουργίες είναι πανομοιότυπες όσον αφορά τη δημιουργία καθυστέρησης. Η μόνη διαφορά είναι ότι, στη συνάρτηση καθυστέρησης (), ο ακέραιος παράμετρος που έχει περάσει είναι σε χιλιοστά του δευτερολέπτου, δηλαδή αν γράψουμε καθυστέρηση (1000) τότε η καθυστέρηση θα είναι 1000 χιλιοστά του δευτερολέπτου, δηλαδή 1 δευτερόλεπτο. Ομοίως, στη συνάρτηση delayMicroseconds (), η παράμετρος που έχει περάσει είναι σε μικροδευτερόλεπτα, δηλαδή αν γράψουμε delayMicroseconds (1000), τότε η καθυστέρηση θα είναι 1000 μικροδευτερόλεπτα, δηλαδή 1 milliseconds.
Εδώ έρχεται το σημείο, και οι δύο λειτουργίες διακόπτουν το πρόγραμμα για το χρονικό διάστημα που πέρασε στη λειτουργία καθυστέρησης. Αν λοιπόν δίνουμε καθυστέρηση 1 δευτερολέπτου, τότε ο επεξεργαστής δεν μπορεί να πάει στην επόμενη οδηγία έως ότου περάσει 1 δευτερόλεπτο. Ομοίως, εάν η καθυστέρηση είναι 10 δευτερόλεπτα, τότε το πρόγραμμα θα σταματήσει για 10 δευτερόλεπτα και ο επεξεργαστής δεν θα επιτρέψει να ακολουθήσει τις επόμενες οδηγίες μέχρι να περάσουν τα 10 δευτερόλεπτα. Αυτό εμποδίζει την απόδοση του μικροελεγκτή ως προς την ταχύτητα και την εκτέλεση των οδηγιών.
Το καλύτερο παράδειγμα για να εξηγήσετε το μειονέκτημα της λειτουργίας καθυστέρησης είναι να χρησιμοποιήσετε δύο κουμπιά. Σκεφτείτε ότι θέλουμε να αλλάξουμε δύο LED χρησιμοποιώντας δύο κουμπιά. Αν λοιπόν πατηθεί ένα κουμπί, τότε η αντίστοιχη λυχνία LED θα ανάψει για 2 δευτερόλεπτα, ομοίως εάν πατηθεί το δεύτερο, τότε η λυχνία LED πρέπει να ανάβει για 4 δευτερόλεπτα. Αλλά όταν χρησιμοποιούμε καθυστέρηση (), εάν ο χρήστης πατήσει το πρώτο κουμπί, τότε το πρόγραμμα θα σταματήσει για 2 δευτερόλεπτα και εάν ο χρήστης πατήσει το δεύτερο κουμπί πριν από καθυστέρηση 2 δευτερολέπτων, τότε ο μικροελεγκτής δεν θα δεχτεί την είσοδο καθώς το πρόγραμμα είναι σε στάση.
Η επίσημη τεκμηρίωση του Arduino το αναφέρει σαφώς στην περιγραφή της λειτουργίας Σημειώσεις και Προειδοποιήσεις καθυστέρησης (). Μπορείτε να το δείτε και να το ελέγξετε για να το καταστήσετε πιο σαφές
Γιατί να χρησιμοποιήσετε millis ();
Για να ξεπεραστεί το πρόβλημα που προκαλείται από τη χρήση καθυστέρησης, ένας προγραμματιστής θα πρέπει να χρησιμοποιεί τη λειτουργία millis () που είναι εύκολη στη χρήση μόλις γίνετε συνήθης και θα χρησιμοποιεί 100% απόδοση CPU χωρίς να δημιουργεί καθυστέρηση στην εκτέλεση των οδηγιών. Το millis () είναι μια συνάρτηση που επιστρέφει μόλις το χιλιοστό του δευτερολέπτου που έχει παρέλθει από τότε που ο πίνακας Arduino άρχισε να τρέχει το τρέχον πρόγραμμα χωρίς να παγώσει το πρόγραμμα. Αυτός ο αριθμός χρόνου θα υπερχειλίσει (δηλαδή θα επιστρέψει στο μηδέν), μετά από περίπου 50 ημέρες.
Ακριβώς όπως το Arduino έχουν καθυστέρηση Μικροδευτερόλεπτα (), έχει επίσης τη μικρο έκδοση του millis () ως micros (). Η διαφορά μεταξύ micros και millis είναι ότι, τα micros () θα ξεχειλίσουν μετά από περίπου 70 λεπτά, σε σύγκριση με τα millis () που είναι 50 ημέρες. Έτσι ανάλογα με την εφαρμογή μπορείτε να χρησιμοποιήσετε millis () ή micros ().
Χρήση millis () αντί καθυστέρησης ():
Για να χρησιμοποιήσετε το millis () για χρονισμό και καθυστέρηση, πρέπει να καταγράψετε και να αποθηκεύσετε τον χρόνο κατά τον οποίο πραγματοποιήθηκε η ενέργεια για να ξεκινήσετε την ώρα και, στη συνέχεια, να ελέγξετε ανά διαστήματα αν έχει περάσει ο καθορισμένος χρόνος. Έτσι, όπως δηλώνεται, αποθηκεύστε την τρέχουσα ώρα σε μια μεταβλητή.
unsigned long currentMillis = millis ();
Χρειαζόμαστε δύο ακόμη μεταβλητές για να μάθουμε αν έχει περάσει ο απαιτούμενος χρόνος. Έχουμε αποθηκεύσει την τρέχουσα ώρα στη μεταβλητή currentMillis, αλλά πρέπει επίσης να γνωρίζουμε ότι πότε ξεκίνησε η χρονική περίοδος και πόσο καιρό είναι η περίοδος. Έτσι δηλώνεται το Interval και το προηγούμενοMillis . Το διάστημα θα μας πει την καθυστέρηση χρόνου και το previosMillis θα αποθηκεύσει την τελευταία φορά που συνέβη το συμβάν.
unsigned long sebelumnyaMillis; μη υπογεγραμμένη μακρά περίοδο = 1000;
Για να το καταλάβουμε, ας πάρουμε ένα παράδειγμα ενός απλού LED που αναβοσβήνει. Η περίοδος = 1000 θα μας πει ότι το LED θα αναβοσβήνει για 1 δευτερόλεπτο ή 1000ms.
const int ledPin = 4; // ο αριθμός πινέζας LED συνδεδεμένος int ledState = LOW; // χρησιμοποιείται για τη ρύθμιση της κατάστασης LED χωρίς υπογραφή πολύ προηγούμενοMillis = 0; // θα αποθηκεύσει την τελευταία φορά που αναβοσβήνει το LED για μεγάλο χρονικό διάστημα = 1000; // περίοδος κατά την οποία θα αναβοσβήνει στο ms void setup () { pinMode (ledPin, OUTPUT); // ορίστε ledpin ως έξοδο } κενό βρόχο () { unsigned long currentMillis = millis (); // αποθηκεύστε την τρέχουσα ώρα εάν (currentMillis - previousMillis> = χρονικό διάστημα) {// ελέγξτε εάν 1000ms πέρασαν sebelumnyaMillis = currentMillis; // αποθηκεύστε την τελευταία φορά που αναβοσβήσατε το LED εάν (ledState == LOW) {// εάν το LED είναι σβηστό, ανάψτε το και αντίστροφα ledState = HIGH; } αλλιώς { ledState = LOW; } digitalWrite (ledPin, ledState); // ρυθμίστε το LED με ledState να αναβοσβήνει ξανά } }
Εδώ, η δήλωση
Οι διακοπές στο Arduino λειτουργούν όπως και σε άλλους μικροελεγκτές. Ο πίνακας Arduino UNO έχει δύο ξεχωριστές ακίδες για την τοποθέτηση διακοπών στο GPIO pin 2 και 3. Το έχουμε καλύψει λεπτομερώς στο Arduino Interrupts Tutorial, όπου μπορείτε να μάθετε περισσότερα σχετικά με τις Διακοπές και τον τρόπο χρήσης τους.
Εδώ θα δείξουμε το Arduino Multitasking χειρίζοντας δύο εργασίες ταυτόχρονα. Οι εργασίες θα περιλαμβάνουν αναβοσβήνει δύο LED σε διαφορετική χρονική καθυστέρηση μαζί με ένα κουμπί που θα χρησιμοποιηθεί για τον έλεγχο της κατάστασης ON / OFF των LED. Έτσι, τρεις εργασίες θα εκτελεστούν ταυτόχρονα.
Απαιτούμενα στοιχεία
- Arduino UNO
- Τρία LED (οποιοδήποτε χρώμα)
- Αντιστάσεις (470, 10k)
- Άλτες
- Ψωμί
Διάγραμμα κυκλώματος
Το διάγραμμα κυκλώματος για την απόδειξη της χρήσης της λειτουργίας Arduino Millis () είναι πολύ εύκολο και δεν έχει πολλά εξαρτήματα για σύνδεση όπως φαίνεται παρακάτω.
Προγραμματισμός του Arduino UNO για πολλαπλές εργασίες
Ο προγραμματισμός του Arduino UNO για πολλαπλές εργασίες απαιτεί μόνο τη λογική πίσω από τον τρόπο λειτουργίας των millis () που εξηγείται παραπάνω. Συνιστάται να εξασκείστε το LED αναλαμπής χρησιμοποιώντας millis ξανά και ξανά για να καθαρίσετε τη λογική και να νιώσετε άνετα με το millis () πριν ξεκινήσετε να προγραμματίζετε το Arduino UNO για πολλαπλές εργασίες. Σε αυτό το σεμινάριο η διακοπή χρησιμοποιείται επίσης με millis () ταυτόχρονα για πολλαπλές εργασίες. Το κουμπί θα είναι διακοπή. Έτσι κάθε φορά που δημιουργείται διακοπή, δηλαδή πιέζεται το κουμπί, η λυχνία LED θα αλλάξει σε κατάσταση ON ή OFF.Ο προγραμματισμός ξεκινά με τη δήλωση αριθμών ακίδων όπου είναι συνδεδεμένα τα LED και το κουμπί.
int led1 = 6; int led2 = 7; int toggleLed = 5; int pushButton = 2;
Στη συνέχεια γράφουμε μια μεταβλητή για να αποθηκεύσουμε την κατάσταση των LED για μελλοντική χρήση.
int ledState1 = ΧΑΜΗΛΟ; int ledState2 = ΧΑΜΗΛΟ;
Όπως εξηγείται παραπάνω στο παράδειγμα αναβοσβήνει, οι μεταβλητές για την περίοδο και το προηγούμενο millis δηλώνεται ότι συγκρίνει και δημιουργεί καθυστέρηση για LED. Το πρώτο LED αναβοσβήνει μετά από κάθε 1 δευτερόλεπτο και ένα άλλο LED αναβοσβήνει μετά από 200ms.
unsigned long sebelumnyaMillis1 = 0; const long period1 = 1000; unsigned long sebelumnyaMillis2 = 0; const long period2 = 200;
Μια άλλη συνάρτηση millis θα χρησιμοποιηθεί για να δημιουργήσει την καθυστέρηση απόρριψης για να αποφευχθούν τα πολλαπλά πατήματα του κουμπιού. Θα υπάρχει παρόμοια προσέγγιση όπως παραπάνω.
int debouncePeriod = 20; int debounceMillis = 0;
Οι τρεις μεταβλητές θα χρησιμοποιηθούν για την αποθήκευση της κατάστασης του κουμπιού ως διακοπή, εναλλαγή LED και κατάσταση κουμπιού.
κουμπί boolPushed = false; int ledChange = ΧΑΜΗΛΟ; int lastState = ΥΨΗΛΟ;
Καθορίστε τη δράση του pin που θα λειτουργεί ως INPUT ή OUTPUT.
pinMode (led1, OUTPUT); pinMode (led2, OUTPUT); pinMode (toggleLed, OUTPUT); pinMode (pushButton, INPUT);
Τώρα ορίστε το pin διακοπής συνδέοντας το interrupt με τον ορισμό του ISR και το Mode διακοπής. Σημειώστε ότι συνιστάται η χρήση του digitalPinToInterrupt (pin_number) κατά τη δήλωση της συνάρτησης attachInterrupt () για τη μετάφραση του πραγματικού ψηφιακού πείρου στον συγκεκριμένο αριθμό διακοπής.
attachInterrupt (digitalPinToInterrupt (pushButton), pushButton_ISR, CHANGE);
Η υπορουτίνα διακοπής είναι γραμμένη και θα αλλάξει μόνο τη σημαία με κουμπί. Σημειώστε ότι, η διακοπή της υπορουτίνας πρέπει να είναι όσο το δυνατόν συντομότερη, οπότε προσπαθήστε να την γράψετε και να ελαχιστοποιήσετε τις επιπλέον οδηγίες.
void pushButton_ISR () { buttonPushed = true; }
Ο βρόχος ξεκινά με την αποθήκευση της τιμής χιλιοστών σε μια μεταβλητή currentMillis που θα αποθηκεύει την τιμή του χρόνου που έχει παρέλθει κάθε φορά που επαναλαμβάνεται ο βρόχος.
unsigned long currentMillis = millis ();
Υπάρχουν συνολικά τρεις λειτουργίες στο multitasking, αναβοσβήνει ένα LED στο 1 δευτερόλεπτο, αναβοσβήνει το δεύτερο LED στα 200 ms και εάν πατηθεί το κουμπί πιέστε τότε σβήστε το LED OFF / ON. Έτσι θα γράψουμε τρία μέρη για να κάνουμε αυτό το έργο.
Το πρώτο είναι εναλλαγή της κατάστασης LED μετά από κάθε 1 δευτερόλεπτο συγκρίνοντας τα χιλιοστά που έχουν περάσει.
if (currentMillis - previousMillis1> = period1) { previousMillis1 = currentMillis; εάν (ledState1 == LOW) { ledState1 = ΥΨΗΛΟ; } αλλιώς { ledState1 = LOW; } digitalWrite (led1, ledState1); }
Παρομοίως, το δεύτερο, εναλλάσσει το LED μετά από κάθε 200ms συγκρίνοντας τα χιλιοστά που έχουν περάσει. Η εξήγηση εξηγείται ήδη νωρίτερα σε αυτό το άρθρο.
if (currentMillis - previousMillis2> = period2) { previousMillis2 = currentMillis; εάν (ledState2 == LOW) { ledState2 = ΥΨΗΛΟΣ; } αλλιώς { ledState2 = LOW; } digitalWrite (led2, ledState2); }
Τέλος, παρακολουθείται η σημαία του κουμπιού Push και αφού δημιουργηθεί καθυστέρηση απόρριψης 20ms, απλώς εναλλάσσεται η κατάσταση του LED που αντιστοιχεί στο κουμπί που είναι συνδεδεμένο ως διακοπή.
if (buttonPushed = true) // ελέγξτε αν το ISR καλείται { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushed) // δημιουργήστε καθυστέρηση απόρριψης 20ms για να αποφύγετε πολλαπλές πιέσεις { debounceMillis = currentMillis; // αποθηκεύστε τον τελευταίο χρόνο καθυστέρησης απόρριψης εάν (digitalRead (pushButton) == LOW && lastState == HIGH) // αλλάξτε το led μετά το πάτημα του κουμπιού { ledChange =! ledChange; digitalWrite (toggleLed, ledChange); lastState = ΧΑΜΗΛΟ; } αλλιώς εάν (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushed = false; } }
Αυτό ολοκληρώνει τον Οδηγό Arduino millis (). Σημειώστε ότι για να εξοικειωθείτε με το millis (), απλώς εξασκήστε αυτήν την λογική σε ορισμένες άλλες εφαρμογές. Μπορείτε επίσης να το επεκτείνετε για τη χρήση κινητήρων, σερβοκινητήρων, αισθητήρων και άλλων περιφερειακών. Σε περίπτωση αμφιβολίας, γράψτε στο φόρουμ μας ή σχολιάστε παρακάτω.
Ο πλήρης κωδικός και το βίντεο για την απόδειξη της χρήσης της λειτουργίας millis στο Arduino παρέχονται παρακάτω.