ΕΡΓΑΣΤΗΡΙΟ 4 ΕΙΣΑΓΩΓΗ ΣΤΟN ARDUINO: ΨΗΦΙΑΚΗ ΕΙΣΟΔΟΣ/ΕΞΟΔΟΣ Σκοπός της άσκησης Οι φοιτητές θα εξοικειωθούν με την πλακέτα του μικροελεγκτή και θα αναγνωρίσουν τα βασικά της στοιχεία. Επίσης θα εξοικειωθούν με το περιβάλλον ανάπτυξης εφαρμογών και θα γράψουν απλό κώδικα για τον έλεγχο κυκλωμάτων, όπως LEDs, και για την είσοδο δεδομένων από μπουτόν. 4.1 Εισαγωγή στην πλακέτα Arduino UNO Ο Arduino UNO είναι μια προγραμματιζόμενη πλακέτα, που στηρίζεται στον μικροελεγκτή AVR ATmega328P. O μικροελεγκτής διαθέτει συνολικά 28 ακροδέκτες, που μοιράζονται στους συνδετήρες της πλακέτας, όπως φαίνεται στο παρακάτω αναλυτικό Σχήμα 4.1. Σχήμα 4.1 Περιγραφή της πλακέτας του Arduino UNO Μερικά βασικά χαρακτηριστικά του μικροελεγκτή ATmega 328P είναι τα εξής: 1. Είναι μικροελεγκτής τύπου RISC, με αρχιτεκτονική Harvard, και διαθέτει σύνολο εντολών που αποτελείται από 131 εντολές. Εμείς, θα τον προγραμματίσουμε σε ανώτερη γλώσσα προγραμματισμού (C-like Wiring) και συνεπώς δεν ασχοληθούμε με το βασικό set εντολών, όπως κάναμε με τους PIC.
2. Διαθέτει 32Κbytes μνήμης προγράμματος, τύπου Flash πάνω στο τσιπ. Το εύρος κάθε θέσης μνήμης προγράμματος είναι 16bits. Επίσης, διαθέτει 32 βασικούς καταχωρητές και 2Kbytes μνήμης δεδομένων τύπου SRAM, πάνω στο τσιπ. 3. Στην πλακέτα Arduino, ο μικροελεγκτής χρονίζεται από κρυσταλλικό ταλαντωτή 16MHz, εξασφαλίζοντας απόδοση 16 MIPS. 4. Διαθέτει 14 ακροδέκτες αφιερωμένους σε ψηφιακή είσοδο/έξοδο, από τους οποίους δύο αποτελούν τους ακροδέκτες RX, TX της σειριακής θύρας (UART). 6. Διαθέτει έξι (6) κανάλια αναλογικής εισόδου (Α0-Α5). Κάθε κανάλι έχει ανάλυση 10 bits και η προκαθορισμένη τάση αναφοράς είναι 5 V (μπορεί να ρυθμιστεί). 7. Διαθέτει έξι κανάλια σημάτων τύπου PWM για εφαρμογές οδήγησης φορτίων όπως μοτέρ ή για την προσομοίωση αναλογικής εξόδου. Οι έξοδοι αυτές βρίσκονται σε ορισμένους ακροδέκτες ψηφιακής εξόδου (3, 5, 6, 9, 10, 11) (συμβολίζονται με ~). 8. Ο μικροελεγκτής τροφοδοτείται με τάση 5V, αλλά η πλακέτα μπορεί να δεχτεί τάσεις 7-12V στο βύσμα τροφοδοσίας. Μπορεί να τροφοδοτηθεί κατευθείαν από τη θύρα USB, όπου συνδέεται για τον προγραμματισμό του. 9. Διαθέτει μια hardware θύρα UART (ακροδέκτες 0, 1 στην πλακέτα), μία θύρα I2C και θύρα SPI. Επίσης, διαθέτει εξελιγμένο σύστημα διακοπών (interrupts) και τρεις συνολικά χρονιστές (timers). Η βασικές καινοτομίες που εισάγονται με την πλακέτα Arduino είναι οι εξής: 1. Διαθέτει bootloader, εγκατεστημένο στη μνήμη προγράμματος του AVR, που καθιστά δυνατό τον προγραμματισμό κατευθείαν μέσω της θύρας USB του PC. Έτσι, δεν χρειάζονται εξειδικευμένα εργαλεία προγραμματισμού. Ας σημειωθεί, ότι η πλακέτα διαθέτει τσιπάκι USB to Serial, ώστε ο προγραμματισμός γίνεται μέσω της σειριακής θύρας (UART) του μικροελεγκτή. 2. Προγραμματίζεται μέσω ενός απλού περιβάλλοντος, που λέγεται Arduino IDE, που είναι εύκολο στην εκμάθηση. Ο προγραμματισμός γίνεται σε γλώσσα Wiring, που μοιάζει πολύ με τη C. Έτσι, ο χρήστης δεν χρειάζεται να προγραμματίσει σε χαμηλό επίπεδο, για απλές εφαρμογές. 3. Πρόκειται για ανοιχτό υλισμικό, δηλαδή τα σχέδια είναι διαθέσιμα με άδειες Creative Commons σε κάθε ενδιαφερόμενο, που θέλει να αναπτύξει το δικό του σύστημα. Έτσι, έχουν αναπτυχθεί διάφορες παραλλαγές και το σύστημα βρήκε μεγάλη διάδοση. 4. Έχουν αναπτυχθεί πολλές βιβλιοθήκες, που ενσωματώνονται στο περιβάλλον ανάπτυξης. Οι βιβλιοθήκες αυτές δίνουν τη δυνατότητα εύκολου προγραμματισμού, για την επικοινωνία με αισθητήρες, επενεργητές και επεκτάσεις (μοτέρ, οθόνες, ethernet, ασύρματα δίκτυα κ.λπ.)
4.2 Αρχές προγραμματισμού του Arduino 4.2.1 Δομή προγραμματισμού Ο Arduino προγραμματίζεται στο περιβάλλον Arduino IDE, το οποίο καταφορτώνεται ελεύθερα από την ιστοσελίδα του προϊόντος (www.arduino.cc/en/main/software). Σχήμα 4.2 Το περιβάλλον προγραμματισμού και η Δομή του Η Δομή αποτελείται από δύο συναρτήσεις με σταθερά ονόματα, τις setup() και loop() Η συνάρτηση setup() συντάσσεται ως εξής: void setup() { //κώδικας που εισάγει ο προγρ/στής Η συνάρτηση setup() δεν παίρνει ορίσματα και δεν επιστρέφει αποτελέσματα (void). Εκτελείται μία μόνο φορά κατά την έναρξη του προγράμματος ή μετά από κάθε επανεκκίνηση του Arduino (reset). Επομένως στην setup() βάζουμε κώδικα αρχικοποίησης που εκτελείται άπαξ στην αρχή του προγράμματος. Η συνάρτηση loop() συντάσσεται ως εξής: void loop() {
//κώδικας που εισάγει ο προγρ/στής Η συνάρτηση loop() δεν παίρνει ορίσματα και δεν επιστρέφει αποτελέσματα (void). Εκτελείται ως ατέρμων βρόχος, μέχρι να πατηθεί το Reset. Προσοχή στις αγκύλες! Σταθερές και μεταβλητές του προγράμματος ορίζονται πριν τη συνάρτηση setup(), για να είναι καθολικές (global). Σταθερές και μεταβλητές που ορίζονται μέσα στο σώμα των συναρτήσεων είναι ορατές μόνον μέσα στις συναρτήσεις (local). Συναρτήσεις που ορίζει ο προγραμματιστής (user defined), ορίζονται μετά τη loop(). 4.2.2 Τύποι δεδομένων Οι τύποι δεδομένων που υποστηρίζει η γλώσσα προγραμματισμού, είναι αντίστοιχοι με αυτούς που χρησιμοποιούμε στη C/C++: boolean, με τιμές το 0 και 1 (ή True False) byte, με τιμές από 0 έως και 255 int, ακέραιος με δυνατές τιμές από -32768 έως και 32767 long, ακέραιος με δυνατές τιμές από -2147483648 έως και 2147483647 float, δεκαδικοί αριθμοί char, ένας χαρακτήρας (μέγεθος ένα Byte) string, πίνακας χαρακτήρων και οι δηλώσεις τους γίνονται κατά τα γνωστά, ως εξής: int my_pin = 13; // ορίζω ακέραια μεταβλητή my_pin (αρχική τιμή 13) float my_val; // ορίζω πραγματική μεταβλητή my_val boolean State; //ορίζω boolean μεταβλητή (TRUE/FALSE) Παρατηρούμε τον απαραίτητο χαρακτήρα ; στο τέλος κάθε πρότασης. Ας σημειωθεί ότι υπάρχουν κι άλλοι, πιο εξειδικευμένοι τύποι δεδομένων. 4.2.3 Σχόλια Ότι ακολουθεί τους χαρακτήρες // σε μια γραμμή, όπως φαίνεται παραπάνω στον ορισμό μεταβλητών, είναι σχόλιο και δεν μεταφράζεται. Πολλές γραμμές σχολίων τοποθετούνται ανάμεσα στους χαρακτήρες /* και */ ως εξής: /* Ακολουθούν πολλές γραμμές σχολίων, που κλείνουν με τους χαρακτήρες */
4.2.4 Ψηφιακή Είσοδος/Έξοδος Όπως γνωρίζουμε και από άλλους μικροελεγκτές, για τη λειτουργία I/O είναι απαραίτητη η αρχικοποίηση των ακροδεκτών και ο χαρακτηρισμός τους ως εισόδων ή εξόδων, μέσα στη συνάρτηση setup(). Για το σκοπό αυτό, ο Arduino χρησιμοποιεί τη συνάρτηση pinmode(pin, Mode) με ορίσματα α) τον αριθμό Pin και β) την κατάσταση λειτουργίας που χαρακτηρίζεται με τη λέξη INPUT (είσοδος) ή OUTPUT(έξοδος). Για παράδειγμα: pinmode(12, OUTPUT); pinmode(my_pin, OUTPUT); pinmode(α2, INPUT); pinmode(5, INPUT) όπου A2 είναι εξ' ορισμού ο ακροδέκτης αναλογικής εισόδου Α2, ενώ 12 είναι ο αντίστοιχος ακροδέκτης ψηφιακής εισόδου/εξόδου. Η μεταβλητή χρήστη my_pin πρέπει να έχει οριστεί στην αρχή του προγράμματος. Μετά τον παραπάνω ορισμό, είμαστε σε θέση να εξάγουμε λογικό 0 ή 1 (0 ή 5V) στους ακροδέκτες 12 και my_pin (παραπάνω θέσαμε int my_pin = 13; άρα πρόκειται για τον ακροδέκτη 13). Επίσης, μπορούμε να διαβάσουμε τιμές από τον ακροδέκτη Α2. Η έξοδος λογικών καταστάσεων σε έναν ακροδέκτη γίνεται με την εντολή digitalwrite(pin), όπου το όρισμα Pin αναφέρεται στον αριθμό του ακροδέκτη εξόδου. digitalwrite(my_pin, HIGH); ή αντίστοιχα digitalwrite(my_pin, LOW); Προϋπόθεση είναι να έχουμε ορίσει τον ακροδέκτη ως έξοδο, μέσω της συνάρτησης pinmode. Για την ψηφιακή είσοδο χρησιμοποιούμε τη συνάρτηση digitalread(pin), όπου το όρισμα Pin αναφέρεται στον αριθμό του ακροδέκτη εισόδου. Για παράδειγμα: my_val = digitalread(a2); State=digitalRead(5); Προϋπόθεση είναι να έχουμε ορίσει τους ακροδέκτες ως εισόδους, μέσω της συνάρτησης pinmode.
4.2.5 Καθυστερήσεις (delays) Γι α να πετύχουμε χρονικές καθυστερήσεις, που θα επιτρέψουν τη σωστή αλληλεπίδραση του χρήστη με το σύστημα, χρησιμοποιούμε τη συνάρτηση delay(ms) ή τη συνάρτηση delaymicroseconds(μs). Το όρισμα είναι ο αριθμός των ms ή των ms αντίστοιχα, της καθυστέρησης στην εκτέλεση του προγράμματος: delay(500); //σταματά την εκτέλεση για 500 ms = 0.5 sec 4.2.6 Εμφάνιση αποτελεσμάτων στη σειριακή κονσόλα Ο arduino μας δίνει τη δυνατότητα να εκτυπώσουμε αποτελέσματα στη σειριακή κοσνόλα της εφαρμογής arduino IDE, με τη βοήθεια της σειριακής θύρας. Προϋπόθεση είναι η αρχικοποίηση της σειρικής θύρας, στη συνάρτηση setup(): Serial.begin(baud_rate); όπου το baud rate είναι ο ρυθμός μετάδοσης σε bits / sec και μπορεί να πάρει τις τιμές 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, ή 115200. Για παράδειγμα: Serial.begin(9600); Για την αποστολή τιμών μεταβλητών μέσω της σειριακής θύρας μπορεί να χρησιμοποιηθεί η μέθοδος: Serial.println(μεταβλητή ή σταθερά); η οποία μεταφέρει μέσω της σειριακής θύρας την τιμή της μεταβλητής ή σταθεράς σε αναγνώσιμη ASCII μορφή και αμέσως μετά μεταδίδει έναν χαρακτήρα αλλαγής γραμμής. Η ανάγνωση των τιμών που αποστέλλει ο Arduino μπορεί να γίνει από το Arduino IDE πατώντας το κουμπί Serial Monitor ή επιλέγοντας από το μενού Tools->Serial Monitor. Παραλλαγή της μεθόδου αυτής είναι η μέθοδος: Serial.print(μεταβλητή ή σταθερά); που κάνει ακριβώς το ίδιο όπως και η Serial.println() αλλά χωρίς να στέλνει την αλλαγή γραμμής.
4.3 Εργαστηριακό μέρος 4.3.1 Ψηφιακή έξοδος σε LED Δημιουργήστε το παρακάτω κύκλωμα 1. Ανοίξτε το περιβάλλον Arduino IDE 2. Συνδέστε την πλακέτα Arduino με τη θύρα USB του υπολογιστή 3. Επιλέξτε Tools->Board->Arduino/Genuino Uno 4. Επιλέξτε Tools->Port-> την τσεκαρισμένη θύρα επικοινωνίας COM 5. Στο περιβάλλον Arduino IDE δημιουργήστε ένα νέο sketch file (File->New) 6. Πληκτρολογήστε τον παρακάτω κώδικα: int ledpin = 10; void setup() { pinmode(ledpin, OUTPUT); void loop() { digitalwrite(ledpin, HIGH); delay(1000); digitalwrite(ledpin, LOW); delay(1000); 7. Κάνετε upload την εφαρμογή και παρατηρήστε το αποτέλεσμα. 8. Μεταβάλετε τις τιμές του ορίσματος στη συνάρτηση delay.
4.3.2 Ψηφιακή είσοδος από button Δημιουργήστε το παρακάτω κύκλωμα, με ένα button και μια αντίσταση Συζητήστε στην τάξη το κυκλωματικό διάγραμμα: Πληκτρολογήστε το παρακάτω πρόγραμμα και συζητήστε το στην τάξη: // Πρόγραμμα ανάγνωσης ψηφιακής θύρας και αποστολής στον Η/Υ μέσω σειριακής σύνδεσης // καθορισμός μεταβλητής με τον αριθμό της ψηφιακής θύρας για ανάγνωση int pushbutton = 2; // Η συνάρτηση setup εκτελείται μία φορά κατά την έναρξη του προγράμματος void setup() {
// Αρχικοποίηση σειριακής επικοινωνίας στα 9600 bits / second: Serial.begin(9600); // αρχικοποίηση του pin 2 ως είσοδος pinmode(pushbutton, INPUT); // Η συνάρτηση loop εκτελείται συνέχεια ως ατέρμων βρόχος void loop() { // ανάγνωση της ψηφιακής θύρας int buttonstate = digitalread(pushbutton); // Αποστολή της ψηφιακής τιμής μέσω σειριακής θύρας προς το Serial Monitor του Η/Υ. Serial.println(buttonState); // καθυστέρηση 1 msec για σταθερότητα delay(30);