Προγραμματισμός AVR -03: Αναγνώριση και μεταγλώτιση κώδικα

Στο προηγούμενο μέρος της εκπαιδευτικής σειράς μας κατασκευάσαμε ένα απλό κύκλωμα σε breadboard και προγραμματίσαμε ένα ATmega168 να τρέχει. Αυτό αποδεικνύει ότι ξέρετε πώς να ακολουθήσετε τις οδηγίες, αλλά η καταπληκτική στιγμή που θα τα κάνετε όλα μόνοι σας έχει ακόμη δρόμο. Αυτή τη φορά γύρω σας, θα έχετε φύλλα δεδομένων, μαθαίνοντας για το από που προήλθε η κάθε γραμμή του κώδικα, και να κάνετε μια δοκιμή στον πρόσφατα εγκαταστημένο μεταγλωττιστή σας . Εμείς θα:
  • Συζητήσουμε για τους δυαδικούς τελεστές και πώς λειτουργούν όταν προγραμματίζουμε μικροελεγκτές
  • Συζητήσουμε για τις συντομεύσεις κώδικα της C
  • Επανεξετάσουμε δείγματα κώδικα από το 2ο τμήμα και θα μιλήσουμε για το τι έχει κάθε γραμμή κώδικα
  • Μάθουμε για την μεταγλώττιση κώδικα
Αν αυτό είναι το πρώτο που έχετε ακούσει για την σειρά μας «Προγραμματισμός AVR» , πάτε πίσω στο 1ο μέρος και να ξεκινήσετε από την αρχή. Διαφορετικά, πάρτε μια βαθιά ανάσα και ξεκινήστε μετά το διάλειμμα.
Χάρτης πορείας:

Προαπαιτούμενα

  • Θα πρέπει να γνωρίζετε κάποιο κωδικό C. Η ικανότητα να διαβάζετε είναι πιθανώς αρκετή, η Google μπορεί να σας βοηθήσει με το υπόλοιπο καθώς θα μαθαίνετε.
  • Βοηθάει αν έχετε ένα κειμενογράφο που περιλαμβάνει επισήμανση σύνταξης (Notepad++).
  • Πάρτε το παράδειγμα κώδικα του 2ου μέρους αυτής της σειράς. Είναι ενσωματωμένο παρακάτω, αλλά μπορείτε να το θέλετε σε ξεχωριστά παράθυρα για αναφορά.
  • Φύλλα δεδομένων, Το εγχειρίδιο οδηγιών για το υλικό. Πάρτε το φύλλο δεδομένων για τον ATmega168 , όπου θα αναφερόμαστε σε συγκεκριμένες σελίδες για παραδείγματα. Γνωρίζοντας πώς να αναζητήσετε πληροφορίες στο φύλλο δεδομένων, και να τις μετατρέπετε σε κώδικα θα καταστήσει εύκολο για σας να χρησιμοποιήσετε οποιαδήποτε τύπο της οικογένειας  AVR.

Δυαδικοί Τελεστές

Ακόμα κι αν εμείς θα πρέπει να γράψουμε κώδικα στη γλώσσα C, είμαστε αρκετά κοντά στο υλικό όταν προγραμματίζουμε μικροελεγκτές . Λόγω αυτού, πρέπει να κατανοήσετε τους δυαδικούς τελεστές. Δεν είναι ακριβώς το είδος μας , όχι διαισθητικά, θα πρέπει να τους γνωρίζετε  αρκετά καλά για να τους διδάξετε σε κάποιον άλλο χωρίς να ψάχνεστε.
Τα χέρια κάτω η καλύτερη εξήγηση που έχω συναντήσει ποτέ είναι από τον [Eric Weddington], ο οποίος επίσης συγγράψει το Makefile που συνοδεύει τον κωδικό των παραδειγμάτων μου. Είναι επίσης γνωστή ως Προγραμματισμός 101. Διαβάστε το, γνωρίστε το, αγαπήστε το.Αλλά εγώ θα προσπαθήσω να σας δώσω ένα γρήγορο πορεία σύγκρουσης για εκείνους που θα τεμπελιάζουν να διαβάσουν ολόκληρο το μάθημά του.
Σύμβολο κώδικα Λογική λειτουργία
| OR – Ή
& AND – ΚΑΙ
~ NOT –  ΟΧΙ
^ XOR – ΑΠΟΚΛΕΙΣΤΙΚΟ Ή
<< Shift Left –  Ολίσθηση αριστερά
>> Shift Right -Ολίσθηση δεξιά
Η παραπάνω λίστα εμφανίζει όλα τα σύμβολα κώδικα και τη λειτουργία τους λογική.
  • OR – Ή  – αληθείς, εάν ένα ή και τα δύο ψηφία που συγκρίνονται είναι 1
  • AND – ΚΑΙ – αληθείς μόνο εάν τα δύο ψηφία που συγκρίνονται είναι 1
  • NOT – ΟΧΙ – έχει ως αποτέλεσμα το αντίθετο από μια τιμή (~ 1 = 0, ~ 0 = 1)
  • XOR – ΑΠΟΚΛΕΙΣΤΙΚΟ Ή- αληθείς  αν ένα ψηφίο που συγκρίνονται είναι 1, αλλά ψευδές αν είναι και τα δύο ή κανένα από τα δύο.
  • Αριστερή Ολίσθηση – μετακινεί ψηφία προς τα αριστερά μέσα σε ένα δυαδικό αριθμό (1<<0 =0b0001,  1<<4  =0b1000)
  • Δεξιά Ολίσθηση  – μετακινεί τα ψηφία προς τα δεξιά σε μια συγκεκριμένη ποσότητα (0b1000>> 2 = 0b0010)
