5. Εισαγωγή στον προγραμματισμό AVR σε γλώσσα assembly

Η CPU ενός μικροελεγκτή δέχεται εντολές και δεδομένα σε δυαδική μορφή δηλαδή αριθμούς αποτελούμενοι από 0 και 1. Το σύνολο των εντολών που γράφουμε εκφρασμένο σε δυαδικό ή δεκαεξαδικό κώδικα ονομάζεται γλώσσα μηχανής (machine code). Για να διευκολυνθούμε στην ανάπτυξη κώδικα μηχανής χρησιμοποιούμε τον assembler, ο οποίος είναι μια εφαρμογή που δέχεται μνημονικά ονόματα για τις εντολές χαμηλού επιπέδου (low – level language) και τις μετατρέπει σε κώδικά μηχανής.

Ένας assembler είναι σύμφωνος με τη λειτουργία της CPU και παράγει κώδικα συμβατό με τον κώδικα μηχανής.  Όμως το γράψιμο κώδικα στη γλώσσα assembly (όπως και στη γλώσσα μηχανής) θέλει μεγάλη προσπάθεια και προκαλεί μεγάλες δυσκολίες.

Για να γράφουμε εύκολα κώδικα αναπτύχθηκαν γλώσσες υψηλού επιπέδου (high – level language) στις οποίες ο προγραμματιστής δεν χρειάζεται να γράφει κώδικα βασισμένο στην εσωτερική δομή και λειτουργία της CPU. Ένας προγραμματιστής χρησιμοποιεί μια εφαρμογή ηλεκτρονικού υπολογιστή για να μετατρέπει τον κώδικα υψηλού επιπέδου σε γλώσσα μηχανής. Αυτή η εφαρμογή λέγεται compiler. Για την ανάπτυξη κώδικα έχουν δημιουργηθεί διάφοροι compilers, όπου σε αυτή τη σειρά μαθημάτων θα αναφερθούμε στον C compiler, ο οποίος περιέχεται στο πακέτο λογισμικού AVR Studio IDE

Ένας κώδικας σε γλώσσα assembly αποτελείται μεταξύ άλλων από σειρές εντολών assembly. Μια εντολή στη γλώσσα assembly αποτελείται από το μνημονικό  και προαιρετικά ακολουθούμενο από ένα ή δυο τελεστέους. Τα μνημονικά είναι οι εντολές προς την CPU ενώ οι τελεστέοι είναι τα δεδομένα που θα διαχειριστούν. Επίσης ένας κώδικας assembly περιέχει directives που δίνουν κατευθύνσεις στον assembler, όπως οι .ORG και .EQU

H directive .ORG προκαλεί τον assembler να ξεκινήσει να τοποθετεί στην μνήμη τον κώδικα σε συγκεκριμένη θέση της μνήμης ROM, ενώ η .EQU θέτει ένα όνομα σε μια σταθερά. Ένα παράδειγμα κώδικα assembly δίνεται παρακάτω:

           ; AVR Assembly Language Program To Add Some Data.
           ; store SUM in SRAM location 0x300
           .EQU  SUM = 0x300     ;SRAM loc $300 for SUM
           .ORG 00               ;start at address 0
           LDI R16, 0x25         ;R16 = 0x25
           LDI R17, $34          ;R17 = 0x34
           LDI R18, 0b00110001   ;R18 = 0x31
           ADD R16, R17          ;add R17 to R16
           ADD R16, R18          ;add R18 to R16
           LDI R17, 11           ;R17 = 0x0B
           ADD R16, R17          ;add R17 to R16
           STS SUM, R16          ;save the SUM in loc $300
HERE:      JMP HERE              ;stay here forever

        

Ο κώδικας ξεκινά με δυο directives. H .EQU που δίνει την τιμή 0x300 στο όνομα SUM και την .ORG 00 που ξεκινά να γράφει τον κώδικα μηχανής του προγράμματος μας από την θέση 00 της μνήμης ROM. Έπειτα ακολουθούν οι γραμμές εντολών όπου κάθε γραμμή αποτελείται από το μνημονικό και τους τελεστέους που δρα η εντολή.

Μπορούμε να γράφουμε σχόλια μιας γραμμής εφόσον τοποθετήσουμε τον χαρακτήρα του ελληνικού ερωτηματικού μπροστά στην γραμμή σχολίων. Προσέξτε την ετικέτα “HERE” ακολουθούμενη από τον χαρακτήρα “:” στην τελευταία γραμμή. Εδώ χρησιμοποιείται από την εντολή “JMP” δημιουργώντας ένα ατέρμονο βρόγχο.

