Καταχωρητές GPR και Ι/Ο μνήμη

Η CPU ενός μικροελεγκτή χρησιμοποιεί κάμποσους καταχωρητές για την προσωρινή αποθήκευση δεδομένων τα οποία χρησιμοποιούνται για αριθμητικές καθώς και για λογικές πράξεις.  Σε αυτή την ενότητα θα εξετάσουμε μεταξύ άλλων τους καταχωρητές γενικού σκοπού GPRs του μικροελεγκτή AVR και θα επιδείξουμε τη χρήση των GPRs με τις απλές εντολές LDI και ADD.

Ένας τυπικός μικροελεγκτής AVR  έχει 32 καταχωρητές γενικού σκοπού GPRs που ονομάζονται R0 έως R31. Οι καταχωρητές GPRs ενός μικροελεγκτή AVR έχουν πλάτος 8-bit. Τα 8-bits ενός καταχωρητή γενικού σκοπού δείχνονται στο ακόλουθο διάγραμμα:

Εδώ φαίνεται η περιοχή από το MSB(most – significant – bit) από την ποιο αριστερή θέση στην οποία βρίσκεται το bit D7 έως στο LSD(least – significant – bit) στην ποιο δεξιά θέση στην οποία βρίσκεται το bit D0.

Οι 32 καταχωρητές γενικού σκοπού ενός μικροελεγκτή AVR βρίσκονται στη χαμηλότερη θέση της μνήμης δεδομένων και μπορούν να χρησιμοποιηθούν από όλες τις αριθμητικές και λογικές εντολές.

Η εντολή LDI

Η εντολή LDI αντιγράφει 8-bit κυριολεκτικές τιμές σε ένα καταχωρητή γενικού σκοπού (GPR)

   LDI  Rd, K   ;  φορτώνει στον καταχωρητή Rd την κυριολεκτική αριθμητική τιμή Κ
                ;  d  πρέπει να είναι μεταξύ 16 και 31

Σημείωση: Κ είναι μια 8-bit σταθερή τιμή που μπορεί να είναι δεκαδικός  0 – 255 ή δεκαεξαδικός  0x00 – 0xFF  ενώ ο Rd είναι ένας καταχωρητής GPR από R16 έως R31 (ένας από την μεγαλύτερη ομάδα καταχωρητών)

Παραδείγματα:

  LDI  R21, 0x30  ;  load  R21  with  0x30  (R21 = 0x30)
  LDI  R28, 0x85  ;  load  0x85 into  R28  (R28 = 0x85)

Σημείωση: Δεν μπορούμε να φορτώσουμε τιμές σε καταχωρητές από R0 έως R15 χρησιμοποιώντας την εντολή LDI. Για παράδειγμα η ακόλουθη εντολή δεν είναι έγκυρη:

LDI R5,  0x68  ;   invalid instruction

Παρατηρήστε στην εντολή LDI τη θέση του καταχωρητή αναχώρησης και εκείνου της άφιξης. Όπως μπορείτε να δείτε η εντολή LDI φορτώνει τον δεξιό τελεστή στον αριστερό τελεστή.

Σημείωση: Για να γράψουμε σχόλια στη γλώσσα Assembly χρησιμοποιούμε τον χαρακτήρα του ελληνικού ερωτηματικού «;». Συγκεκριμένα το υπόλοιπο της γραμμής μετά τον χαρακτήρα του ερωτηματικού αγνοείται. Χρησιμοποιείται για να περιγράψουμε την λειτουργία του κώδικα έτσι ώστε αργότερα ο κώδικας να μπορεί να γίνει κατανοητός.

Σημείωση:  Όταν θέλουμε να εισάγουμε ένα δεκαεξαδικό αριθμό τοποθετούμε το σύμβολο του δολαρίου $ ή το 0x μπροστά στην τιμή. Εάν δεν τοποθετήσουμε τίποτα σημαίνει ότι έχουμε δεκαδική τιμή. Για το σύμβολο 0b μπροστά από μια τιμή που είναι σύνολο από 0 και 1 δείχνει δυαδικό αριθμό.

Η εντολή ADD

Η εντολή ADD έχει την ακόλουθη μορφή:

ADD Rd, Rr; πρόσθεσε τα περιεχόμενα του Rr σε εκείνα του Rd και φόρτωσε το αποτέλεσμα πίσω στον Rd

Παράδειγμα:

LDI  R19,  0x65     ;      load  0x65   into    R19
LDI  R20,  0x44     ;      load  0x44   into    R20
ADD  R19,  R20      ;       add value  R20 to  R19  (R19 = R19 + R20)