Εμείς πρόκειται να χρησιμοποιούμε το πλήκτρο Αριστερό Shift όλη την ώρα στον κώδικά μας γιατί είναι ένας γρήγορος τρόπος για να χτίζουμε ένα δυαδικό αριθμό. Εργαζόμαστε συνεχώς με δυαδικούς αριθμούς που αποτελούνται από οκτώ ψηφία. Τα εν λόγω ψηφία είναι αριθμημένα σε 0-7, γιατί μετρώντας πάντα ξεκινά με 0, όταν πρόκειται για μικρο-ελεγκτές. Έτσι, εάν θέλετε να ρυθμίσετε το πέμπτο ψηφίο σε λογικό υψηλό («1»),  θα ολισθήσετε το «1», αριστερά για 5 φορές:
 1<<5
Αυτό θα έχει ως αποτέλεσμα το δυαδικό αριθμό 0b00100000. Αν αυτό είναι παιχνίδι για παιδιά, να προχωρήσετε στην επόμενη ενότητα. Αν όχι, διαβάστε το φροντιστήριο του [Έρικ] .

Συντομογραφία κώδικα C

Φροντίζουμε να χρησιμοποιούμε συντομογραφίες στον κώδικά μας γιατί τα χέρια σας συχνά θα πονούν από την πολύ πληκτρολόγηση (όπως κάνουν σήμερα). Εδώ είναι ένας πίνακας με γρήγορα παραδείγματα:
Παραδοσιακός κώδικας Ισοδύναμο συντομογραφίας
value = value + 1;
value += 1;
value = value >> 1;
value >>= 1;
value = value & bitMask;
value &= bitMask;
value = value | bitMask;
value |= bitMask;
PORTD = PORTD ^ (1<<0); PORTD ^= (1<<0);
Έτσι, βασικά, αν καθορίσουμε μια μεταβλητή χρησιμοποιώντας την ίδια μεταβλητή ως τον πρώτο τελεστή μπορούμε να βάλουμε μόνο τον χειριστή πριν από το ίσον και να θέσουμε το δεύτερο τελεστή μετά το σημείο ίσον για να ολοκληρώσετε την ίδια εργασία χωρίς να πληκτρολογήσουμε το όνομα της μεταβλητής δύο φορές. Αν έχετε καταλάβει την πρόταση τότε πάτε πολύ καλά!

Μετάβαση στο παραδείγματα κώδικα

