Μάθετε πώς να εργάζεστε με διακοπές χρονοδιακόπτη. Ας γράψουμε ένα απλό πρόγραμμα με παράλληλες διεργασίες.

Σε ένα πραγματικό πρόγραμμα, πολλές ενέργειες πρέπει να εκτελούνται ταυτόχρονα. Έδωσα ένα παράδειγμα στην εισαγωγή. Να τι κάνει:

Λειτουργία

Χρόνος κύκλου
Ανακρίνει 3 κουμπιά, επεξεργάζεται σήματα από αυτά για να εξαλείψει τη φλυαρία 2 ms
Αναγεννά δεδομένα LED επτά τμημάτων 2 ms
Παράγει σήματα ελέγχου για 2 αισθητήρες θερμοκρασίας DS18B20 και διαβάζει δεδομένα από αυτούς. Οι αισθητήρες έχουν σειριακή διεπαφή 1 καλωδίου. 100 µs για κάθε bit,
Συνολικός κύκλος ανάγνωσης 1 δευτερολέπτου
Ανάγνωση αναλογικών τιμών ρεύματος και τάσης στο στοιχείο Peltier, τάση τροφοδοσίας 100 µs
Ψηφιακό φιλτράρισμα τιμών αναλογικού ρεύματος και τάσης 10 ms
Υπολογισμός ισχύος στο στοιχείο Peltier 10 ms
Ελεγκτής σταθεροποίησης ρεύματος και τάσης PID (αναλογικό ενσωματωμένο διαφορικό). 100 µs
ρυθμιστής ισχύος 10 ms
Ρυθμιστής θερμοκρασίας 1 δευτερόλεπτο
Προστατευτικές λειτουργίες, έλεγχος ακεραιότητας δεδομένων 1 δευτερόλεπτο
Διαχείριση, γενική λογική του συστήματος 10 ms

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

Στο πρόγραμμα του ελεγκτή ψυγείου, υπάρχουν πολλές παράλληλες διεργασίες που εκτελούν όλες αυτές τις ενέργειες, η καθεμία σε έναν κύκλο με τη δική της χρονική περίοδο. Οι παράλληλες διεργασίες είναι διεργασίες των οποίων οι ενέργειες εκτελούνται ταυτόχρονα.

Στα προηγούμενα μαθήματα, δημιουργήσαμε μια τάξη για το αντικείμενο κουμπί. Είπαμε ότι πρόκειται για μια κλάση για την επεξεργασία ενός σήματος σε μια παράλληλη διαδικασία. Ότι για την κανονική λειτουργία του είναι απαραίτητο να καλέσουμε τη συνάρτηση επεξεργασίας σήματος (μέθοδος) σε έναν κύκλο με κανονική περίοδο (επιλέξαμε χρόνο 2 ms). Και τότε τα σημάδια που δείχνουν την τρέχουσα κατάσταση του κουμπιού ή του σήματος είναι διαθέσιμα οπουδήποτε στο πρόγραμμα.

Σε έναν βρόχο, τοποθετήσαμε τον κωδικό για την επεξεργασία της κατάστασης των κουμπιών και τον έλεγχο των LED. Και στο τέλος του βρόχου, βάζουμε τη συνάρτηση καθυστέρησης(2). Όμως, ο χρόνος που χρειάζεται για να εκτελεστεί ένα πρόγραμμα σε βρόχο αλλάζει τον συνολικό χρόνο βρόχου. Και η περίοδος κύκλου σαφώς δεν είναι ίση με 2 ms. Επιπλέον, κατά την εκτέλεση της συνάρτησης delay(), το πρόγραμμα παγώνει και δεν μπορεί να εκτελέσει άλλες ενέργειες. Ένα πολύπλοκο πρόγραμμα θα οδηγήσει σε απόλυτο χάος.

Έξοδος - καλέστε τη λειτουργία επεξεργασίας της κατάστασης του κουμπιού στη διακοπή από το χρονόμετρο υλικού. Κάθε 2 ms ο κύριος βρόχος του προγράμματος πρέπει να διακόπτεται, το σήμα του κουμπιού υποβάλλεται σε επεξεργασία και ο έλεγχος επιστρέφει στον κύριο βρόχο στον κωδικό όπου διακόπηκε. Ένας σύντομος χρόνος επεξεργασίας για το σήμα του κουμπιού δεν θα επηρεάσει σημαντικά την εκτέλεση του κύριου βρόχου. Εκείνοι. Η επεξεργασία κουμπιών θα γίνει παράλληλα, ανεπαίσθητα για το κύριο πρόγραμμα.

Διακοπή υλικού από το χρονόμετρο.

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

Από την άποψη του προγράμματος, μια διακοπή είναι μια κλήση συνάρτησης σε ένα εξωτερικό συμβάν που δεν σχετίζεται άμεσα με τον κώδικα του προγράμματος.

Το σήμα διακοπής από το χρονόμετρο παράγεται κυκλικά, με δεδομένο χρόνο. Δημιουργεί το χρονόμετρο υλικού του - έναν μετρητή με λογική που επαναφέρει τον κώδικά του όταν επιτευχθεί μια συγκεκριμένη τιμή. Ρυθμίζοντας προγραμματικά τον κωδικό για τη λογική επαναφοράς, μπορούμε να ορίσουμε την ώρα της περιόδου διακοπής του χρονοδιακόπτη.

Η ρύθμιση του τρόπου λειτουργίας και της ώρας της περιόδου χρονοδιακόπτη Arduino γίνεται μέσω των καταχωρητών υλικού του μικροελεγκτή. Μπορείτε να μάθετε πώς να το κάνετε αν θέλετε. Αλλά προτείνω μια απλούστερη επιλογή - τη χρήση της βιβλιοθήκης MsTimer2. Επιπλέον, η ρύθμιση της λειτουργίας χρονοδιακόπτη είναι σπάνια, πράγμα που σημαίνει ότι η χρήση των λειτουργιών της βιβλιοθήκης δεν θα επιβραδύνει το πρόγραμμα.

Βιβλιοθήκη MsTimer2.

Η βιβλιοθήκη προορίζεται για τη διαμόρφωση μιας διακοπής υλικού από το χρονόμετρο 2 του μικροελεγκτή. Έχει μόνο τρεις λειτουργίες:

  • MsTimer2::set(unsigned long ms, void (*f)())

Αυτή η συνάρτηση ορίζει το χρόνο της περιόδου διακοπής σε ms. Με μια τέτοια περίοδο, θα κληθεί ο χειριστής διακοπής f. Πρέπει να δηλωθεί ως άκυρη (δεν επιστρέφει τίποτα) και να μην δέχεται ορίσματα. * Το f είναι δείκτης συνάρτησης. Αντίθετα, γράψτε το όνομα της συνάρτησης.

  • MsTimer2::start()

Η λειτουργία επιτρέπει τις διακοπές του χρονοδιακόπτη.

  • MsTimer2::stop()

Η λειτουργία απενεργοποιεί τις διακοπές του χρονοδιακόπτη.

Πρέπει να γράψετε MsTimer2:: πριν από το όνομα της συνάρτησης. η βιβλιοθήκη γράφεται χρησιμοποιώντας την οδηγία χώρου ονομάτων.

Για να εγκαταστήσετε τη βιβλιοθήκη, αντιγράψτε τον κατάλογο MsTimer2 στο φάκελο βιβλιοθήκες στον φάκελο εργασίας του Arduino IDE. Στη συνέχεια, εκτελέστε το πρόγραμμα Arduino IDE, ανοίξτε Σκίτσο -> Συμπερίληψη βιβλιοθήκηςκαι δείτε ότι η βιβλιοθήκη MsTimer2 υπάρχει στη λίστα των βιβλιοθηκών.

Μπορείτε να κάνετε λήψη της βιβλιοθήκης MsTimer2 σε αρχείο zip. Για να το εγκαταστήσετε, πρέπει να το αποσυσκευάσετε.

Ένα απλό πρόγραμμα με παράλληλη επεξεργασία του σήματος του κουμπιού.