AVR Data Memory

Σε ένα μικροελεγκτή AVR υπάρχουν δυο τύποι μνήμης: Η μνήμη κώδικα (code memory space) και η μνήμη δεδομένων (data memory space). Ο κώδικας που γράφουμε αποθηκεύεται στην μνήμη κώδικα ενώ τα δεδομένα στην μνήμη δεδομένων. Η μνήμη δεδομένων αποτελείται από τρία είδη μνήμης: GPRs (καταχωρητές γενικού σκοπού), τη μνήμη Ι/Ο και την εσωτερική μνήμη δεδομένων  RAM. Βλέπε το ακόλουθο σχήμα:

GPRs (καταχωρητές γενικού σκοπού)
Όπως αναφέραμε σε προηγούμενη ενότητα, οι GPRs χρησιμοποιούν τα πρώτα 32 bytes της μνήμης δεδομένων. Αυτοί οι καταχωρητές παίρνουν διευθύνσεις $00 έως $1F της μνήμης δεδομένων ανεξάρτητα από τον AVR μικροελεγκτή που έχουμε.

Μνήμη Ι/Ο  (SFRs)
Η μνήμη Ι/Ο δίνει βάση για ειδικές λειτουργίες, όπως τον καταχωρητή κατάστασης, τους χρονιστές, τη σειριακή επικοινωνία, τις Ι/Ο θύρες, τον ΑDC κ.ά. Η λειτουργικότητα κάθε μιας Ι/Ο θέσης μνήμης είναι προκαθορισμένη από τον σχεδιαστή της CPU και χρησιμοποιείται για τον έλεγχο του μικροελεγκτή και των περιφερειακών. Η ΑVR  I/O μνήμη αποτελείται από 8-bit καταχωρητές.

Όλοι οι μικροελεγκτές AVR έχουν το λιγότερο 64 bytes μέγεθος Ι/Ο θέσης μνήμης. Αυτό το 64-byte τμήμα ονομάζεται standard I/O μνήμη. Για AVRs με περισσότερα χαρακτηριστικά, υπάρχει επίσης η extended I/O μνήμη το οποίο περιέχει τους καταχωρητές για τον έλεγχο των έξτρα θυρών και περιφερειακών. Σε άλλους μικροελεγκτές οι καταχωρητές Ι/Ο ονομάζονται SFRs (special function registers) στους οποίους ο κάθε ένας είναι υπεύθυνος για μια λειτουργία σε αντίθεση με τους GPRs οι οποίοι δεν έχουν ειδική λειτουργία και προορίζονται για αποθήκευση προσωρινών δεδομένων.

Εσωτερική μνήμη δεδομένων SRAM
Η εσωτερική μνήμη δεδομένων χρησιμοποιείται για την αποθήκευση δεδομένων και παραμέτρων από τους AVR προγραμματιστές και  C compilers. Κάθε θέση της SRAM μπορεί να προσπελαθεί απευθείας με την διεύθυνση της. Κάθε θέση έχει πλάτος 8-bit και μπορεί να χρησιμοποιηθεί για την αποθήκευση δεδομένων που θέλουμε, εφόσον έχουν πλάτος 8-bits. Το μέγεθος της εσωτερικής μνήμης SRAM μεταβάλλεται από τσιπ σε τσιπ.

Η μνήμη EEPROM στους μικροελεγκτές AVR
Σήμερα οι περισσότεροι  μικροελεγκτές έχουν EEPROM μνήμη που χρησιμοποιείται για μόνιμη αποθήκευση δεδομένων. Όπως γνωρίζουμε η μνήμη EEPROM δεν χάνει δεδομένα με την διακοπή της τροφοδοσίας της, ενώ η μνήμη SRAM τα χάνει. Η μνήμη EEPROM χρησιμοποιείται για την αποθήκευση δεδομένων και παραμέτρων που δεν αλλάζουν συχνά και δεν θέλουμε να χαθούν σε μια διακοπή της τροφοδοσίας.

Χρησιμοποιώντας εντολές πάνω στη μνήμη δεδομένων

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

Η εντολή LDS (Load direct from data Space)

Η εντολή LDS έχει την ακόλουθη σύνταξη:

  LDS Rd, K  ;  φόρτωση του καταχωρητή  Rd με τα περιεχόμενα της θέσης Κ  (0 < d <31)
             ;  K είναι μια διεύθυνση μεταξύ  $0000  έως   $FFFF