Ψευδοκώδικας
Μια καλή πρακτική κατά την ανάπτυξη κώδικα είναι να γράφετε ψευδοκώδικα. Κάτι που δηλώνει σαφώς τι θέλετε να κάνετε σε απλή γλώσσα. Αυτό είναι μια περίληψη της δομής που θα πάρει το πρόγραμμά σας  και αυτό δεν πρέπει να περιλαμβάνει ειδικό κωδικό, αλλά θα αντικατασταθεί από τον εν λόγω κώδικα αργότερα:
//Setup the clock  Ρύθμιση του ρολογιού
//prepare an interrupt every 1 second  προετοιμασία μιας διακοπής κάθε 1 δευτερόλεπτο
//Setup the I/O for the LED Ρύθμιση του I/O για το LED
//toggle the LED during each interrupt Εναλλαγή του LED κατά τη διάρκεια κάθε διακοπής
Αυτό το πρόγραμμα είναι τόσο απλό που ο ψευδοκώδικα φαίνεται περιττός, αλλά θα σας βοηθήσει και θα σας κρατήσει εστιασμένους να εξορκίσετε τα λάθη σε μεγαλύτερα έργα.
Ο πραγματικός κωδικός
Η main.c από το το παράδειγμα κώδικα του 2ου μέρους είναι ενσωματωμένη στη συνέχεια. Πάρτε λίγο χρόνο για να ταιριάξετε τα τμήματα του παραπάνω ψευδοκώδικα με τα πραγματικά τμήματα κώδικα παρακάτω .
01 /*
02 * Hackaday.com AVR Tutorial firmware
03 * written by: Mike Szczys (@szczys)
04 * 10/24/2010
05 *
06 * ATmega168
07 * Blinks one LED conneced to PD
08 *
09 * http://hackaday.com/2010/10/25/avr-programming-02-the-hardware/
10 */
11
12 #include <avr/io.h>
13 #include <avr/interrupt.h>
14
15 int main(void)
16 {
17
18 //Setup the clock
19 cli(); //Disable global interrupts
20 TCCR1B |= 1<<CS11 | 1<<CS10; //Divide by 64
21 OCR1A = 15624; //Count 15624 cycles for 1 second interrupt
22 TCCR1B |= 1<<WGM12; //Put Timer/Counter1 in CTC mode <
23 TIMSK1 |= 1<<OCIE1A; //enable timer compare interrupt
24 sei(); //Enable global interrupts
25
26 //Setup the I/O for the LED
27
28 DDRD |= (1<<0); //Set PortD Pin0 as an output
29 PORTD |= (1<<0); //Set PortD Pin0 high to turn on LED
30
31 while (1) { } //Loop forever, interrupts do the rest
32 }
33
34 ISR(TIMER1_COMPA_vect) //Interrupt Service Routine
35 {
36 PORTD ^= (1<<0); //Use xor to toggle the LED
37 }
Οι πρώτες γραμμές είναι σχόλια για το ανθρώπινο μάτι και δεν θα χρησιμοποιηθούν από τον μικροελεγκτή. Τα σχόλια στη C προσδιορίζονται από δύο καθέτους (//) για μία γραμμή για σχόλια ή εγκιβωτισμένες σε ζεύγη κάθετο-αστερι (/*) και το αστέρι-κάθετο(*/) για σχόλια πολλών γραμμών. Είναι μια καλή ιδέα να γράψετε αναλυτικά σχόλια για  το πρόγραμμα, τι κάνει, σε τι υλικό επάνω τρέχει , καθώς και κάθε άλλη χρήσιμη πληροφορία. Θεωρώ ότι η επαναχρησιμοποίηση συχνά κώδικα από προηγούμενα έργα και ένα κομμάτι των πληροφοριών στο επάνω μέρος του αρχείου βοηθά στον εντοπισμό αυτό που ψάχνω γρήγορα.
Τα Περιλαμβανόμενα (Includes)
Το επόμενο πράγμα που θα δείτε είναι τα περιλαμβανόμενα (includes):
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
Τα Περιλαμβανόμενα λένε στον μεταγλωττιστή που θα πάει για να χρησιμοποιεί τα πράγματα από άλλα αρχεία. Στην περίπτωση αυτή, τα δύο αρχεία από την AVR libc που έρχονται μαζί με τον  δια-μεταγλώττιση που  έχουμε εγκαταστήσει στο 1ο μέρος. Αυτά είναι αρχεία C, ώστε να μας επιτρέψουν ο κώδικας να είναι αναγνωρίσιμος από τον άνθρωπο (και να τα θυμόμαστε!)  κατά την εργασία με το υλικό πάνω στο τσιπ. Το  αρχείο io.h κρατά όλα τα  αρχεία κεφαλίδας για όλες τα υποστηριζόμενα τσιπ AVR. Ορίζουμε τον επεξεργαστή που χρησιμοποιούμε στο αρχείο Makefile,  και το κατάλληλο αρχείο κεφαλίδας αυτόματα επιλέγεται από io.h όταν μεταγλωττίσουμε τον κώδικα μας αργότερα σε αυτό το μέρος.Στο δικό μας παράδειγμά κώδικα έχουμε χρησιμοποιήσει ονόματα όπως DDRD, PORTD, TCCR1B, OCR1A, TIMSK1, κτλ. Όλα αυτά έχουν διευθύνσεις που έχουν σημειωθεί στη χρήση του αρχείου io.h . Αυτό μας επιτρέπει να καλέσουμε τους ακροδέκτες του τσιπ από τα ονόματα όπως PORTD που είναι τα ίδια σε όλες τις εκδόσεις AVR αντί του μητρώου διευθύνσεις όπως 0x0B το οποίο έχει διαφορετικές λειτουργίες σε διαφορετικά τσιπ. Πιθανότατα θα χρειαστεί να συμπεριλάβετε το αρχείο io.h σε κάθε πρόγραμμα AVR  που χρησιμοποιείτε και κάνοντας κάτι τέτοιο κάνετε τον κώδικά σας πιο μεταφέρσιμο. Το αρχείο interrupt.h είναι απαραίτητο μόνο αν χρησιμοποιείτε διακοπές, κάτι που θα μιλήσουμε στο επόμενο τμήμα κώδικα.
Ρυθμίζοντας το ρολόι για ρήση με διακοπές
Οι μικροεπεξεργαστές έχουν ανάγκη από ένα σήμα ρολογιού , για να λειτουργήσουν. Τα τσιπ AVR μπορούν να χρησιμοποιήσουν εξωτερικό ρολόγια όπως ένας κρυσταλλικός ταλαντωτής ή ένας κεραμικός  συντονιστής, αλλά από το εργοστάσιο έχουν ρυθμιστεί να χρησιμοποιούν τον εσωτερικό ταλαντωτή RC, όπως το ρολόι του συστήματος (διαβάστε περισσότερα στη σελίδα 28 του φύλλου δεδομένων). Ο εσωτερικός ταλαντωτής RC του ATmega168 τρέχει στα 8,0 MHz περίπου  ανάλογα με την τάση της σταθερότητας και της θερμοκρασίας. Επίσης, αποστέλλεται  με ενεργοποιημένη την ασφάλεια DIV8 που διαιρεί το σήμα ρολογιού σε 1,0 MHz. Για το πρόγραμμα του παραδείγματος που θέλουμε ένα LED να ανάβει και να σβήνει, αλλάζει περίπου μια φορά το δευτερόλεπτο. Εδώ είναι το τμήμα κώδικα που καθορίζει αυτή τη λειτουργία:
//Setup the clock
2 cli(); //Disable global interrupts
3 TCCR1B |= 1<<CS11 | 1<<CS10; //Divide by 64
4 OCR1A = 15624; //Count 15624 cycles for 1 second interrupt
5 TCCR1B |= 1<<WGM12; //Put Timer/Counter1 in CTC mode
6 TIMSK1 |= 1<<OCIE1A; //enable timer compare interrupt
7 sei(); //Enable global interrupts
Η πρώτη γραμμή έχει να κάνει με διακοπές. Μία διακοπή είναι ένα μεγάλο χαρακτηριστικό γνώρισμα των μικροεπεξεργαστών. Βασικά λέτε το τσιπ να παρακολουθεί μία συγκεκριμένη κατάσταση. Όταν ταιριάζει με αυτήν την προϋπόθεση θα σταματήσει αυτό που κάνει δεν έχει σημασία πού βρίσκεται, και θα εκτελέσει ένα διαφορετικό σύνολο κώδικα που ονομάζεται Ρουτίνας Εξυπηρέτησης Διακοπής   (Interrupt Service Routine ISR). Επειδή είμαστε έτοιμοι να αλλάξουμε  ορισμένες ρυθμίσεις που έχουν να κάνουν με διακοπές, δεν θέλουμε τίποτα (όπως μια άλλη διακοπή) για να μας σταματήσει στη μέση της διαδικασίας αυτής.Έχω χρησιμοποιήσει μια εντολή που είναι διαθέσιμη σε εμάς, γιατί συμπεριλαμβάνονται στο interrupt.h στην αρχή του αρχείου μας.Η εντολή είναι CLI (); που απενεργοποιεί όλες τις διακόπτες.Μόλις τελειώσετε με τις ρυθμίσεις μας πρέπει να θυμόμαστε να τις ενεργοποιήσουμε και πάλι, χρησιμοποιώντας την εντολή SEI (); .Μπορείτε να δείτε πως το έχουμε κάνει στο κάτω μέρος αυτού του τμήματος κώδικα.
Τώρα θέλουμε να παρακολουθήσουν αν περάσει 1 δευτερόλεπτο χρόνου. Οι τέσσερις γραμμές μεταξυ των δύο εντολών που χρησιμοποιούμε είναι για να στήσουμε ένα μετρητή για αυτό. Επειδή ο εσωτερικός ταλαντωτής λειτουργεί στο 1 MHz, ή 1 εκατομμύριο κύκλοι ανά δευτερόλεπτο, θα πρέπει να προκαλούν μια διακοπή κάθε 1 εκατομμύρια κύκλους. Το μεγαλύτερο χρονόμετρο σε αυτό το τσιπ έχει  16-ψηφία και μπορεί να υπολογίζει μόνο από το 0 έως 65.535. Με άλλα λόγια, δεν έχουμε ένα χρονόμετρο που μπορεί να μετρήσει αρκετά ώστε να μετρήσει ένα τόσο μεγάλο αριθμό κύκλων.
Ευτυχώς, έχουμε τη δυνατότητα να χρησιμοποιήσετε ένα διαιρέτη με το χρονόμετρο μας, που ονομάζεται υποβάθμιση ?????(prescaler). Για να γίνει αυτό βλέπουμε στο δελτίο στη σελίδα 134 να δείτε ένα διάγραμμα που περιγράφει το ρολόι που επιλέξετε. Δείχνει επιλογές υποβάθμισης που διαιρούν το ρολόι του συστήματος κατά 1, 8, 64, 256 και 1024. Γνωρίζοντας ότι θέλουμε να μετρούν 1.000.000 κύκλους μπορούμε να χρησιμοποιήσουμε ένα κομμάτι των μαθηματικών, για να επιλέξετε την καλύτερη υποβάθμιση:
1,000,000 / 1 = 1,000,000
1,000,000 / 8 = 125,000
1,000,000 / 64 = 15,625
1,000,000 / 256 = 3,906.25
1,000,000 / 1024 = 976.5625
Τα μαθηματικά μας αφήνουν μόνο με μία επιλογή.
Τα μαθηματικά μας αφήνουν μόνο με μία επιλογή. Αυτό συμβαίνει γιατί με τη χρήση της υποβάθμισης του 1 ή 8 τα αποτελέσματα σε μια σειρά κύκλων που είναι μεγαλύτερο από 65.536 μέχρι 16-bit χρονοδιακόπτη μας δεν μπορεί να υπολογίζει αρκετά υψηλή. Η υποβάθμιση των 256 και 1024 δίνουν τα αποτελέσματα που δεν είναι σε ακέραιο αριθμό. Αν δεν χρησιμοποιήσουμε σε ακέραιο αριθμό έχουμε εισαγάγει μια ανακρίβεια στη χρονική στιγμή μας γιατί δεν μπορούμε να μετρήσουμε ένα κλάσμα ενός κύκλου. Μια υποβάθμιση από 64 πληροί και τις δύο ανάγκες μας, είναι ένα ακέραιο αριθμό που είναι μικρότερη από τα όρια των 16-bit μετρητών μας.
Πώς μπορούμε να δημιουργήσουμε αυτή την υποβάθμιση; Το εγχειρίδιο του AVR τα λέει όλα. Κοιτάζοντας στο «Timer/Counter1 Control Register Β» (TCCR1B), η οποία εκτείνεται στις σελίδες 133 και 134 μπορούμε να βρούμε την απάντηση. Το διάγραμμα 15-5 δείχνει ένα  πίνακα ρυθμίσεων ρολογιού. Στην περίπτωσή μας πρέπει να θέσουμε τα CS10 και  CS11 σε ‘1’ στον καταχωριτή  TCCR1B. Για να γίνει αυτό χρησιμοποιούμε ένα τελεστή OR  και μια Αριστερά Ολίσθηση ενός  «1» στη θέση των ψηφίων  CS10 και CS11:
TCCR1B |= 1<<CS11 | 1<<CS10;
Επειδή αυτή είναι η πρώτη πραγματική μαθηματική μας εντολή bitwise, ας την  εξετάσουμε σε βάθος. Πρώτα , πρέπει να ρυθμίσουμε μόνο δύο ψηφία στον καταχωριτή, έτσι δεν θέλουμε να χρησιμοποιήσουμε απλά μόνο μία ισότητα. Αν το είχαμε κάνει αυτό, η εντολή αυτή θα έθετε όλα τα άλλα ψηφία σε «0». Αντ ‘αυτού, μπορούμε  να χρησιμοποιήσουμε  την συντομογραφία κώδικα για τον τελεστή OR και να συγκρίνουμε τον  TCCR1B με μια μάσκα bitmask που περιέχει «1» στις σωστές θέσεις για τα ψηφία CS10 και CS11. Οποιαδήποτε άλλα ψηφία στον καταχωριτή TCCR1B που έχουν οριστεί σε «1» θα παραμείνει έτσι.
Έχουμε  δημιουργήσει ένα bitmask στα δεξιά του τελεστή |= .  Όπως μιλήσαμε για αυτό στην ενότητα Περιλαμβανόμενα, τα CS10 και CS11 ορίζονται στο io.h. Όμως κοιτάζοντας στον καταχωριτή TCCR1B μπορούμε να δούμε ότι το CS10 είναι το ψηφίο 0 και το CS11 το ψηφίο 1. Εάν λύναμε το πρόβλημα μαθηματικά σε μάκρος  θα έμοιαζε με αυτό:
1<<CS11 | 1<<CS10;
1<<1 | 1<<0;
0b00000010 | 0b00000001;
0b00000011;
Αυτή είναι η μέθοδος που χρησιμοποιείται για τη ρύθμιση κάθε ψηφίου για οποιοδήποτε σκοπό. Είναι πραγματικά τόσο απλό. Κατασκευάστε ένα bitmask και εφαρμόστε το σε ένα καταχωριτή ή μια μεταβλητή. Απλά να θυμηθείτε να είναι προσεκτικοί σχετικά με τη διατήρηση δεδομένων που μπορεί να είναι ήδη αποθηκευμένο σε ένα καταχωριτή ή σε μια τιμή, κατά τη διάρκεια της ανάθεσης με τη χρήση του τελεστή OR .
Τώρα που έχουμε μια διαιρεμένη πηγή χρόνου για τον μετρητή, και για να παρακολουθούμε, ως στόχο τον αριθμό των 15.625 κύκλων χάρη στα παραπάνω μαθηματικά. Μπορούμε να χρησιμοποιήσουμε μία από τις λειτουργίες του χρονιστή Timer1, την Clear Timer στο Compare Match (CTC), για να προκαλέσει μια διακοπή ακριβώς εκείνη την στιγμή του κύκλου. Ρίξτε μια ματιά στη σελίδα 121 του φύλλου δεδομένων και θα δείτε που πρέπει να θέσουμε την OCR1A στην την τιμή στόχο μας. Θα την θέσετε σε 15.624, ένα λιγότερο κύκλο από αυτό που θα μετρά, γιατί τα χρονόμετρα των μικροελεγκτών αρχίζουν να μετρούν με τον αριθμό μηδέν, και όχι από ένα. Αυτή τη φορά θα χρησιμοποιήσουμε το σύμβολο της ισότητας, γιατί δεν υπάρχουν άλλες τιμές που είναι αποθηκευμένες σε αυτόν τον καταχωριτή:
1 |  OCR1A = 15624;
Θα πρέπει επίσης να ρυθμίσετε τη λειτουργία χρονομέτρου που θέλετε να χρησιμοποιήσετε. Ο Πίνακας 15-4 στη σελίδα 133 έχει πολλές πληροφορίες για αυτό. Όπως συζητήθηκε προηγουμένως, θέλουμε να χρησιμοποιήσουμε τη λειτουργία CTC, ώστε να περιορίζει τις επιλογές μου σε αυτόν τον πίνακα ακριβώς σε δύο. Μπορούμε να επιλέξουμε μεταξύ αυτών, γιατί ξέρουμε ότι εμείς ορίσαμε την τιμή της OCR1A ως το μεγαλύτερο αριθμό που το χρονόμετρο θα μετρά, ή TOP.Το διάγραμμα μας λέει να ρυθμίσουμε το ψηφίο WGM12 στον καταχωριτή TCCR1B σε 1.:
TCCR1B |= 1<<WGM12;
Απλό έτσι δεν είναι; Κάντε το μερικές φορές και αυτό θα γίνει. Υπάρχει μεγάλη λειτουργικότητα με τα χρονόμετρα σε αυτά τα τσιπς και το μπέρδεμα στις ρυθμίσεις του καταχωριτή είναι το τίμημα που καταβάλλετε για την ευελιξία. Αλλά τώρα είμαστε έτοιμοι να προχωρήσουμε σε διακοπές του ενός δευτερολέπτου.
Εγκαινίαση (Initializing) των ακίδων Εισόδου / Εξόδου (Input/Output)
Όταν ένα τσιπ AVR επανεκκινήται, όλες οι ακίδες τοποθετούνται σε λειτουργία Tri-State. Κατά την έναρξη του προγράμματος, κάθε ακίδα εισόδου και εξόδου πρέπει να τεθεί στην επιθυμητή λειτουργία. Ξεκινώντας στη σελίδα 73 του φύλλου δεδομένων μπορείτε να διαβάσετε σχετικά με τη χρήση ακίδων ως γενικές είσοδοι και έξοδοι. Υπάρχουν τρεις καταχωριτές για κάθε ακίδα που θα έχουμε και ορίζονται ως: Data Direction Register (DDR), Port Register (PORT) και Pin Register (PIN). Σε κάθε έναν από αυτούς θα μπαίνει σαν επίθεμα ένα γράμμα που αντιστοιχεί στο σύνολο ακίδων που εργαζόμαστε. Έχουμε συνδέσει το LED στη Θύρα – Πόρτα (Port) D γι’αυτό πρέπει να εργαστούμε με DDRD, PORTD, και αν είχαμε την Πόρτα για χρήση εισόδου, με τον PIN.
1 //Setup the I/O for the LED
2
DDRD |= (1<<0); //Set PortD Pin0 as an output
4 PORTD |= (1<<0); //Set PortD Pin0 high to turn on LED
Ο παραπάνω κώδικας χρησιμοποιείται για να λειτουργήσει ένα LED. Ρυθμίζοντας ένα ψηφίο στην DDRD σε «1», θα κάνετε την αντίστοιχη ακίδα έξοδο. Αν τεθεί σε μηδέν, θα την κάνει είσοδο. Εδώ δημιουργούμε μια έξοδο για να οδηγήσουμε ένα LED. Οι έξοδοι μπορούν να ενεργοποιούνται ή να απενεργοποιούνται με τον καθορισμό ενός 1 ή 0 στον αντίστοιχο καταχωριτή PORT. Έτσι παραπάνω,  έχουμε χρησιμοποιήσει τον PORTD για να ενεργοποιήσουμε το ψηφίο 0 που αντιστοιχεί στην ακίδα που είναι συνδεδεμένο το LED.
Αν χρησιμοποιούσαμε μία ακίδα σαν είσοδο ο καταχωριτής PORT θα έπρεπε να χρησιμοποιηθεί για να ενεργοποιήσει ή να απενεργοποιήσει μια εσωτερική pull-up αντίσταση και ο καταχωριστής PIN θα χρησιμοποιηθεί για να μετρήσει τη λογική τιμή στην εν λόγω ακίδα. Ο πίνακας 13-1 στη σελίδα 74 παρουσιάζει τις διάφορες καταστάσεις των ακίδων I/O, αλλά θα καλυφθούν περισσότερο στο τμήμα 4 της παρούσας σειράς.
Ο Βρόχος (Loop)
Τα ενσωματωμένα προγράμματα πρέπει να έχουν ένα ατελείωτο βρόχο που να αποτρέπει το πρόγραμμα από ένα τέλος και μία έξοδο. Κι αυτό γιατί αν το πρόγραμμα μας βγει, το τσιπ θα κάτσε εκεί και δεν θα κάνει τίποτα (μετά από όλα, δεν θα τρέχει κανένα πρόγραμμα). Σε αυτή την περίπτωση δεν χρειάζεται ο βρόχος να κάνει τίποτα από τότε έχουμε θέσει το υλικό και χρησιμοποιούμε μια διακοπή για να αποσοβήσουμε το LED
(1) { } // Βρόχος για πάντα, οι διακόπες κάνουν τα υπόλοιπα
Θα προσθέσουμε λειτουργικότητα στο βρόχο στο μέρος 4 της σειράς, αλλά για τώρα ο βρόχος 'while(1)' μόλις παγίδεψε το πρόγραμμα και δεν κάνει τίποτα άλλο.
Χειρισμός της διακοπής
Τα πάντα τώρα είναι ρυθμισμένα και είναι έτοιμα να ξεκινήσουν, αλλά τίποτα δεν θα συμβεί αν δεν γράψουμε κώδικα που να κάνει κάτι όταν συμβεί μία διακοπή. Αυτό ονομάζεται Ρουτίνα Εξυπηρέτησης Διακοπής Interrupt Service Routine (ISR). Ο υπόλοιπος κώδικα σταματάει και εκτελείται αυτή η ρουτίνα. Αυτό είναι καλύτερο να διαρκέσει όσο το δυνατόν λιγότερο, που είναι εύκολο εδώ επειδή το μόνο που χρειάζεται είναι να αντιστρέψει το LED:
1 |ISR(TIMER1_COMPA_vect) //Interrupt Service Routine Διακοπή Υπηρεσία ρουτίνας
2 |{
3 |PORTD ^= (1<<0); //Χρήση της XOR για εναλλαγή των LED
4 |}
Αν κοιτάξετε στη σελίδα 62 του φύλλου δεδομένων μπορείτε να δείτε ότι η πηγή διακοπής είναι ο Timer/Counter1 Compare A που ονομάζεται «TIMER1  COMPA«. Το παίρνουμε αυτό και το χρησιμοποιούμε ως μεταβλητή εισόδου για την ISR, αντικαθιστώντας τα κενά με παύλες και προσθέτοντας με μικρά «vect» στο τέλος. Αυτό είναι για να γνωρίζει ο compiler ποιο ISR ανήκει σε διαφορετικές πηγές διακοπής . Όσον αφορά το ίδιο το LED, έχουμε χρησιμοποιήσει το τελεστή της πράξης XOR και μίας μασκας. Η μάσκα εγγυάται ότι μόνο το bit 0 θα αλλάξει.

Μεταγλώττιζοντας Κώδικα – Compiling Code

Πριν αφήσουμε αυτό το τμήμα της εκπαιδευτικής σειράς θα πρέπει να κάνετε μια δοκιμαστική μεταγλώττιστη. Ο compiler παίρνει τον κωδικά μας σε C και τον μετατρέπει σε ένα αρχείο που μπορεί να γραφτεί στον μικροελεγκτή. Τα περάσματα ενός μεταγλωττιστή κάνει ένα κούρεμα στα ψηφία και αυτό δεν είναι η κατάλληλη στιγμή για να το εξηγήσουμε με λεπτομέρειες.
Αλλά, καθώς θα μαθαίνετε να γράφετε ενσωματωμένο κώδικα θα πρέπει να κάνετε μια προσπάθεια να μάθετε επίσης πώς αυτός ο κώδικας θα πρέπει να ερμηνεύεται από τον compiler. Με αυτόν τον τρόπο θα αποτρέψετε πολλούς πονοκέφαλους που προκαλούνται από τη βελτιστοποίηση (ο compiler προσπαθεί να εξορθολογισει τον φουσκωμένο κώδικας  σε C) και θα σας επιτρέψει να κάνετε, τόσο περισότερο λειτουργικό το μεγαλύτερο μέρος του υλικού σας, όσο και να καταλαμβανει το πρόγραμμα μικρότερο χώρο.
Αλλά για τώρα υπάρχει ένα αρχείο που κάνει τα παραπάνω και περιλαμβάνεται στον παράδειγμα από το Τμήμα 2 . Αν δεν το έχετε ήδη κάνει, αποσυμπιέστε το πακέτο και περιηγηθείτε στο κατάλογο ‘src’. Υπάρχουν δύο αρχεία σε αυτόν τον κατάλογο, τα main.c και το Makefile. Ένα Makefile είναι ένας τρόπος για να αυτοματοποιήσετε τη διαδικασία μεταλγώττισης. Αυτό και μόνο μεταγλωττίζει, συνδέει, και προγράμματίζει ενα αρχείο με κώδικα C. Αν κοιτάξετε το makefile θα παρατηρήσετε ότι υπάρχουν πολλές ρυθμίσεις χρήστη κοντά στην κορυφή. Θα πρέπει να ρυθμίσετε το μικροεπεξεργαστή για τον οποίο έχετε γράψει κώδικα, το όνομα του αρχείου πηγαίου κώδικα που έχετε γράψει (TARGET = main), ο προγραμματιστής που χρησιμοποιείτε (από τον κατάλογο AVRdude, συζητείται στο Mέρος 2), και την θύρα για τον προγραμματιστή.
Αν πληκτρολογήσετε «make» θα πρέπει να είστε σε θέση να μεταγλωττίσετε το πρόγραμμα παράδειγμα. Αν δεν έχετε έναν προγραμματιστή AVR Dragon και τρέχετε Linux θα πάρετε ένα λάθος όταν προσπαθείσετε να προγραμματίσετε το τσιπ, αλλά θα πρέπει να μεταγλωττίσετε τον κώδικα με επιπλέον αρχεία εξόδου:

 

$  ls -la
total 84
drwxr-xr-x 2 mike mike  4096 2010-11-04 14:20 .
drwxr-xr-x 3 mike mike  4096 2010-11-01 14:55 ..
-rw-r--r-- 1 mike mike   894 2010-10-24 12:34 main.c
-rw-r--r-- 1 mike mike    23 2010-11-04 14:20 main.d
-rw-r--r-- 1 mike mike    13 2010-11-04 14:20 main.eep
-rwxr-xr-x 1 mike mike  7121 2010-11-04 14:20 main.elf
-rw-r--r-- 1 mike mike   750 2010-11-04 14:20 main.hex
-rw-r--r-- 1 mike mike  5224 2010-11-04 14:20 main.lss
-rw-r--r-- 1 mike mike  5171 2010-11-04 14:20 main.lst
-rw-r--r-- 1 mike mike 14464 2010-11-04 14:20 main.map
-rw-r--r-- 1 mike mike  3972 2010-11-04 14:20 main.o
-rw-r--r-- 1 mike mike  1454 2010-11-04 14:20 main.sym
-rw-r--r-- 1 mike mike 10235 2010-10-24 10:44 makefile

‘main.hex’ is the file that you can program onto the microcontroller. This makefile is extremely versatile. You can also see that it output ‘main.eep’ which can be used to program the EEPROM on the chip if your code includes default data stored in the EEPROM. It can also be altered to output an assembler file, or binaries in different formats.

Το «Main.hex» είναι το αρχείο που μπορείτε να προγραμματίσετε επάνω στο μικροελεγκτή. Αυτό το makefile είναι εξαιρετικά ευέλικτο. Μπορείτε επίσης να δείτε ότι το «main.eep» που παράγεται μπορεί να χρησιμοποιηθεί για το πρόγραμμα της EEPROM στο τσιπ, αν στον κωδικό σας περιλαμβάνονται προεπιλεγμένα δεδομένα που αποθηκεύονται στο EEPROM. Μπορεί επίσης να τροποποιηθει με την απόδοση ενός αρχείου assembler, ή δυαδικά σε διαφορετικές μορφές.

If you’re compiler didn’t spit out this information, there’s something wrong with your toolchain. Use your friend Google to search for any error messages and see if you can’t get things fixed up. Another great exercise would be to modify this file to work with your programmer. If you managed to get AVRdude working in Part 2 of this series, this alteration is as simple as changing the makefile to use those same settings.

Εάν ο  μεταγλωττίστης δεν δώσει αυτές τις πληροφορίες, υπάρχει κάτι λάθος με τις παραμέτρους σας. Χρησιμοποιήστε τον φίλο σας Google  για να ψάξετε για τυχόν μηνύματα λάθους και να δείτε εάν μπορείτε να φτιάξετε τα πράγματα. Μια άλλη μεγάλη άσκηση θα ήταν να τροποποιήσετε αυτό το αρχείο για να συνεργαστεί με τον προγραμματιστή σας. Αν τα καταφέρετε να πάρετε το AVRdude που δουλέψατε στο Μέρος 2 αυτής της σειράς, αυτή η μεταβολή είναι τόσο απλή όσο την αλλαγή του makefile που χρησιμοποιεί τις ίδιες ρυθμίσεις.

Conclusion – Συμπέρασμα

That’s it for now. In the next installment of this series I’ll be talking about fuse bits, writing our own code, and I’ll try to touch on many of the different peripheral features of this chip. I’m plan to augment the original circuit with a few more LEDs (so make sure you have at least 8 of them and their matching resistors) along with adding a button for input. Thanks for reading!

Αυτό είναι για τώρα. Κατά την επόμενη δόση της σειράς αυτής θα είμαι μιλάμε για bits ασφάλειες, γράφοντας τον δικό μας κώδικα, και θα προσπαθήσουμε να αναφερθούμε σε πολλά από τα διαφορετικά περιφερειακά χαρακτηριστικά αυτού του τσιπ. Το αρχικό κύκλωμα θα αυξήσει με λίγα περισσότερα LEDs (οπότε φροντίστε να έχετε τουλάχιστον 8 από αυτά και ασορτί αντιστάσεις ) μαζί με την προσθήκη ενός κουμπιού για την είσοδο. Ευχαριστώ για την ανάγνωση!

Πόροι

Αφήστε μια απάντηση