Εργαστήριο ενσωματωμένων συστημάτων Παρουσίαση 2: Βασικός Προγραμματισμός Arduino (AVR) Εργαστήριο Αυτομάτου Ελέγχου
Η πλατφόρμα Arduino UNO Microcontroller: ATmega328 Operating Voltage: 5V Digital I/O Pins: 14 (of which 6 provide PWM output) Analog Input Pins: 6 Flash Memory: 32 KB (ATmega328) SRAM: 2 KB (ATmega328) EEPROM: 1 KB (ATmega328) Clock Speed: 16 MHz
Προγραμματισμός Arduino Η γλώσσα του Arduino υποστηρίζει όλες τις βασικές δομές της C καθώς και κάποιες της C++. Xρησιμοποιεί το GNU toolchain και το AVR Libc για να μεταγλωττίζει προγράμματα. Υπάρχουν έτοιμες βιβλιοθήκες για την επικοινωνία με πληθώρα συσκευών εισόδου/εξόδου. Ο χρήστης θα πρέπει να ορίσει δύο βασικές λειτουργίες για να κάνουν ένα πρόγραμμα κυκλικής εκτέλεσης: void setup() void loop()
Κλάσσεις στην C++ Ορισμός Οι κλάσεις χρησιμοποιούνται για την υλοποίηση αφηρημένων τύπων δεδομένων. Για το σκοπό αυτό μπορούμε να ορίσουμε μέλη (members) της κλάσης: μεταβλητές και συναρτήσεις που είναι ορατά μόνο από συναρτήσεις που αναφέρονται στον τύπο αυτό (private) καθώς και μεταβλητές και συναρτήσεις που είναι καθολικά ορατά (public). Οι μεταβλητές ορίζουν ιδιότητες (properties) και οι συναρτήσεις ορίζουν μεθόδους πρόσβασης (methods) των αντικειμένων της κλάσης. Η δήλωση μιας κλάσης (class) είναι παρόμοια με αυτή μιας δομής.
Κλάσσεις στην C++ Παράδειγμα header / point.h
Κλάσσεις στην C++ Παράδειγμα class implementation / point.cpp
Κλάσσεις στην C++ Παράδειγμα source / main.cpp
Βασικές συναρτήσεις int res = map(value_in, from_min, from_max, to_min, to_max): Μετασχηματίζει την value_in από ένα αρχικό εύρος σε ένα τελικό. Μόνο για ακεραίους. Ψηφιακές θύρες: pinmode(pin,mode): Ορίζει την λειτουργία του pin (είσοδος/έξοδος). Την χρησιμοποιούμε κάθε φορά που συνδέουμε κάποιο σήμα εισόδου ή εξόδου στον Arduino. digitalwrite(pin,val): Γραφει την τιμή εξόδου του pin (LOW/HIGH). Χρησιμοποιείται σε ψηφιακά pins. Χρήσιμη μεταξύ άλλων για να οδηγήσουμε LEDs ή να θέσουμε λογικές εισόδους σε περιφερειακά. val = digitalread(pin): Διαβάζει την τιμή του pin. Χρησιμοποιείται σε ψηφιακά pins. Χρήσιμη για να διαβάσουμε κουμπιά. analogwrite(pin,val): (PWM) Γραφει την τιμή % duty cycle του pin (0-255). Η default συχνότητα της παλμοσειράς είναι περίπου 500Hz και δεν αλλάζει εύκολα. Για duty cycle 0 τη σήμα είναι πρακτικά σταθερά 0, ενώ για 255 σταθερά 5V. Χρησιμοποιείται σε ψηφιακά pins με δυνατότητα PWM (3, 5, 6, 9, 10, 11). Χρήσιμη για να οδηγήσουμε σήματα ελέγχου κινητήρων (όχι απευθείας). Αναλογικές θύρες: val = analogread(pin): Διαβάζει την τιμή του pin (0-1023) Χρησιμοποιείται για τα αναλογικά pins. Τιμή 0 σημαίνει ότι στο pin ασκούνται 0V, ενώ 1023 σημαίνει πως του ασκούνται 5V. Χρήσιμη για να διαβάζουμε ποτενσιόμετρα και αναλογικούς αισθητήρες.
Βασικές συναρτήσεις (2) Χρονιστές time = millis(): Επιστρέφει τον χρόνο σε ms από τότε που άρχισε το πρόγραμμα time = micros(): Επισρέφει τον χρόνο σε μs από τότε που άρχισε το πρόγραμμα Προσοχή, καθώς μπορεί να κάνει overflow σε σχετικά μικρό χρονικό διάστημα. delay(val): Κάνει πάυση το πρόγραμμα για val ms. Τίποτα άλλο δεν τρέχει όσο αναμένεται να περάσει το διάστημα αναμονής (εκτός από interrupts). delaymicroseconds(val): Κάνει πάυση το πρόγραμμα για val μs
Βασικές συναρτήσεις (3) Σειριακές θύρες / USB Με την σειριακή θύρα μπορούμε να επικοινωνήσουμε είτε με άλλες συσκευές που διαθέτουν σειριακές θύρες ή με τον υπολογιστή μεσω USB. Η σειριακή θύρα είναι διαθέσιμη μέσω του αντικειμένου Serial. Βασικές μέθοδοι κλάσης Serial: val = avaliable(): Επιστρέφει τον αριθμό των bytes που είναι διαθέσιμα προς ανάγνωση στην θύρα. begin(baudrate): Ξεκινάει/Ρυθμίζει την σειριακή θύρα. Απαραίτητο στην αρχή κάθε προγράμματος, μέσα στην setup(). print(val): Τυπώνει την τιμή val σε ASCII text. println(val): Τυπώνει την τιμή val σε ASCII text και αλλάζει γραμμή ('\n'). val = read(): Διαβάζει ένα byte απο την σειριακή. readbytes(buffer,length): Διαβάζει length bytes απο την σειριακή και τα σώζει στον buffer. val = parseint(): Επιστρέφει τον πρώτο διαθέσιμο ακέραιο που αναμένει στην σειριακή θύρα. Περιμένει για κάποιον αριθμό έως ένα συγκεκριμένο timeout διάστημα.
Βασικές συναρτήσεις (4) Interrupts Όλοι οι μικροεπεξεργαστές υποστηρίζουν interrupts. Είναι ένας μηχανισμός που επιτρέπει την παρακολούθηση της κατάστασης ενός pin σε παράλληλο χρόνο με την εκτέλεση του προγράμματος. Όταν συμβαίνει μία ορισμένη μεταβολή στο pin, τότε καλείται ασύχρονα μία προκαθορισμένη συνάρτηση. Βασικές συναρτήσεις attachinterrupt(pin, isr, mode): Δηλώνει πως αντιστοιχίζεται μία interrupt service routine στο pin. O Arduino UNO υποστηρίζει interrupts στα pins 2 και 3. Η interrupt routine που θα εξυπηρετεί το interrupt είναι η isr, την οποία γράφετε εσείς. Δεν θα πρέπει να παίρνει ορίσματα ούτε να επιστρέφει αποτελέσματα. Λειτουργεί κυρίως πάνω σε global μεταβλητές. Το όρισμα mode καθορίζει πότε θα καλείται η interrupt service routine. Δυνατές τιμές είναι: LOW CHANGE RISING FALLING
Βασικές βιβλιοθήκες Servo Η βιβλιοθήκη <Servo.h> επιτρέπει τον έλεγχο σερβοκινητήρων από τον Arduino, μέσω σημάτων PWM. Instantiation Servo myservo; Βασικές μέθοδοι κλάσης: attach(pin): Αντιστοιχίζει ένα αντικείμενο servo σε ένα pin του Arduino (οποιοδήποτε ψηφιακό). attach(pin, min, max): Επιπλέον ορίζει την μέγιστη και την ελάχιστη διάρκεια του παλμού που θα στέλνεται προς το servo και θα αντιστοιχεί σε εντολή 0 και 180 μοίρες αντίστοιχα. Ίσως να θέλετε να βαθμονομήσετε τις τιμές αυτές ανάλογα με τον σερβοκινητήρα που σας δίνεται. write(degrees): Στρέφει τον άξονα του σερβοκινητήρα στις degree μοίρες. Η συχνότητα της παλμοσειράς είναι 50Hz, κατάλληλη για τα περισσότερα servo του εμπορίου.
Βασικές βιβλιοθήκες Επικοινωνία με I2C (Wire) Η βιβλιοθήκη Wire, η οποία περιλαμβάνεται στην βασική έκδοση του Arduino IDE, ενσωματώνει τις βασικές λειτουργίες επικοινωνίας με I2C περιφερειακές συσκευές. Βασικές συναρτήσεις δομής: begin()/begin(address): Συνδέει την συσκευή στον δίαυλο I2C ως master ή ως slave με διεύθυνση address. begintransmission(address): Ξεκινά την επικοινωνία μεταξύ master και του slave στη διεύθυνση address. endtransmission(address): Λήγει την τρέχουσα επικοινωνία. write(val): Στέλνει στον slave το περιεχόμενο της val (τιμή, string, array) requestfrom(address, quantity): Ζητά από τον slave στη διεύθυνση address απάντηση μήκους quantity val = read(): Διαβάζει ένα byte απο τον slave Συνήθως δεν θα χρειαστεί να ελέγχεται απευθείας το κανάλι δεδομένων της θύρας I2C, αλλά θα χρησιμοποιείτε βιβλιοθήκες που αναλαμβάνουν το ρόλο αυτό, ενώ σας δίνουν μια πιο αφηρημένη δομή ελέγχου.
Ολοκληρωμένο Παράδειγμα (1)
Ολοκληρωμένο Παράδειγμα (2)
Εργαστήριο ενσωματωμένων συστημάτων Τέλος Παρουσίασης Εργαστήριο Αυτομάτου Ελέγχου