Η εντολή LDS προκαλεί τη CPU να φορτώσει ένα byte από μια διεύθυνση της μνήμης δεδομένων σε ένα καταχωρητή GPR. Μετά που αυτή η εντολή εκτελεστεί ο καταχωρητής GPR θα έχει την ίδια τιμή με εκείνη της διεύθυνσης Κ της μνήμης δεδομένων. Η θέση στη μνήμη δεδομένων μπορεί να είναι οποιαδήποτε μέρος της μνήμης δεδομένων. Μπορεί να είναι ένας Ι/Ο καταχωρητής, μια θέση στην εσωτερική μνήμη RAM ή ένας καταχωρητής γενικού σκοπού (GPR)

Παράδειγμα

Στην επόμενη εντολή φορτώνονται στον καταχωρητή R8 τα περιεχόμενα της θέσης μνήμης δεδομένων με διεύθυνση 0x310
      LDS  R8,  0x310  ;   load R8 with the contents of location $310

Η εντολή  STS  (Store direct to data  Space)

Η εντολή STS έχει την ακόλουθη σύνταξη: 

STS  K,  Rr  ;  αποθήκευσε τα περιεχόμενα του καταχωρητή Rr  στη θέση Κ της μνήμης δεδομένων
             ;  Κ  είναι μια διεύθυνση μεταξύ  $0000 έως   $FFFF

Η εντολή  STS  προκαλεί  τη CPU να αποθηκεύσει τα περιεχόμενα του καταχωρητή GPR σε μια θέση της μνήμης δεδομένων. Μετά την εκτέλεση αυτής της εντολής η θέση στην μνήμη δεδομένων θα έχει την ίδια τιμή με εκείνη του καταχωρητή GPR. Η θέση μπορεί να είναι η διεύθυνση οποιοδήποτε μέρους της μνήμης δεδομένων, όπως ένας Ι/Ο καταχωρητής, μια θέση στην εσωτερική μνήμη RAM ή ένας καταχωρητής GPR.

Παράδειγμα

Με την εντολή  STS  0x01,  R12  αντιγράφονται τα δεδομένα του καταχωρητή R12 στη θέση 1 της μνήμης δεδομένων. Όπως μπορείς να δεις η θέση 1 της μνήμης δεδομένων αντιστοιχεί στο κομμάτι της GPRs και είναι η διεύθυνση του καταχωρητή R1. Αυτή η εντολή αντιγράφει τον καταχωρητή R12 στον R1.

Παράδειγμα

Στην επόμενη εντολή αποθηκεύονται τα περιεχόμενα του καταχωρητή R24 στη θέση 0x253 της μνήμης δεδομένων που ανήκει στην εσωτερική μνήμη RAM
STS  0x253, R24  ;   store  R24  to data space location  0x253

Η εντολή ΙΝ (IN  from  I/O θέση)

Η εντολή ΙΝ έχει την ακόλουθη σύνταξη:

ΙΝ  Rd,  A  ;  φόρτωσε μια Ι/Ο θέση μνήμης σε ένα καταχωρητή GPR  (0≤d≤31)  ,  (0≤A≤63)

Η εντολή ΙΝ προκαλεί τη CPU να φορτώσει ένα byte από ένα Ι/Ο καταχωρητή σε ένα GPR. Μετά που αυτή η εντολή εκτελεστεί ο καταχωρητής GPR θα έχει την ίδια τιμή όπως ο καταχωρητής Ι/Ο. Για παράδειγμα η εντολή  ΙΝ  R18,  0x15  θα αντιγράψει τα περιεχόμενα της θέσης με διεύθυνση 15 (σε hex) της Ι/Ο θέσης μνήμης στον καταχωρητή R18. Όπως μπορείς να δεις στο παρακάτω σχήμα, η Ι/Ο μνήμη έχει δυο διευθύνσεις την Ι/Ο διεύθυνση και την διεύθυνση της μνήμης δεδομένων.

Κάθε θέση στη μνήμη δεδομένων έχει μοναδική διεύθυνση που ονομάζεται data memory address. Κάθε Ι/Ο καταχωρητής έχει σχετική διεύθυνση με την αρχή της Ι/Ο μνήμης, που ονομάζεται Ι/Ο διεύθυνση.