Τώρα ας γράψουμε ένα απλό πρόγραμμα με ένα κουμπί και ένα LED από το μάθημα 6. Ένα κουμπί συνδέεται στην πλακέτα Arduino σύμφωνα με το σχήμα:

Μοιάζει με αυτό:

Κάθε φορά που πατάτε το κουμπί, το LED στην πλακέτα του Arduino αλλάζει την κατάστασή του. Πρέπει να εγκατασταθούν οι βιβλιοθήκες MsTimer2 και Button:

MsTimer2

Και πληρώστε. Μόνο 40 ρούβλια. ανά μήνα για πρόσβαση σε όλους τους πόρους του ιστότοπου!

// sketch_10_1 του μαθήματος 10
// Πατώντας το κουμπί αλλάζει η κατάσταση του LED

#περιλαμβάνω
#περιλαμβάνω

#define LED_1_PIN 13 //
#define BUTTON_1_PIN 12 // κουμπί συνδεδεμένο στον ακροδέκτη 12

Κουμπί κουμπιού1(BUTTON_1_PIN, 15); // δημιουργία αντικειμένου - κουμπί

void setup()(

MsTimer2::set(2, timerInterupt); // ρυθμίστε την περίοδο διακοπής του χρονοδιακόπτη στα 2 ms
MsTimer2::start(); //
}

void loop() (

// Έλεγχος LED
if (button1.flagClick == true) (
// έγινε ένα κλικ στο κουμπί



}
}

// χειριστής διακοπής
void timerInterrupt() (
button1.scanState(); // καλώντας τη μέθοδο αναμονής σταθερής κατάστασης για το κουμπί
}

Στη συνάρτηση setup(), ορίζουμε το χρόνο κύκλου διακοπής του χρονοδιακόπτη στα 2 ms και καθορίζουμε το όνομα του χειριστή διακοπής timerInterrupt . Η λειτουργία χειρισμού σήματος κουμπιού button1.scanState() καλείται στον χειριστή διακοπής χρονοδιακόπτη κάθε 2 ms.

Έτσι, επεξεργαζόμαστε την κατάσταση του κουμπιού σε μια παράλληλη διαδικασία. Και στον κύριο βρόχο του προγράμματος, ελέγχουμε το σύμβολο ενός κουμπιού και αλλάζουμε την κατάσταση του LED.

Ο πτητικός προσδιορισμός.

Ας αλλάξουμε τον βρόχο loop() στο προηγούμενο πρόγραμμα.

void loop() (

ενώ (αλήθεια) (
if (button1.flagClick == true) break;
}

// έγινε ένα κλικ στο κουμπί
button1.flagClick= false; // δυνατότητα επαναφοράς
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // Αναστροφή LED
}

Λογικά δεν έχει αλλάξει τίποτα.

  • Στην πρώτη έκδοση, το πρόγραμμα πέρασε από τον βρόχο βρόχου μέχρι το τέλος και ανέλυσε τη σημαία button1.flagClick σε αυτό.
  • Στη δεύτερη παραλλαγή, το πρόγραμμα αναλύει τη σημαία button1.flagClick σε έναν άπειρο βρόχο while. Όταν η σημαία ενεργοποιηθεί, ξεφεύγει από τον βρόχο while και αντιστρέφει την κατάσταση του LED.

Η μόνη διαφορά είναι σε ποιο βρόχο το πρόγραμμα περιστρέφεται σε βρόχο ή σε ενώ.

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

#περιλαμβάνω
#define LED_1_PIN 13 // LED συνδεδεμένο στον ακροδέκτη 13
int count=0;

void setup()(
pinMode(LED_1_PIN, OUTPUT); // ορίστε την έξοδο του LED ως έξοδο
MsTimer2::set(500, timerInterupt); // ρυθμίστε την περίοδο διακοπής του χρονοδιακόπτη στα 500 ms
MsTimer2::start(); // ενεργοποίηση διακοπής χρονοδιακόπτη
}

void loop() (

ενώ (αλήθεια)(
if (count != 0) break;
}

count=0;
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // Αναστροφή κατάστασης LED
}

// χειριστής διακοπής
void timerInterrupt() (
count++;
}

Σε αυτό το πρόγραμμα, ο μετρητής αυξάνεται κατά 1 στον χειριστή διακοπής κάθε 500 ms. Στον βρόχο while, αναλύεται, με break βγαίνουμε από τον βρόχο και αντιστρέφουμε την κατάσταση του LED. Δεν μπορείτε να φανταστείτε ένα απλούστερο πρόγραμμα, αλλά ούτε και λειτουργεί.

Το γεγονός είναι ότι ο μεταγλωττιστής της γλώσσας C ++ βελτιστοποιεί το πρόγραμμα στο βαθμό της ευφυΐας του. Μερικές φορές δεν λειτουργεί. Ο μεταγλωττιστής βλέπει ότι δεν εκτελούνται λειτουργίες στη μεταβλητή count στον βρόχο while. Ως εκ τούτου, πιστεύει ότι αρκεί να ελέγξουμε την κατάσταση της καταμέτρησης μόνο μία φορά. Τι για να ελέγξω σε έναν κύκλο, τι δεν μπορεί ποτέ να αλλάξει. Ο μεταγλωττιστής διορθώνει τον κώδικα, βελτιστοποιώντας τον για το χρόνο εκτέλεσης. Με απλά λόγια, αφαιρεί τον κωδικό ελέγχου μεταβλητής από τον βρόχο. Ο μεταγλωττιστής δεν μπορεί να καταλάβει ότι η μεταβλητή count αλλάζει την κατάστασή της στον χειριστή διακοπής. Ως αποτέλεσμα, είμαστε κολλημένοι στον βρόχο while.

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

Αν, για παράδειγμα, προσθέσουμε μια κλήση στη συνάρτηση delay() στον βρόχο while, τότε το πρόγραμμα θα λειτουργήσει.

ενώ (αλήθεια)(
if (count != 0) break;
καθυστέρηση(1);
}

Είναι καλό να αναπτύσσετε προγράμματα όπου ο βρόχος τρέχει μέχρι το τέλος και το πρόγραμμα δεν κρέμεται πουθενά. Το επόμενο μάθημα θα είναι ο μόνος κώδικας που αναλύει σημαίες σε άπειρους βρόχους while. Επιπλέον, σκοπεύω να εκτελέσω τον βρόχο σε όλα τα προγράμματα μέχρι το τέλος.

Μερικές φορές αυτό δεν είναι εύκολο ή όχι τόσο αποτελεσματικό. Στη συνέχεια, πρέπει να χρησιμοποιήσετε τον προσδιοριστικό πτητικό. Καθορίζεται κατά τη δήλωση μιας μεταβλητής και λέει στον μεταγλωττιστή να μην προσπαθήσει να βελτιστοποιήσει τη χρήση της. Εμποδίζει τον μεταγλωττιστή να κάνει υποθέσεις σχετικά με την τιμή μιας μεταβλητής, καθώς η μεταβλητή μπορεί να αλλάξει σε άλλο μπλοκ προγράμματος, όπως μια παράλληλη διεργασία. Επίσης, ο μεταγλωττιστής εκχωρεί μια μεταβλητή στη μνήμη RAM, όχι σε καταχωρητές γενικού σκοπού.

Αρκεί να γράψετε στο πρόγραμμα όταν δηλώνετε μέτρηση

volatile int count=0;

και όλες οι επιλογές θα λειτουργήσουν.

Για ένα πρόγραμμα ελέγχου κουμπιών, πρέπει να δηλώσετε ότι οι ιδιότητες μιας παρουσίας της κλάσης Button μπορούν να αλλάξουν.

volatile Button button1(BUTTON_1_PIN, 15); // δημιουργία αντικειμένου - κουμπί

Σύμφωνα με τις παρατηρήσεις μου, η χρήση του χαρακτηριστικού volatile δεν αυξάνει το μήκος του κώδικα προγράμματος με κανέναν τρόπο.

Σύγκριση της μεθόδου επεξεργασίας σήματος κουμπιών με τη βιβλιοθήκη Bounce.