Επιλέγοντας ευανάγνωστες και ξεκάθαρες ετικέτες και ονόματα μπορείς να κάνεις τον κώδικα που γράφεις ξεκάθαρο και εύκολο να συντηρηθεί. Υπάρχουν αρκετοί κανόνες που πρέπει να ακολουθήσεις. Πρώτον το όνομα της ετικέτας να είναι μοναδικό. Τα ονόματα που χρησιμοποιούνται για ετικέτες θα πρέπει να αποτελούνται από αλφαβητικούς χαρακτήρες μικρά ή κεφαλαία, τα ψηφία 0 έως 9 και τους χαρακτήρες (?), (.), (@), (_), ($). Ο πρώτος χαρακτήρας του ονόματος ετικέτας πρέπει να είναι αλφαβητικός και δεν πρέπει να είναι αριθμός. Το όνομα μιας ετικέτας δεν πρέπει να είναι δεσμευμένο όνομα που χρησιμοποιεί ο assembler όπως τα μνημονικά ονόματα εντολών όπως π.χ. το “LDI” ή “ADD”.

Ο σημαντικότερος καταχωρητής σε ένα AVR μικροελεγκτή είναι ο PC (program counter). O program counter χρησιμοποιείται από τη CPU για να δείξει την διεύθυνση της επόμενης εντολής που θα εκτελεστεί. Καθώς η CPU αντλεί τον κώδικα της εντολής από την μνήμη προγράμματος ROM o program counter αυξάνεται αυτόματα για να δείξει στην επόμενη εντολή. Όσα σε περισσότερα bits σε πλάτος είναι ο program counter τόσο σε μεγαλύτερο πλάτος μνήμης ROM μπορεί να προσπελαύνει. Για παράδειγμα ένας 14-bit program counter μπορεί να προσπελαύνει το μέγιστο 16Κ  (214 = 16Κ) μέγεθος μνήμης.

Σε ένα AVR μικροελεγκτή κάθε θέση μνήμης Flash έχει πλάτος 2bytes. Για παράδειγμα σε ένα ATmega328 του οποίου η μνήμη Flash είναι 32Κbytes η μνήμη Flash οργανώνεται σαν 16Κ Χ 16bits και ο program counter είναι 14bits στο πλάτος (214 = 16Κ θέσεις μνήμης) Ο ATmega64 έχει ένα 15-bit program counter οπότε η Flash έχει 32Κ θέσεις (215 = 32 Κ) στις οποίες κάθε θέση περιέχει 2bytes (32Κ Χ 2bytes = 64Kbytes). Στην περίπτωση ενός 16bit program counter  το μέγιστο της περιοχής κώδικα είναι 64Κ  (216 = 64Κ) η οποία καταλαμβάνει την περιοχή διευθύνσεων 0000 – $FFFF.

Θα πρέπει να σημειώσουμε ότι η πρώτη θέση στην μνήμη ROM σε ένα AVR μικροελεγκτή έχει διεύθυνση 000000 και η τελευταία διεύθυνση της μνήμης κώδικα ROM δεν είναι ίδια για όλα τα μέλη της οικογένειας που ορίζεται από το μέγεθος της μνήμης ROM που περικλείει ο μικροελεγκτής. Το μέλος ATmega8 έχει 8Κ πάνω στο τσιπ μνήμης ROM. Αυτή η 8Κ μνήμη ROM οργανώνεται σαν 4Κ Χ 2 bytes και έχει διευθύνσεις από 00000 έως $00FFF. Επομένως η πρώτη θέση της  μνήμης Flash έχει διεύθυνση 00000 και η τελευταία θέση έχει διεύθυνση $00FFF.

Ένα ερώτημα που πρέπει να απαντήσουμε για ένα μικροελεγκτή είναι το εξής: Από ποια διεύθυνση ξεκινά να εκτελεί εντολές μόλις τροφοδοτηθεί με τάση (δηλαδή να εφαρμόσουμε τάση VCC στο πιν RESET). Ό AVR στην αρχή που τροφοδοτηθεί με τάση ο PC (program counter)  έχει την τιμή 00000. Αυτό σημαίνει ότι η πρώτη εντολή που θα εκτελέσει βρίσκεται στην θέση με διεύθυνση 00000. Για αυτό το λόγο σε ένα AVR σύστημα η πρώτη εντολή πρέπει να γραφεί στην διεύθυνση 00000 της ROM διότι από εκεί ξεκινά η εκτέλεση των εντολών. Στον AVR assembler, αυτό το επιτυγχάνουμε με την έκφραση .ORG στον πηγαίο κώδικα όπως έχουμε αναφέρει σε προηγούμενη ενότητα.