Στην εντολή ΙΝ, ο Ι/Ο καταχωρητής αναφέρεται προς την Ι/Ο διεύθυνση. Για παράδειγμα η εντολή  ΙΝ R18,  0x06  αντιγράφει τα περιεχόμενα της θέσης $06 της Ι/Ο μνήμης (της οποίας η διεύθυνση της μνήμης δεδομένων είναι 0x26) στον καταχωρητή  R18. Όπως φαίνεται στον πίνακα η Ι/Ο διεύθυνση  0x06 ανήκει στον Ι/Ο καταχωρητή PINC και έτσι η εντολή αυτή αντιγράφει τα περιεχόμενα του PINC της θύρας C στον καταχωρητή R18.

Στην επόμενη εντολή ο καταχωρητής R17 φορτώνεται με τα περιεχόμενα της θέσης 0x03 της Ι/Ο μνήμης.

ΙΝ  R17,  0x03 ;  load R17 with location $3 (R17 = PINB)

Για να δουλέψουμε με τους Ι/Ο καταχωρητές πιο εύκολα χρησιμοποιούμε τα ονόματα τους αντί για τις Ι/Ο διευθύνσεις τους. Για παράδειγμα, η επόμενη εντολή φορτώνει τον καταχωρητή R22 με τα περιεχόμενα PIND

IN R22, PIND  ;  load R22  with  PIND

Όπως αναφέραμε νωρίτερα μπορούμε να χρησιμοποιήσουμε την LDS εντολή για να αντιγράψουμε τα περιεχόμενα μιας θέσης της μνήμης δεδομένων σε ένα καταχωρητή GPR. Αυτό σημαίνει ότι μπορούμε να φορτώσουμε ένα Ι/Ο καταχωρητή σε ένα καταχωρητή γενικού σκοπού GPR χρησιμοποιώντας μια εντολή LDS. Η χρήση της εντολής ΙΝ για το διάβασμα ενός Ι/Ο καταχωρητή προσφέρει πλεονεκτήματα έναντι της εντολής LDS, τα εξής:
1] H CPU εκτελεί την εντολή ΙΝ γρηγορότερα από την LDS. Αυτό οφείλεται στο ότι η εντολή ΙΝ εκτελείται σε ένα κύκλο μηχανής σε αντίθεση με την LDS που εκτελείται σε δυο.
2] Η εντολή ΙΝ είναι μια 2-byte εντολή ενώ η LDS είναι 4-byte εντολή. Δηλαδή η εντολή ΙΝ καταλαμβάνει λιγότερη μνήμη προγράμματος.
3] Όταν χρησιμοποιούμε την εντολή ΙΝ μπορούμε να χρησιμοποιούμε τα ονόματα των Ι/Ο καταχωρητών ενώ στην εντολή LDS όχι.

Η εντολή OUT  ( OUT  to  I/O location)

Η εντολή OUT έχει την ακόλουθη σύνταξη:

OUT  A,  Rr  ;  αποθήκευσε τον καταχωρητή Rr  στη  Ι/Ο θεση Α  (0≤r≤31)  ,  (0≤A≤63)

Η εντολή OUT προκαλεί τη CPU να αποθηκεύσει τον καταχωρητή  GPR  στον  Ι/Ο καταχωρητή. Μετά που η εντολή εκτελεστεί, ο Ο/Ι καταχωρητής θα έχει την ίδια τιμή όπως ο GPR. Για παράδειγμα η εντολή  OUT  PORTB,  R12  αντιγράφει τα περιεχόμενα του R12  στο  PORTB  (θέση  0x05 της Ι/Ο μνήμης).

Το επόμενο κομμάτι κώδικα αντιγράφει την τιμή  0xB6  στον καταχωρητή  SPL
LDI   R17,  0xB6  ;  load R17  with  0xB6
OUT  SPL,  R17    ;  out  R17  to  SPL

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

Το επόμενο κομμάτι κώδικα αντιγράφει τον καταχωρητή PINC στη θύρα PORTD

 Το επόμενο κομμάτι κώδικα αντιγράφει τον καταχωρητή PINC στη θύρα PORTD
 IN   R21,  PINC   ;  load R21 with the contents of I/O reg  PINC
 OUT  PORTD,  R21  ;  out  R21  to  PORTD

Η εντολή  MOV

Η εντολή MOV χρησιμοποιείται για να αντιγράψει δεδομένα μεταξύ καταχωρητών γενικού σκοπού GPR από R0 έως R31

MOV  Rd,  Rr  ;  Rd = Rr  (αντέγραψε τον Rr  στον  Rd)
              ;  Rd και Rr  μπορεί να είναι οποιοδήποτε από τους GPRs

Παράδειγμα:

Η επόμενη εντολή αντιγράφει τα περιεχόμενα του R22 στον R12
MOV  R12,  R22   ;   R12 = R22
Αν ο R22 περιέχει την τιμή 70 μετά την εκτέλεση της παραπάνω εντολής και οι δυο R22 και R12 θα περιέχουν την ίδια τιμή 70.

Παράδειγμα

Το επόμενο κομμάτι κώδικα προσθέτει την τιμή 0x25 στα περιεχόμενα της θέσης 0x230 της μνήμης δεδομένων και αποθηκεύει το αποτέλεσμα στη θέση 0x235:
LDI  R21, 0x25  ;  load R21 with  0x25
LDS R22, 0x230  ;  load R21 with the contents of location on 0x230
ADD R22, R21  ;  R22=R22+R21
STS  0x235, R22   ;  store  R21  to  location  0x235

Η εντολή INC

Η εντολή INC έχει την ακόλουθη σύνταξη:

INC  Rd  ;  αύξησε τα περιεχόμενα του Rd κατά 1

Για παράδειγμα η ακόλουθη εντολή προσθέτει 1 στα περιεχόμενα του R5

 INC  R5  ;  R5 = R5+1
Το ακόλουθο κομμάτι κώδικα αυξάνει τα περιεχόμενα της θέσης 0x450 της μνήμης δεδομένων κατά 1
    LDS  R18, 0x450  ;  R18 = contents of location 0x450
    INC  R18   ;    R18 = R18 + 1
    STS 0x450, R18  ;  store  R18 to location 0x450

Η εντολή SUB

Η εντολή SUB έχει την ακόλουθη σύνταξη:

SUB  Rd,  Rr    ;     Rd = Rd – Rr

Η εντολή SUB προκαλεί τη CPU να αφαιρέσει την τιμή του καταχωρητή Rr  από τον Rd και να βάλει το αποτέλεσμα πίσω στον καταχωρητή Rd.

Παράδειγμα

Στο ακόλουθο κομμάτι κώδικα αφαιρούμε την τιμή 0x30 από την τιμή 0x45
LDI  R18,  0x30  ;  R18 = 0x30
LDI  R19, 0x45   ;  R19 = 0x45
SUB  R19, R18    ;  R19 = R19 – R18

Παράδειγμα

Το επόμενο κομμάτι κώδικα, αφαιρεί την τιμή 0x08 από τα περιεχόμενα της θέσης 0x350 και αποθηκεύει το αποτέλεσμα στη θέση 0x380
LDS  R10, 0x350  ;   R10 = contents of location 0x350
LDI  R20, 0x08   ;  R20 = 0x08
SUB  R10,  R20   ;  R10 = R10 – R16
STS  0x380, R10  ;  store the contents of  R10 to location 0x380

Παράδειγμα

Το επόμενο κομμάτι κώδικα μειώνει τα περιεχόμενα του R12 κατά 1
LDI  R18, 0x01
SUB  R12,  R18

Η εντολή  DEC

Η εντολή  DEC  έχει την ακόλουθη σύνταξη:

DEC  Rd  ;  Rd = Rd – 1

Η εντολή DEC μειώνει  (αφαιρεί 1 από) τα περιεχόμενα του καταχωρητή Rd και βάζει το αποτέλεσμα πίσω στον καταχωρητή Rd.

Παράδειγμα, η επόμενη εντολή αφαιρεί 1 από τα περιεχόμενα του R14
DEC R14  ;  R10 = R10 – 1

Η εντολή  COM

Η εντολή  COM  Rd  παρέχει το συμπλήρωμα ως προς ένα του καταχωρητή Rd και μετά βάζει το αποτέλεσμα πίσω στον Rd. Το συμπλήρωμα ως προς ένα μιας δυαδικής τιμής 8-bit δίνεται αντικαθιστώντας τα 0 με 1 και τα 1 με 0.

Παράδειγμα

Στο επόμενο κομμάτι κώδικα βάζουμε την τιμή 0x55 στον R20 και τον στέλνουμε στη θύρα PORTD. Μετά παίρνουμε το συμπλήρωμα ως προς 1 του καταχωρητή R20 που γίνεται 0xAA στο δεκαεξαδικό. Δηλαδή παίρνουμε το συμπλήρωμα της τιμής 01010101 (0x55) που γίνεται 10101010 (0xAA), και το στέλνουμε στη θύρα D:
LDI  R20,  0x55  ;  R20 = 0x55
OUT  PORTD, R20  ;  copy  R20  to port D  (PORTD = 0x55)
COM  R20         ;  complement  R20
OUT  PORTD, R20  ;  copy  R20  to port D  (PORTD = 0xAA)