Υπάρχει μια έτοιμη βιβλιοθήκη για την αφαίρεση των κουμπιών Bounce. Ο έλεγχος της κατάστασης του κουμπιού πραγματοποιείται όταν καλείται η συνάρτηση update(). Σε αυτή τη λειτουργία:

  • διαβάζεται το σήμα του κουμπιού.
  • σε σύγκριση με την κατάσταση κατά την προηγούμενη κλήση για ενημέρωση();
  • Ελέγξτε πόσος χρόνος έχει περάσει από την προηγούμενη κλήση χρησιμοποιώντας τη συνάρτηση millis ().
  • λαμβάνεται απόφαση για το αν έχει αλλάξει η κατάσταση του κουμπιού.
  • Αλλά αυτό δεν είναι παράλληλη επεξεργασία σήματος. Η συνάρτηση update() συνήθως καλείται στον κύριο, ασύγχρονο βρόχο προγράμματος. Εάν δεν καλείται για περισσότερο από ορισμένο χρόνο, τότε οι πληροφορίες σήματος του κουμπιού θα χαθούν. Οι ακανόνιστες κλήσεις οδηγούν σε εσφαλμένη λειτουργία του αλγορίθμου.
  • Η ίδια η συνάρτηση έχει έναν αρκετά μεγάλο κώδικα και εκτελείται πολύ περισσότερο από τις λειτουργίες της βιβλιοθήκης Button ().
  • Το ψηφιακό φιλτράρισμα των σημάτων κατά τη μέση τιμή δεν υπάρχει καθόλου.

Είναι καλύτερα να μην χρησιμοποιείτε αυτήν τη βιβλιοθήκη σε πολύπλοκα προγράμματα.

Στο επόμενο μάθημα, θα γράψουμε ένα πιο σύνθετο πρόγραμμα με παράλληλες διεργασίες. Θα μάθουμε πώς να υλοποιούμε την εκτέλεση μπλοκ προγραμμάτων σε κύκλους με διαφορετικά χρονικά διαστήματα από μία διακοπή χρονομέτρου.

Κατηγορία: . Μπορείτε να προσθέσετε σελιδοδείκτη.

Αυτό το άρθρο θα καλύψει τα ζητήματα της παράλληλης και σειριακής σύνδεσης πολλών slave συσκευών με το δίαυλο SPI, τη σειριακή σύνδεση των καταχωρητών μετατόπισης, την εργασία με μια διπλή οθόνη 7 τμημάτων, την υλοποίηση ανεξάρτητων διεργασιών στο Arduino. Ως αποτέλεσμα, θα φτιάξουμε ένα κύκλωμα στο οποίο ένα φίδι θα τρέχει κατά μήκος ενός διπλού 7-τμήματος και δευτερόλεπτα θα σημειωθούν στο άλλο, μονό, αυτή τη στιγμή.

γνωριστήκαμε με το δίαυλο SPI και μάθαμε ότι χρειάζονται 4 καλώδια για τη σύνδεση ενός slave σε έναν κύριο. Ωστόσο, εάν υπάρχουν περισσότερες από μία slave συσκευές, έχουμε ήδη ενδιαφέρουσες επιλογές.

Σύνδεση συσκευών παράλληλα με τον δίαυλο SPI

Όταν συνδέονται παράλληλα, πολλές εξαρτημένες συσκευές χρησιμοποιούν κοινά καλώδια SCLK, MOSIκαι MISO, ενώ κάθε σκλάβος έχει τη δική του γραμμή SS . Ο συντονιστής καθορίζει συσκευή προς ανταλλαγή, με σχηματίζοντας χαμηλό σήμα πάνω του SS .
Μπορεί να φανεί ότι για να συνδεθεί nαπαιτούμενες συσκευές nγραμμές SS , δηλαδή για τη λειτουργία του περιβάλλοντος SPI με nπρέπει να διατεθούν σκλάβοι για αυτό n+3πόδια του μικροελεγκτή.

Σειριακή σύνδεση συσκευών στο δίαυλο SPI

Όταν οι συσκευές συνδέονται σε σειρά, χρησιμοποιούν κοινά καλώδια. SCLKκαι SS , και η έξοδος του ενός συνδέεται με την είσοδο του άλλου. MOSIο κύριος συνδέεται με την πρώτη συσκευή και MISO- μέχρι το τελευταίο. Δηλαδή, για τον κύριο στο δίαυλο SPI, αυτή είναι, σαν να λέγαμε, μία συσκευή.
Μια τέτοια σύνδεση σας επιτρέπει να δημιουργήσετε, για παράδειγμα, έναν καταχωρητή μετατόπισης 16-bit από δύο καταχωρητές μετατόπισης 8-bit, κάτι που θα κάνουμε τώρα.
Απομένει να σημειώσουμε τη γοητεία μιας τέτοιας σύνδεσης: συνδέστε τουλάχιστον 3, τουλάχιστον 8 συσκευές, θα χρειαστούν μόνο 4 πόδια στον ελεγκτή.

Σειριακή σύνδεση δύο καταχωρητών βάρδιας
Ας ρίξουμε μια άλλη ματιά στον καταχωρητή μετατόπισης 74HC595:

Το θυμόμαστε αυτό Δ.Σ.- υπάρχει μια σειριακή ακίδα εισόδου και Q0-Q7σειριακές ακίδες εξόδου. Q7S, που δεν χρησιμοποιούσαμε όταν είχαμε μόνο έναν καταχωρητή στο κύκλωμα, είναι η σειριακή έξοδος ενός καταχωρητή. Βρίσκει τη χρήση του όταν μεταφέρουμε περισσότερα από 1 byte σε καταχωρητές. Μέσω αυτού του pin, όλα τα byte που προορίζονται για τους επόμενους καταχωρητές θα προωθηθούν διαδοχικά και το τελευταίο byte που μεταδόθηκε θα παραμείνει στον πρώτο καταχωρητή.


Συνδέοντας τον ακροδέκτη Q7S ενός πρώτου καταχωρητή με τον ακροδέκτη DS του δεύτερου (και ούτω καθεξής, εάν χρειάζεται), παίρνουμε έναν διπλό (τριπλό κ.λπ.) καταχωρητή.

Σύνδεση μιας διπλής οθόνης 7 τμημάτων

Μια διπλή οθόνη 7 τμημάτων είναι συνήθως μια συσκευή με 18 πόδια, 9 για κάθε χαρακτήρα. Ας ρίξουμε μια ματιά στο διάγραμμα (η οθόνη μου φέρει την ένδειξη LIN-5622SR και υπάρχει μεγάλη πιθανότητα το διάγραμμα καλωδίωσης να είναι μοναδικό):

Αυτή είναι μια κοινή οθόνη ανόδου, που σημαίνει ότι τόσο το com1 όσο και το com2 χρειάζονται υψηλό επίπεδο TTL και χαμηλό επίπεδο στο αντίστοιχο σκέλος για να ανάψουν τη δίοδο. Εάν έχετε κοινή οθόνη καθόδου, κάντε το αντίθετο!

Συνδέστε την οθόνη όπως φαίνεται στο διάγραμμα:

Η αριστερή οθόνη συνδέεται με τον πρώτο καταχωρητή: 1A στο σκέλος Q0, 1B στο σκέλος Q1, 1C στο σκέλος Q2 κ.λπ. Συνδέουμε την κοινή επαφή com1 στη γείωση. Κάνουμε το ίδιο με τη δεξιά οθόνη: 2A στο πόδι Q0, 2B στο πόδι Q1 κ.λπ., η κοινή επαφή com2 είναι στο έδαφος.

Το κύκλωμα δεν θα μοιάζει με την εικόνα, εάν το pinout της οθόνης είναι διαφορετικό από το δικό μου, εδώ πρέπει απλώς να είστε προσεκτικοί κατά τη σύνδεση. Εάν η οθόνη είναι με κοινή κάθοδο, τότε τα com1 και com2 συνδέονται στο ρεύμα!

Ένα απλό φίδι σε μια διπλή οθόνη 7 τμημάτων

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

Ο κύκλος μας θα αποτελείται από οκτώ πλαίσια, καθένα από τα οποία θα ανάβει συγκεκριμένα τρία LED. Στο πρώτο πλαίσιο, το 1E, 1F, 1A θα ανάψει (βλ. διάγραμμα), στο δεύτερο - 1F, 1A, 2A, στο τρίτο - 1A, 2A, 2B και ούτω καθεξής, στο όγδοο - 1D, 1E, 1F .

Και πάλι, για λόγους ευκολίας, ας φτιάξουμε έναν πίνακα με byte, θυμόμαστε ότι, από προεπιλογή, τα bit μεταδίδονται ξεκινώντας από το υψηλότερο, δηλ. 2h.

Πλαίσιο

1 abcd efgh

2 abcd efgh

μαγεύω

0111 0011

1111 1111

ECFF

0111 1011

0111 1111

ΕΔ ΕΦ

0111 1111

0011 1111

EF CF

1111 1111

0001 1111

FF 8F

1111 1111

1000 1111

FF 1F

1110 1111

1100 1111

7F 3F

1110 0111

1110 1111

7E 7F

1110 0011

1111 1111

7CFF


Ο κεντρικός υπολογιστής πρέπει να ρυθμίσει το χαμηλό (ενεργό) επίπεδο στο καλώδιο SS , μεταφέρετε δύο byte και αφήστε το καλώδιο. Αυτή τη στιγμή, θα εμφανιστεί ένα κλείδωμα, ένα byte θα γραφτεί σε κάθε καταχωρητή, δύο χαρακτήρες θα ανάψουν.

#περιλαμβάνω<SPI .h> // συνδέστε τη βιβλιοθήκη SPI
enum(reg=9); //επιλογή γραμμήςSS εγγραφείτε στην 9η ακίδα του Arduino

κενός εγκατάσταση () {
SPI.begin(); //αρχικοποίηση SPI
//μετάφραση του pin που επιλέχθηκε για μετάδοση στη λειτουργία εξόδου
pinMode(reg, OUTPUT);
}


κενός βρόχος () {
//Γεμίστε τον πίνακα με τα byte που θα στείλουμε
στατικό uint8_t ψηφίο =
(0xFF,0xCE,0xFF,0xDE,0xFC,0xFE,0xF8,0xFF,
0xF1,0xFF,0xF3,0xF7,0xF7,0xE7,0xFF,0xC7};
//μεταφέρετε δύο byte από τον πίνακα και κλειδώστε τους καταχωρητές
για (int i=0;i<16;i+=2){
digitalWrite(reg, LOW);
SPI .transfer(ψηφίο[i]);
SPI.transfer(ψηφίο);
digitalWrite(reg, HIGH);
καθυστέρηση(80); //παύση μεταξύ καρέ
}
}


Βίντεο του προγράμματος:

Παράλληλες διεργασίες στο Arduino

Γιατί οι προγραμματιστές του Arduino δίνουν ιδιαίτερη προσοχή στο παράδειγμα Blink χωρίς καθυστέρηση;

Συνήθως το πρόγραμμα Arduino είναι γραμμικό - πρώτα κάνει ένα πράγμα και μετά άλλο. Στο παραπάνω παράδειγμα, χρησιμοποιήσαμε τη συνάρτηση καθυστέρηση (80)ώστε κάθε καρέ να σχεδιάζεται 80 χιλιοστά του δευτερολέπτου μετά το προηγούμενο. Ωστόσο, τελικά, αυτά τα 80 χιλιοστά του δευτερολέπτου ο επεξεργαστής δεν κάνει τίποτα και δεν επιτρέπει σε κανέναν να κάνει τίποτα! Για να τρέξουμε δύο ή περισσότερες παράλληλες διεργασίες, πρέπει να αλλάξουμε την έννοια της κατασκευής ενός προγράμματος, εγκαταλείποντας καθυστέρηση() .

Ο πυρήνας του νέου μας σχεδίου θα είναι το χρονόμετρο. Το χρονόμετρο θα μετρήσει το χρόνο και θα αναγκάσουμε αυτό ή εκείνο το γεγονός να συμβεί σε ορισμένα διαστήματα. Για παράδειγμα, η οθόνη του ρολογιού θα χτυπά κάθε δευτερόλεπτο και η λυχνία LED θα αναβοσβήνει κάθε 0,86 δευτερόλεπτα.

Υπάρχει ένα πράγμα στο Arduino που μετράει το χρόνο από την έναρξη του προγράμματος, ονομάζεται millis (). Με τη βοήθειά του οργανώνεται «παραλληλισμός» εργασιών.

Τελικό έργο: ένα ρολόι και ένα πονηρό φίδι


Ας συνθέσουμε αυτό το διάγραμμα:

Ο αριστερός και ο μεσαίος καταχωρητής λειτουργούν για εμάς από την άποψη του ηγέτη ως μία συσκευή και ο δεξιός καταχωρητής - ως άλλη. Μπορεί να φανεί ότι αυτές οι δύο συσκευές χρησιμοποιούν το ίδιο καλώδιο. SCLK(Πείρος Arduino 13, το σύρμα φαίνεται με πορτοκαλί) και MOSI(11η καρφίτσα, κίτρινη), SS χρησιμοποιούνται διαφορετικά (καρφίτσες 8 και 9, πράσινο χρώμα). Η σύνδεση οθονών 7 τμημάτων με καταχωρητές εμφανίζεται για τα συγκεκριμένα μοντέλα μου και πιθανότατα δεν θα ταιριάζει με τα δικά σας.


Αυτή τη φορά θα κάνουμε το φίδι μας πιο πονηρό: θα διατρέχει όλα τα τμήματα με τον ίδιο τρόπο όπως ο λύκος οδήγησε μια μοτοσικλέτα κατά μήκος της διασταύρωσης του δρόμου στη σειρά "Wait a minute!", η οποία ξεκινά με το γεγονός ότι το βγάζει αυτό ίδια μοτοσυκλέτα από το γκαράζ και βάζει κράνος .

Η ακολουθία byte για αυτό το φίδι θα είναι:

Στατικό uint8_t φίδι =


Τώρα η ουσία:λειτουργία millis ()κάθεται και μετράει χιλιοστά του δευτερολέπτου από την αρχή της αρχής. Στην αρχή κάθε βρόχου θυμόμαστε την τιμή millis ()σε μια μεταβλητή μετρών την ώραν.Ρύθμιση μεταβλητών snakeTimerPrevκαι digitTimerPrev, το οποίο θα αποθηκεύσει τη στιγμή του προηγούμενου συμβάντος: για snakeTimerPrevείναι η συμπερίληψη του προηγούμενου καρέ του κινούμενου σχεδίου φιδιού, για digitTimerPrev- συμπερίληψη του προηγούμενου ψηφίου. Μόλις η τρέχουσα διαφορά ώρας ( μετρών την ώραν) και προηγούμενο ( snakeTimerPrevή digitTimerPrev) γίνεται ίσο με την καθορισμένη περίοδο (στην περίπτωσή μας, 80 και 1000 ms, αντίστοιχα), μεταδίδουμε το επόμενο πλαίσιο/byte.

Με αυτόν τον τρόπο,

  • κάθε 80ms ο ελεγκτής θα χαμηλώνει το σήμα στη γραμμή SSδιπλή εμφάνιση, μεταδώστε δύο byte και αφήστε τη γραμμή.
  • κάθε δευτερόλεπτο ο ελεγκτής θα χαμηλώνει το σήμα στη γραμμή SSμία οθόνη, μεταδώστε ένα byte και αφήστε τη γραμμή.
Ας το εφαρμόσουμε στο Arduino. Έχω ήδη περιγράψει τα πάντα λεπτομερώς πριν, νομίζω ότι δεν έχει νόημα να σχολιάσω.

#περιλαμβάνω<SPI .h>

enum ( snakePin = 9, digitPin = 8 );
ανυπόγραφο long timer=0, snakeTimerPrev=0, digitTimerPrev=0;
int i=0, j=0;



κενός εγκατάσταση () {
SPI.begin();
pinMode(digitPin, OUTPUT );
pinMode(snakePin, OUTPUT );
}


κενός βρόχος () {
στατικό uint8_t ψηφίο =
(0xC0.0xF9.0xA4.0xB0.0x99.0x92.0x82.0xF8,
0x80.0x90.0x88.0x83.0xC6.0xA1.0x86.0x8E);
στατικό uint8_t φίδι =
(0xFF,0x9E,0xFF,0xDC,0xFF,0xF8,0xFF,0xF1,
0xFF,0xE3,0xFF,0xA7,0xBF,0xAF,0xBD,0xBF,
0xBC,0xFF,0xDC,0xFF,0xCE,0xFF,0xC7,0xFF,
0xE3,0xFF,0xB3,0xFF,0xBB,0xBF,0xBF,0x9F);


timer=millis();


if (timer-snakeTimerPrev>80)(
digitalWrite(snakePin, LOW);
SPI.transfer(snake[j]);
SPI.transfer(snake);
digitalWrite(snakePin, HIGH);
ι<30 ? j+=2: j=0;
snakeTimerPrev=χρονόμετρο;
}
εάν (χρονόμετρο-ψηφίοTimerPrev>1000)(
digitalWrite (digitPin, LOW);
SPI.transfer(ψηφίο[i]);

Μόλις εγκαταστήσετε αυτό το πρόγραμμα, θα εκπλαγείτε πόσο παρόμοιο είναι με το Arduino IDE. Μην εκπλαγείτε, και τα δύο προγράμματα γίνονται στον ίδιο κινητήρα.

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

Ας ξεκινήσουμε το Arduino IDE και ας επιλέξουμε το απλούστερο παράδειγμα εξόδου δεδομένων Σειριακή θύρα:

void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // περιμένετε 500 χιλιοστά του δευτερολέπτου πριν στείλετε ξανά delay(500); )

Ας εκτελέσουμε το παράδειγμα και βεβαιωθείτε ότι ο κώδικας λειτουργεί.

Λήψη δεδομένων

Τώρα θέλουμε να λάβουμε το ίδιο κείμενο στο . Ξεκινάμε ένα νέο έργο και γράφουμε τον κώδικα.

Το πρώτο βήμα είναι η εισαγωγή της βιβλιοθήκης. Ας πάμε στο Σκίτσο | Εισαγωγή βιβλιοθήκης | Κατα συρροη. Η γραμμή θα εμφανιστεί στο σκίτσο:

Εισαγωγή processing.serial.*; Σειριακή σειρά; // δημιουργία αντικειμένου σειριακής θύρας Η συμβολοσειρά ελήφθη. // δεδομένα που λαμβάνονται από τη σειριακή θύρα void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // εάν υπάρχουν δεδομένα, ελήφθησαν = σειριακή. readStringUntil("\n"); // ανάγνωση των δεδομένων ) println(received); // εμφάνιση των δεδομένων στην κονσόλα )

Για να διασφαλίσουμε ότι λαμβάνονται δεδομένα από τη σειριακή θύρα, χρειαζόμαστε ένα αντικείμενο της κλάσης Κατα συρροη. Δεδομένου ότι στέλνουμε δεδομένα συμβολοσειράς από το Arduino, πρέπει να λάβουμε τη συμβολοσειρά και στην Επεξεργασία.

Στη μέθοδο εγκατάσταση ()πρέπει να αποκτήσετε μια διαθέσιμη σειριακή θύρα. Συνήθως, αυτή είναι η πρώτη διαθέσιμη θύρα στη λίστα. Μετά από αυτό μπορούμε να ρυθμίσουμε το αντικείμενο Κατα συρροη, υποδεικνύοντας τη θύρα και το ρυθμό μεταφοράς δεδομένων (είναι επιθυμητό οι τιμές να ταιριάζουν).

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

Η επεξεργασία σάς επιτρέπει να εργάζεστε όχι μόνο με την κονσόλα, αλλά και να δημιουργείτε τυπικά παράθυρα. Ας ξαναγράψουμε τον κώδικα.

Εισαγωγή processing.serial.*; Σειριακή σειρά; // δημιουργία αντικειμένου σειριακής θύρας Η συμβολοσειρά ελήφθη. // δεδομένα που λαμβάνονται από τη σειριακή θύρα void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial .available() > 0) ( // εάν υπάρχουν δεδομένα, // διαβάστε τα και γράψτε τα στη μεταβλητή λήφθηκε λήφθηκε = σειριακή. readStringUntil("\n"); ) // Ρυθμίσεις για κείμενο textSize(24); διαγραφή (); if (received != null) ( text(received, 10, 30); ) )

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

Έτσι, μάθαμε πώς να λαμβάνουμε δεδομένα από το Arduino. Αυτό θα μας επιτρέψει να σχεδιάσουμε όμορφα γραφήματα ή να δημιουργήσουμε προγράμματα για την παρακολούθηση των μετρήσεων των αισθητήρων.

Αποστολή στοιχείων

Μπορούμε όχι μόνο να λάβουμε δεδομένα από τον πίνακα, αλλά και να στείλουμε δεδομένα στον πίνακα, αναγκάζοντάς μας να εκτελέσουμε εντολές από τον υπολογιστή.

Ας υποθέσουμε ότι στέλνουμε τον χαρακτήρα "1" από την Επεξεργασία. Όταν η πλακέτα εντοπίσει τον απεσταλμένο χαρακτήρα, ανάψτε το LED στη θύρα 13 (ενσωματωμένη).

Το σκίτσο θα είναι παρόμοιο με το προηγούμενο. Για παράδειγμα, ας δημιουργήσουμε ένα μικρό παράθυρο. Όταν κάνουμε κλικ στην περιοχή του παραθύρου, θα στείλουμε το "1" και θα το αντιγράψουμε στην κονσόλα για επαλήθευση. Εάν δεν υπάρχουν κλικ, αποστέλλεται η εντολή "0".

Εισαγωγή processing.serial.*; Σειριακή σειρά; // δημιουργία αντικειμένου σειριακής θύρας Η συμβολοσειρά ελήφθη. // δεδομένα που λαμβάνονται από τη σειριακή θύρα void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed == true) ( ​​//αν κάναμε κλικ μέσα στο παράθυρο serial.write("1"); //send 1 println("1"); ) αλλιώς ( //αν δεν υπήρχε κλικ serial.write(" 0"); // αποστολή 0 ))

Τώρα ας γράψουμε ένα σκίτσο για το Arduino.

Char commandValue; // δεδομένα που προέρχονται από τη σειριακή θύρα int ledPin = 13; // ενσωματωμένη λυχνία LED void setup() ( pinMode(ledPin, OUTPUT); // λειτουργία εξόδου δεδομένων Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ); ) if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // ενεργοποιήστε το LED ) αλλιώς ( digitalWrite(ledPin, LOW); // διαφορετικά απενεργοποιήστε ) delay(10); // καθυστέρηση πριν από την επόμενη ανάγνωση δεδομένων )

Ας τρέξουμε και τα δύο σκίτσα. Κάνουμε κλικ μέσα στο παράθυρο και παρατηρούμε ότι το LED ανάβει. Μπορείτε ακόμη και να μην κάνετε κλικ, αλλά κρατήστε πατημένο το κουμπί του ποντικιού - το LED θα ανάβει συνεχώς.

Ανταλλαγή δεδομένων

Τώρα ας προσπαθήσουμε να συνδυάσουμε και τις δύο προσεγγίσεις και να ανταλλάξουμε μηνύματα μεταξύ του πίνακα και της εφαρμογής προς δύο κατευθύνσεις.

Για μέγιστη απόδοση, ας προσθέσουμε μια μεταβλητή boolean. Ως αποτέλεσμα, δεν χρειάζεται πλέον να στέλνουμε συνεχώς 1 ή 0 από το Processing και η σειριακή θύρα ξεφορτώνεται και δεν μεταδίδει περιττές πληροφορίες.

Όταν η πλακέτα εντοπίσει την απεσταλμένη μονάδα, αλλάζουμε τη δυαδική τιμή στο αντίθετο σε σχέση με την τρέχουσα κατάσταση ( ΧΑΜΗΛΟΣστο ΥΨΗΛΟΣκαι αντίστροφα). ΣΤΟ αλλούχρησιμοποιούμε τη συμβολοσειρά "Hello Kity", την οποία θα στείλουμε μόνο αν δεν βρούμε "1".

Λειτουργία installContact()στέλνει τη συμβολοσειρά που αναμένουμε να λάβουμε στην επεξεργασία. Εάν έρθει η απάντηση, τότε η Επεξεργασία μπορεί να λάβει τα δεδομένα.

Char commandValue; // δεδομένα που προέρχονται από τη σειριακή θύρα int ledPin = 13; boolean ledState = LOW; //ελέγξτε την κατάσταση του LED void setup() ( pinMode(ledPin, OUTPUT); Serial.begin(9600); establishContact(); // στείλτε ένα byte στην επαφή ενώ ο δέκτης αποκρίνεται ) void loop() ( // εάν τα δεδομένα μπορούν να διαβαστούν εάν (Serial.available() > 0) ( // read data commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState );<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

Ας περάσουμε στο σκίτσο Επεξεργασία. Θα χρησιμοποιήσουμε τη μέθοδο serialEvent(), το οποίο θα καλείται κάθε φορά που ένας συγκεκριμένος χαρακτήρας βρίσκεται στο buffer.

Προσθέστε μια νέα μεταβλητή boolean πρώτη επαφή, το οποίο σας επιτρέπει να προσδιορίσετε εάν υπάρχει σύνδεση με το Arduino.

Στη μέθοδο εγκατάσταση ()προσθέστε μια γραμμή serial.bufferUntil("\n");. Αυτό μας επιτρέπει να αποθηκεύουμε τα εισερχόμενα δεδομένα σε ένα buffer μέχρι να βρούμε έναν συγκεκριμένο χαρακτήρα. Σε αυτήν την περίπτωση, επιστρέφουμε (\n) αφού στέλνουμε Serial.println()από το Arduino. "\n"στο τέλος σημαίνει ότι θα ενεργοποιήσουμε μια νέα σειρά, δηλαδή θα είναι τα τελευταία δεδομένα που θα δούμε.

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

Τώρα εξετάστε την κύρια μέθοδο serialEvent(). Κάθε φορά που εισάγουμε μια νέα γραμμή (\n), καλείται αυτή η μέθοδος. Και κάθε φορά εκτελείται η ακόλουθη σειρά ενεργειών:

  • Τα εισερχόμενα δεδομένα διαβάζονται.
  • Ελέγχεται αν περιέχουν οποιεσδήποτε τιμές (δηλαδή, εάν μας διαβιβάστηκε ένας κενός πίνακας δεδομένων ή "null").
  • Αφαίρεση διαστημάτων.
  • Εάν έχουμε λάβει τα απαραίτητα δεδομένα για πρώτη φορά, αλλάζουμε την τιμή της μεταβλητής boolean πρώτη επαφήκαι πείτε στο Arduino ότι είμαστε έτοιμοι να λάβουμε νέα δεδομένα.
  • Εάν αυτή δεν είναι η πρώτη λήψη του απαιτούμενου τύπου δεδομένων, τα εμφανίζουμε στην κονσόλα και στέλνουμε δεδομένα σχετικά με το κλικ που έγινε στον μικροελεγκτή.
  • Λέμε στο Arduino ότι είμαστε έτοιμοι να λάβουμε ένα νέο πακέτο δεδομένων.
εισαγωγή επεξεργασίας.σειριακό.*; Σειριακή σειρά; // δημιουργία αντικειμένου σειριακής θύρας Η συμβολοσειρά ελήφθη. // δεδομένα που λαμβάνονται από τη σειριακή θύρα // Έλεγχος για δεδομένα από το δυαδικό Arduino firstContact = false; void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); serial.bufferUntil("\n"); ) void draw() ( ) void serialEvent(Serial myPort) ( // σχηματίζουν μια συμβολοσειρά από τα δεδομένα που λαμβάνονται // "\n" - το διαχωριστικό - το τέλος του πακέτου δεδομένων που ελήφθη = myPort. readStringUntil("\n"); // βεβαιωθείτε ότι τα δεδομένα μας δεν είναι άδεια πριν πώς να συνεχίσουμε εάν (received != null) ( //remove spaces λάβει = trim(received); println(received); //αναζητήστε τη συμβολοσειρά μας "A" για να ξεκινήσει η χειραψία //if βρέθηκε, διαγράψτε το buffer και στείλτε αίτημα για δεδομένα εάν (firstContact == false) ( if (received.equals("A")) ( serial.clear(); firstContact = true; myPort.write("A"); println ("επαφή"); ) ) αλλιώς ( //αν η επαφή έχει δημιουργηθεί, λάβετε και αναλύστε τα δεδομένα println(received); if (mousePressed == true) ( ​​//αν κάναμε κλικ στο παράθυρο serial.write( "1"); //send 1 println(" 1"); ) // όταν έχετε όλα τα δεδομένα, κάντε ένα αίτημα για ένα νέο πακέτο serial.write("A"); ) ) )

Κατά τη σύνδεση και την εκκίνηση, η φράση "Hello Kitty" θα πρέπει να εμφανίζεται στην κονσόλα. Όταν κάνετε κλικ στο παράθυρο Επεξεργασία, το LED στον ακροδέκτη 13 θα ανάψει και θα σβήσει.

Εκτός από την Επεξεργασία, μπορείτε να χρησιμοποιήσετε προγράμματα PuTTy ή να γράψετε το δικό σας πρόγραμμα C# χρησιμοποιώντας έτοιμες κλάσεις για εργασία με θύρες.

04.Επικοινωνία: Dimmer

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

Για παράδειγμα, χρειάζεστε ένα τυπικό κύκλωμα με αντίσταση και LED στον ακροδέκτη 9.

Σκίτσο για το Arduino.

Const int ledPin = 9; // LED στην ακίδα 9 void setup() ( Serial.begin(9600); // ορίστε τη λειτουργία στην καρφίτσα pinMode(ledPin, OUTPUT); ) void loop() (φωτεινότητα byte; // ελέγξτε αν υπάρχουν δεδομένα από ο υπολογιστής if (Serial.available()) ( // διάβασε τα τελευταία ληφθέντα byte από 0 έως 255 φωτεινότητα = Serial. read(); // ορίστε τη φωτεινότητα του LED analogWrite(ledPin, φωτεινότητα); ) )

Κώδικας για Επεξεργασία

Εισαγωγή processing.serial.*; σειριακή θύρα; void setup() ( size(256, 150); println("Διαθέσιμες σειριακές θύρες:"); println(Serial.list()); // Χρησιμοποιεί την πρώτη θύρα σε αυτήν τη λίστα (αριθμός 0). Αλλάξτε την για να επιλέξετε το θύρα // που αντιστοιχεί στην πλακέτα σας Arduino. Η τελευταία παράμετρος (π.χ. 9600) είναι η // ταχύτητα της επικοινωνίας. Πρέπει να αντιστοιχεί στην τιμή που έχει περάσει στο // Serial.begin() στο σκίτσο του Arduino. port = new Serial (this, Serial.list(), 9600); // Εάν γνωρίζετε το όνομα της θύρας που χρησιμοποιείται από την πλακέτα Arduino, καθορίστε ρητά //port = new Serial(this, "COM1", 9600); ) void draw( ) ( // σχεδιάστε μια κλίση από μαύρο σε λευκό για (int i = 0; i

Εκκινήστε και μετακινήστε το ποντίκι πάνω από το παράθυρο που δημιουργήθηκε προς οποιαδήποτε κατεύθυνση. Όταν μετακινείστε προς τα αριστερά, η φωτεινότητα του LED θα μειωθεί, όταν μετακινηθείτε προς τα δεξιά, θα αυξηθεί.

04. Επικοινωνία: PhysicalPixel (Ανάψτε το LED με το ποντίκι)

Ας αλλάξουμε λίγο το πρόβλημα. Θα μετακινήσουμε το ποντίκι πάνω από το τετράγωνο και θα στείλουμε τον χαρακτήρα "H" (Υψηλό) για να ανάψει το LED στον πίνακα. Όταν το ποντίκι φύγει από την περιοχή του τετραγώνου, θα στείλουμε τον χαρακτήρα "L" (Low) για να σβήσει το LED.

Κωδικός για το Arduino.

Const int ledPin = 13; // pin 13 για LED int incomingByte. // μεταβλητή για λήψη δεδομένων void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // εάν υπάρχουν δεδομένα εάν (Serial.available() > 0) ( // ανάγνωση byte στο buffer incomingByte = Serial.read(); // εάν αυτός είναι χαρακτήρας H (ASCII 72), στη συνέχεια ενεργοποιήστε το LED if (incomingByte == "H") ( digitalWrite(ledPin, HIGH); ) // εάν αυτός είναι χαρακτήρας L (ASCII 76), τότε απενεργοποιήστε το LED εάν (incomingByte == "L") ( digitalWrite(ledPin, LOW); ) ) )

Κώδικας για Επεξεργασία.

Εισαγωγή processing.serial.*; floatboxX; floatboxY; intboxSize=20; booleanmouseOverBox = false; σειριακή θύρα; void setup() ( size(200, 200); boxX = πλάτος / 2.0; boxY = ύψος / 2.0; rectMode(RADIUS); println(Serial.list()); // Ανοίξτε τη θύρα στην οποία είναι συνδεδεμένη η πλακέτα Arduino (σε αυτήν την περίπτωση #0) // Φροντίστε να ανοίξετε τη θύρα με την ίδια ταχύτητα που χρησιμοποιεί το Arduino (9600bps) port = new Serial(this, Serial.list(), 9600); ) void draw() ( background(0 ); // Εάν ο κέρσορας βρίσκεται πάνω από το τετράγωνο, εάν (ποντίκιΧ > κουτίΧ - πλαίσιοΜέγεθος && ποντίκιΧ κουτίY - πλαίσιοΜέγεθος && ποντίκιY

04. Επικοινωνία: Γράφημα (Σχεδιάστε ένα γράφημα)

Εάν στο προηγούμενο παράδειγμα στείλαμε δεδομένα από τον υπολογιστή στον πίνακα, τώρα θα εκτελέσουμε την αντίστροφη εργασία - θα λάβουμε δεδομένα από το ποτενσιόμετρο και θα τα εμφανίσουμε με τη μορφή γραφήματος.

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

Για παράδειγμα, ας αναβοσβήνουμε ένα LED σε μια δεδομένη συχνότητα και ας εκπέμπουμε ταυτόχρονα ήχους ανόδου και πτώσης σαν σειρήνα από έναν πιεζοπομπό. Έχουμε ήδη συνδέσει τόσο το LED όσο και τον πιεζοηλεκτρικό πομπό στο Arduino περισσότερες από μία φορές. Ας συναρμολογήσουμε το κύκλωμα, όπως φαίνεται στο σχήμα.

Εάν συνδέετε μια λυχνία LED σε μια ψηφιακή ακίδα εκτός από το "13", μην ξεχνάτε μια αντίσταση περιορισμού ρεύματος 220 ohm.

2 LED και Piezo Buzzer Controlχρησιμοποιώντας τον τελεστή delay().

Ας γράψουμε αυτό το σκίτσο και ας το ανεβάσουμε στο Arduino.

Const int soundPin = 3; /* Δηλώστε μια μεταβλητή με τον αριθμό της ακίδας στην οποία είναι συνδεδεμένο το πιεζοηλεκτρικό στοιχείο */ const int ledPin = 13; // Δηλώστε μια μεταβλητή με τον αριθμό pin LED void setup()( pinMode(soundPin, OUTPUT); // Δηλώστε την ακίδα 3 ως έξοδο. pinMode(ledPin, OUTPUT); // Δηλώστε την ακίδα 13 ως έξοδο. } void loop() (// Έλεγχος ήχου: τόνος (soundPin, 700); // παράγει ήχο σε συχνότητα 700 Hz καθυστέρηση(200); τόνος (SoundPin, 500); // σε καθυστέρηση 500 Hz(200); τόνος (SoundPin, 300); // σε καθυστέρηση 300 Hz(200); τόνος (SoundPin, 200); // σε καθυστέρηση 200 Hz(200); // Έλεγχος LED: digitalWrite(ledPin, HIGH); // καθυστέρηση πυρκαγιάς(200); digitalWrite(ledPin, LOW); // καθυστέρηση κατάσβεσης(200); }

Αφού το ενεργοποιήσετε, φαίνεται ότι το σκίτσο δεν εκτελείται ακριβώς όπως χρειαζόμαστε: έως ότου η σειρήνα επεξεργαστεί πλήρως, το LED δεν θα αναβοσβήνει και θα θέλαμε να αναβοσβήνει το LED στη διάρκειαο ήχος μιας σειρήνας. Ποιο είναι το πρόβλημα εδώ;

Το γεγονός είναι ότι αυτό το πρόβλημα δεν μπορεί να λυθεί με τον συνηθισμένο τρόπο. Οι εργασίες εκτελούνται από τον μικροελεγκτή αυστηρά διαδοχικά. Χειριστής καθυστέρηση()καθυστερεί την εκτέλεση του προγράμματος για μια καθορισμένη χρονική περίοδο και μέχρι να λήξει αυτός ο χρόνος, οι ακόλουθες εντολές του προγράμματος δεν θα εκτελεστούν. Εξαιτίας αυτού, δεν μπορούμε να ορίσουμε διαφορετική διάρκεια εκτέλεσης για κάθε εργασία στον βρόχο. βρόχος()προγράμματα. Ως εκ τούτου, πρέπει να προσομοιώσετε με κάποιο τρόπο το multitasking.

3 Παράλληλες Διεργασίεςχωρίς τελεστή "delay()".

Η επιλογή στην οποία το Arduino θα εκτελεί εργασίες ψευδοπαράλληλα προτείνεται από τους προγραμματιστές του Arduino. Η ουσία της μεθόδου είναι ότι με κάθε επανάληψη του κύκλου βρόχος()ελέγχουμε αν είναι ώρα να αναβοσβήνει η λυχνία LED (εκτελέστε μια εργασία στο παρασκήνιο) ή όχι. Και αν είναι, τότε αντιστρέφουμε την κατάσταση του LED. Αυτό είναι ένα είδος χειριστή παράκαμψης καθυστέρηση().

Const int soundPin = 3; // μεταβλητή με τον αριθμό ακίδων του πιεζοηλεκτρικού στοιχείου const int ledPin = 13; // μεταβλητή με αριθμό pin LED const long ledInterval = 200; // Διάστημα αναβοσβήνειας LED, msec. int ledState = LOW; // αρχική κατάσταση του LED ανυπόγραφο long previousMillis = 0; // αποθηκεύστε το χρόνο της προηγούμενης ενεργοποίησης των LED void setup()( pinMode(soundPin, OUTPUT); // ορίστε την ακίδα 3 ως έξοδο. pinMode(ledPin, OUTPUT); // ορίστε τον ακροδέκτη 13 ως έξοδο. } void loop() (// Έλεγχος ήχου: τόνος (soundPin, 700); καθυστέρηση (200); τόνος (SoundPin, 500); καθυστέρηση (200); τόνος (SoundPin, 300); καθυστέρηση (200); τόνος (SoundPin, 200); καθυστέρηση (200); // Αναβοσβήνει το LED: // χρόνος από τότε που ενεργοποιήθηκε το Arduino, ms: ανυπόγραφο μεγάλο ρεύμαMillis = millis(); // Εάν είναι ώρα να αναβοσβήνει, εάν (currentMillis - previousMillis >= ledInterval) ( previousMillis = τρέχονMillis; // στη συνέχεια αποθηκεύστε την τρέχουσα ώρα εάν (ledState == LOW) ( // και αντιστρέψτε την κατάσταση LED ledState = HIGH; ) αλλιώς ( ledState = LOW; ) digitalWrite (ledPin, ledState); // εναλλαγή κατάστασης LED ) }

Ένα σημαντικό μειονέκτημα αυτής της μεθόδου είναι ότι το τμήμα κώδικα πριν από το μπλοκ ελέγχου LED πρέπει να εκτελείται ταχύτερα από το χρονικό διάστημα που αναβοσβήνει το LED "ledInterval". Διαφορετικά, το αναβοσβήσιμο θα εμφανίζεται λιγότερο συχνά από όσο χρειάζεται και δεν θα έχουμε το αποτέλεσμα της παράλληλης εκτέλεσης εργασιών. Συγκεκριμένα, στο σκίτσο μας, η διάρκεια της αλλαγής του ήχου της σειρήνας είναι 200+200+200+200 = 800 ms, και ορίζουμε το διάστημα αναβοσβήνει του LED στα 200 ms. Αλλά η λυχνία LED θα αναβοσβήνει σε μια περίοδο 800 ms, που είναι 4 φορές μεγαλύτερη από αυτή που έχουμε ορίσει.

Γενικά, εάν ο κωδικός χρησιμοποιεί τον τελεστή καθυστέρηση(), σε αυτή την περίπτωση είναι δύσκολο να προσομοιωθεί ο ψευδοπαραλληλισμός, επομένως είναι επιθυμητό να αποφευχθεί.

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

4 Χρήση της βιβλιοθήκης ArduinoThreadγια τη δημιουργία παράλληλων νημάτων

Για να λύσουμε το πρόβλημα, θα χρησιμοποιήσουμε μια υπέροχη βιβλιοθήκη Νήμα Arduino, που σας επιτρέπει να δημιουργείτε εύκολα ψευδοπαράλληλες διεργασίες. Λειτουργεί με παρόμοιο τρόπο, αλλά σας επιτρέπει να μην γράφετε κώδικα για να ελέγξετε την ώρα - πρέπει να ολοκληρώσετε την εργασία σε αυτόν τον κύκλο ή όχι. Αυτό μειώνει την ποσότητα του κώδικα και βελτιώνει την αναγνωσιμότητα του σκίτσου. Ας δούμε τη βιβλιοθήκη σε δράση.


Πρώτα απ 'όλα, κατεβάστε το αρχείο της βιβλιοθήκης από τον επίσημο ιστότοπο και αποσυμπιέστε το στον κατάλογο βιβλιοθήκες/Περιβάλλον ανάπτυξης Arduino IDE. Στη συνέχεια μετονομάστε το φάκελο Arduino Thread-masterσε Νήμα Arduino.

Το διάγραμμα καλωδίωσης θα παραμείνει το ίδιο. Μόνο ο κωδικός του προγράμματος θα αλλάξει.

#περιλαμβάνω // σύνδεση της βιβλιοθήκης ArduinoThread const int soundPin = 3; // μεταβλητή με τον αριθμό ακίδων του πιεζοηλεκτρικού στοιχείου const int ledPin = 13; // μεταβλητή με αριθμό pin LED Νήμα ledThread = Thread(); // δημιουργήστε ένα νήμα για τον έλεγχο του νήματος LED soundThread = Thread(); // Δημιουργία ροής ελέγχου για τη σειρήνα void setup()( pinMode(soundPin, OUTPUT); // Δηλώστε την ακίδα 3 ως έξοδο. pinMode(ledPin, OUTPUT); // Δηλώστε την ακίδα 13 ως έξοδο. ledThread.onRun(ledBlink); // εκχώρηση μιας εργασίας στο νήμα ledThread.setInterval(1000); // ορίστε το διάστημα απόκρισης, ms soundThread.onRun(sound); // εκχωρήστε μια εργασία στο νήμα soundThread.setInterval(20); // ορίστε το διάστημα απόκρισης, ms } void loop() (// Ελέγξτε αν είναι ώρα να αλλάξετε το LED: if (ledThread.shouldRun()) ledThread.run(); // έναρξη του νήματος // Ελέγξτε αν είναι ώρα να αλλάξετε τον τόνο της σειρήνας: if (soundThread.shouldRun()) soundThread.run(); // νήμα έναρξης } // ροή LED: void ledBlink() ( static bool ledStatus = false; // Κατάσταση LED On/Off ledStatus = !ledStatus; // invert the state digitalWrite(ledPin, ledStatus); // ενεργοποιήστε/σβήστε το LED } // Ροή σειρήνας: κενός ήχος() ( static int tone = 100; // τόνος ήχου, τόνος Hz(SoundPin, ton); // ενεργοποιήστε τη σειρήνα σε "τόνο" Hz εάν (τόνος)

Στο πρόγραμμα, δημιουργούμε δύο νήματα - ledThreadκαι soundThread, το καθένα εκτελεί τη δική του λειτουργία: το ένα αναβοσβήνει το LED, το δεύτερο ελέγχει τον ήχο της σειρήνας. Σε κάθε επανάληψη του βρόχου για κάθε νήμα, ελέγχουμε αν έχει έρθει ή όχι η ώρα της εκτέλεσής του. Εάν φτάσει, εκκινείται για εκτέλεση χρησιμοποιώντας τη μέθοδο τρέξιμο(). Το κύριο πράγμα δεν είναι να χρησιμοποιήσετε τον χειριστή καθυστέρηση(). Πιο λεπτομερείς εξηγήσεις δίνονται στον κώδικα.


Φορτώστε τον κώδικα στη μνήμη Arduino, εκτελέστε τον. Τώρα όλα λειτουργούν ακριβώς όπως θα έπρεπε!

Μπορεί να απαιτηθούν αρκετές διακοπές κατά την υλοποίηση του έργου, αλλά εάν καθεμία από αυτές έχει τη μέγιστη προτεραιότητα, τότε στην πραγματικότητα καμία από τις λειτουργίες δεν θα την έχει. Για τον ίδιο λόγο, δεν συνιστάται η χρήση περισσότερων από δώδεκα διακοπών.

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

Τη στιγμή που μια διακοπή είναι ενεργή και οι υπόλοιπες είναι απενεργοποιημένες, υπάρχουν δύο σημαντικές αποχρώσεις που πρέπει να λάβει υπόψη ο σχεδιαστής του κυκλώματος. Πρώτον, ο χρόνος διακοπής πρέπει να είναι όσο το δυνατόν μικρότερος.

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

Μην χρησιμοποιείτε μακροχρόνια επεξεργασία βρόχος() , είναι καλύτερο να αναπτύξετε κώδικα για έναν χειριστή διακοπής με τη μεταβλητή να έχει οριστεί σε Volatile. Θα πει στο πρόγραμμα ότι δεν χρειάζεται περαιτέρω επεξεργασία.

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

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

Με ποιες λειτουργίες λειτουργεί ένας συγκεκριμένος χρονοδιακόπτης;

Για τον μικροελεγκτή Arduino Uno, καθένας από τους τρεις χρονοδιακόπτες έχει τις δικές του λειτουργίες.

Έτσι Χρονοδιακόπτης0 είναι υπεύθυνος για τις λειτουργίες PWM στην πέμπτη και έκτη ακίδα millis () , micros () , καθυστέρηση() .

Ένα άλλο χρονόμετρο - χρονοδιακόπτης 1, χρησιμοποιείται με PWM στην ένατη και δέκατη ακίδα, με βιβλιοθήκες WaveHC και Servo.

Χρονοδιακόπτης 2 λειτουργεί με PWM στις ακίδες 11 και 13, καθώς και με τόνος.

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

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

Το μειονέκτημα αυτής της διαδικασίας μπορεί να είναι ότι οι πραγματικές τιμές αντικαθίστανται με αποθηκευμένα αντίγραφα, γεγονός που μπορεί να οδηγήσει σε απώλεια λειτουργικότητας.

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