AVR ASSEMBLY SLIDE SET 1 Αρχιτεκτονική HARVARD Ο μικροελεγκτής AVR περιέχει έναν επεξεργαστή RISC (Reduced Instruction Set Computer) o οποίος έχει σχεδιαστεί με βάση την αρχιτεκτονική Harvard. Στην αρχιτεκτονική Harvard υπάρχει διαφορετικός δίαυλος για τη μεταφορά δεδομένων (data bus) και διαφορετικός δίαυλος για τη μεταφορά των εντολών (instruction bus). Διαφορετικός δίαυλος για τη μεταφορά: ▪ εντολών (instructionbus) και ▪ δεδομένων (databus) Περιλαμβάνει δύο διαφορετικές μνήμες: ▪ μνήμη προγράμματος (program memory) και ▪ μνήμη δεδομένων (data memory) Επιτρέπει οι εντολές να έχουν διαφορετικό μήκος από τα δεδομένα ανάλογα με το πλήθος των εντολών Επιτρέπει επικάλυψη λειτουργιών ανάκλησης εντολής, ανάγνωσης εγγραφής δεδομένων, εκτέλεσης εντολών. (!) Οι ADIW/SBIW χρησιμοποιούνται με pointers για έμμεση διευθυνσιοδότηση (!) Η ANDI αποθηκεύει το αποτέλεσμα στον καταχωρητή, για αυτό χρησιμοποιούμε temp για τη διατήρηση της αρχικής τιμής Παράδειγμα 1ο: Nα προστεθούν δυο 16-bitαριθμοί, που βρίσκονται στις θέσεις μνήμης 0x0060-0x0061 και 0x0062-0x0063. Το αποτέλεσμα σώζεται στη διεύθυνση 0x0064-0x0065 της μνήμης δεδομένων. .include "m16def.inc" .org 0x0000 main: ; first add the lower 8 bits and keep the carry for the upper 8 bits. check for overflow ldi xl, 0x60 clr xh ; because the addresses are consecutive, we can simply use one pointer ld r1,x+ ; save the value held in 0x60 in r1 and increment pointer for the next command ld r2,x+ ld r3,x+ ld r4,x+ ; use x to store result in the consecutive memory addresses add r1,r3 adc r2,r4 st x+,r1 st x,r2 .exit Byte Address vs Word Address: Το byte address έχει μέγεθος 1 byte, ενώ το word address έχει μέγεθος 2 bytes (16 bit normal address). Π.χ. για λέξη 0x1201 έχει bytes (σε little endian): 0x01 και 0x12. Επομένως, για να κάνουμε ανάγνωση στα στοιχεία ενός πίνακα δεκαεξαδικών στοιχείων, θα κάνουμε ldi ZH, HIGH(Table * 2), ldi ZL, LOW(Table * 2). Με το επί 2 γίνεται left shift για την ανάκτηση της διεύθυνσης του byte, όχι της λέξης. Για να μετακινηθούμε x στοιχεία στον πίνακα, θα πρέπει να γίνει ADIW ZL, x*2. (επί 2 για μετακίνηση του 16bit pointer). (!) Αρχικοποιούμε το HIGH και LOW register του pointer, όμως ΠΑΝΤΑ μετακινούμε το χαμηλό pointer. (!) Η εντολή LPM αποθηκεύει το δεδομένο από τη μνήμη προγράμματος που δείχνει ο δείκτης Z. (!) Tο DW δημιουργεί word addressed data (2 bytes), οπότε είναι απαραίτητο να γίνεται lsl του index (left shift). To DB δημιουργεί byte addressed data, οπότε δεν χρειάζεται κάτι τέτοιο Παράδειγμα 2ο: ένας πίνακας με 8τιμές-λέξεις (16-bit), όπου η έκτη τιμή διαβάζεται και αποθηκεύεται στους καταχωρητές R25:R24. Table: .DW 0x1201,0x3423,0x5645,0x7867,0x9A89 .DW 0xBCAB,0xDECD,0xF0EF LDI ZL, LOW(Table*2) ; save both byte addresses of the first word address pointer LDI ZH, HIGH(Table*2) ; only use ZL to access memory ADIW ZL, 10 ; move ZL pointer 5 elements ahead, to element 6 LPM ; reads memory pointed to by ZL and stores it in R0 MOV R24, R0 ADIW ZL, 1 LPM MOV R25, R0 Οπτικοποίηση του πίνακα (!) όταν θέλουμε να ελέγξουμε μόνο το lsb ή msb ενός καταχωρητή, είναι αποδοτικότερη η χρήση carry με rotate through carry Παράδειγμα 3: Πρόγραμμα που υπολογίζει το τετράγωνο ενός αριθμού με βάση έναν πίνακα δεδομένων, που περιέχει τα τετράγωνα των αριθμών, εντός της μνήμης προγράμματος. Ο αριθμός ανήκει στο διάστημα [0,15]. .def num, r16 .def result, r17 start: clr temp out DDRC temp ; port C is set to input ldi ZL, LOW(Table*2) ldi ZH, HIGH(Table*2) in num, PORTC add ZL, num clr ZH lpm mov result, r0 rjmp start ; no need to use both word bytes, since max number is 152 = 225 (1byte) Παράδειγμα 4: Στο παράδειγμα αυτό γίνεται η παρουσίαση εντολών που επιτρέπουν τον χειρισμό σε επίπεδο bit, χρήσιμα στην επίλυση προβλημάτων συνδυαστικής λογικής. Πιο συγκεκριμένα υποθέτουμε ότι οι λογικές μεταβλητές A, B, C, D και E βρίσκονται συνδεδεμένες στα 5 πρώτα LSB της θύρας PORTD. Να δοθεί το assembly πρόγραμμα που υλοποιεί τη λογική εξίσωση: ΕΞΟΔΟΣ(LED5) = (A+B΄)*(C*D+E) Το ένα bit της εξόδου παρέχεται από το 5ο bit της θύρας PORTB. .include “m16def.inc” .def A=r16 .def BN=r17 .def C=r18 .def D=r19 .def E=r20 .def temp = r21 .cseg .org 0 ;define each bit in a separate register for clarity start: clr temp out DDRD, temp ser temp out PORTD, temp out DDRB, temp ; set port D as input ; get the complement of temp (all 1s) ; pullup for port D (ensure pin reads high when it’s not pressed) ; set port B as output again: in temp, PIND mov A, temp lsr temp mov BN, temp com BN lsr temp mov C, temp lsr temp mov D, temp lsr temp mov E, temp or A, BN and C,D or C,E and A,C andi A,1 lsl A (x4) out PORTB,A rjmp again Why use a pull-up? Digital input pins can be in one of three states: 1. High (1) – connected to Vcc 2. Low (0) – connected to GND 3. Floating (undefined) – not connected to anything; may randomly pick up noise A pull-up resistor prevents that third "floating" state by gently holding the pin high unless something else pulls it low. SLIDESET 2 (!) Η brne αυτόματα συγκρίνει με το 0 (!) Η SBIS είναι ιδιαίτερα χρήσιμη σε προγράμματα όπου θέλουμε led ouuput υπό συνθήκη για τα bits του input. Άμεση και Έμμεση Διευθυνσιοδότηση Οι εντολές ταξινομούνται με βάση τον τρόπο που επιτυγχάνεται πρόσβαση στα δεδομένα και τις πράξεις που εκτελούνται με αυτά. Πρόκειται για τους τρόπους διευθυνσιοδότησης του προγράμματος και των δεδομένων. Η διευθυνσιοδότηση διακρίνεται σε άμεση και έμμεση: • • • • Άμεση Διευθυνσιοδότηση ενός καταχωρητή ◦ Η εντολή διαβάζει τα περιεχόμενα του καταχωρητή, εκτελεί τις πράξεις με αυτά και αποθηκεύει το αποτέλεσμα στον ίδιο καταχωρητή. Ο καταχωρητής είναι ένας από τους 32 καταχωρητές εργασίας R0-R31 που διαθέτουν το αντίστοιχο τμήμα μνήμης (register file). Άμεση Διευθυνσιοδότηση δύο καταχωρητών ◦ Η εντολή διαβάζει τα περιεχόμενα των δυο καταχωρητών, εκτελεί την πράξη μεταξύ των δεδομένων και αποθηκεύει το αποτέλεσμα στον καταχωρητή προορισμού Rd. Άμεση Διευθυνσιοδότηση των θυρών εισόδου-εξόδου ◦ H πρόσβαση στις θέσεις μνήμης που αντιστοιχούν οι θύρες εισόδου εξόδου (Ι/Ο memory) επιτυγχάνεται με 2 εντολές: της ΙΝ Rd, PortAddress και της OUT PortAddress, Rr όπου PortAddress είναι η διεύθυνση του καταχωρητή εισόδου-εξόδου. Άμεση Διευθυνσιοδότηση μνήμης δεδομένων ◦ H πρόσβαση στις θέσεις μνήμης επιτυγχάνεται με εντολές μήκους 2 λέξεων, όπου η μία λέξη (16 bits) αντιστοιχεί στη διεύθυνση της μνήμης δεδομένων. Άρα, ο χώρος μνήμης όπου επιτυγχάνεται πρόσβαση είναι 64Kbyte. ◦ (!) Στα AVR, όπως και σε πολλούς άλλους μικροελεγκτές αρχιτεκτονικής Harvard, η μνήμη του προγράμματος και η μνήμη δεδομένων είναι ξεχωριστά. Οι εντολές συνήθως έχουν μήκος 1 λέξης, ωστόσο κάποιες εντολές, ειδικά εκείνες που περιλαμβάνουν διευθυνσιοδότηση, έχουν μεγαλύτερο μέγεθος. ◦ (!) Ο χώρος μνήμης (μέγιστος αριθμός διευθυνσιοδοτημένων bytes) δίνεται από τον αριθμό όλων των δυνατών διευθύνσεων. Π.χ. για διευθύνσεις μήκους 16bits όπου κάθε διεύθυνση αντιστοιχεί σε 1 byte, έχει συνολικό χώρο μνήμης 64kB. • • • Έμμεση Διευθυνσιοδότηση μνήμης δεδομένων ◦ Οι εντολές αυτές έχουν μήκος 1 λέξης και χρησιμοποιούν έναν κατ/τή δείκτη (X,Y,Z), το περιεχόμενο του οποίου αντιστοιχεί στη διεύθυνση της μνήμης δεδομένων. Ο κατ/τής δείκτη X,Y,Z μπορεί να αυξηθεί κατά μία μονάδα μετά την ανάθεση της τιμής στον κατ/τη Rr, ώστε να δείχνει σε επόμενη θέση μνήμης ή να μειωθεί κατά μία μονάδα πριν την ανάθεση της τιμής στον κατ/τη Rr, ώστε να δείχνει σε προηγούμενη θέση μνήμης ή να έχουμε έμμεση φόρτωση του κατ/τή Rd με τα περιεχόμενα της θέσης μνήμης (Υ+q), ώστε να επιτύχουμε πρόσβαση σε παραπέρα θέση μνήμης. Πρόκειται δηλαδή για μετατόπιση. Σχετική Διευθυνσιοδότηση μνήμης προγράμματος ◦ Οι εντολές αυτές επιτυγχάνουν πρόσβαση σε χώρο μνήμης προγράμματος και είναι του τύπου RCALL, RJMP όπου χρησιμοποιείται μια μετατόπιση +/ 2K στο περιεχόμενο του μετρητή προγράμματος. Έμμεση Διευθυνσιοδότηση μνήμης προγράμματος ◦ Οι εντολές αυτές επιτυγχάνουν πρόσβαση σε χώρο μνήμης προγράμματος έως 64Kbytes με χρήση του καταχωρητή Ζ, ως δείκτη μιας θέσης μνήμης προγράμματος. Στοίβα Η στοίβα χρησιμοποιείται από την αριθμητική λογική μονάδα (ALU) για αποθήκευση διευθύνσεων επιστροφής από ρουτίνες διακοπής-υπορουτίνες. Η στοίβα χρειάζεται έναν δείκτη στοίβας (SP) και χώρο μνήμης στην SRAM. Πρόκειται, λοιπόν, για δομή τύπου Last-In-First-Out (LIFO). Μια διεύθυνση SRAM έχει μήκος 16 bits, οπότε ο καταχωρητής SPL κρατά τα 8 LSB bits και ο SPH τα 8 MSB bits της διεύθυνσης. Η μνήμη της στοίβας αυξάνεται προς τα κάτω. Επιπλέον, εάν ξεκινάει από τη διεύθυνση (SP), το πρώτο της στοιχείο θα βρίσκεται στις θέσεις: (SP)-1 ← rh και (SP)-2 ← rl • Χρήσιμες εντολές: ◦ XTHL : exchange top of stack with H and L ◦ STHL data16 : save HL register pair in address data16 and data16 – 1. Useful for manual stack manipulation, in conjuction with INX and DCX Αρχικοποίηση Στοίβας Σε προγράμματα με κλήσεις συναρτήσεων, με διακοπές και εντολές PUSH/POP, είναι απαραίτητη η αρχικοποίηση της στοίβας ως εξής: ldi reg, LOW(RAMEND) out SPL, reg ldi reg, HIGH(RAMEND) out SPH, reg Με αυτόν τον κώδικα τοποθετούμε τον δείκτη στην κορυφή της RAM (όπου η στοίβα μεγαλώνει προς τα κάτω). Παράδειγμα 1: Εντολές ελέγχου ροής του προγράμματος και διακλάδωσης Το πρόγραμμα βρίσκει αν η τιμή στον καταχωρητή r20 (num) που δίνεται από το PORTD είναι σε περιοχή της μνήμης δεδομένων SRAM που η πρώτη διεύθυνση βρίσκεται στον καταχωρητή X= r27:r26 και το πλήθος τους, στον καταχωρητή r21 (count). Αν βρει τη τιμή, θέτει στον καταχωρητή r22 τιμή 1 και τη διεύθυνση που βρήκε το δεδομένο (μέσω του καταχωρητή δείκτη X), αλλιώς θέτει 0 στον καταχωρητή r22 (flag). .def num = r20 .def count = r21 .def flag = r22 .def data = r19 start: clr num out DDRD, num ; set port D as input in num, PORTD loop: ld data, x+ cp data, num breq found ; load memory in SRAM pointed to by pointer x, keep incrementing x ; check if found ; branch equal to found not_found: ; instead of keeping track of a variable and comparing to count, decrement count dec count cpi count, 0 brne loop ; branch if not equal clr flag rjmp end found: ldi flag, 1 sbiw r26, 1 end: ; pointer points past the address because of ld data, x+. Must decrement Παράδειγμα 2: Αναζήτηση ενός χαρακτήρα Να υπολογιστεί πόσες φορές εμφανίζεται ένας χαρακτήρας, που η τιμή του δίνεται από το PORTD, σε περιοχή της μνήμης δεδομένων SRAM που η πρώτη διεύθυνση βρίσκεται στον καταχωρητή X=r27:r26 και το πλήθος τους, στον καταχωρητή r21 (count1). Το πλήθος των εμφανίσεων του χαρακτήρα επιστρέφεται μέσω του καταχωρητή r22 (count2). .def char = r16 ; input from port D .def count1 = r21 ; length of memory block .def count2 = r22 ; instances of char in the block .def data = r19 start: clr char out DDRD, char in char, PORTD clr count2 ; initialize count 2 to not have junk data loop: ld data, x+ cp char, data brne not_found inc count2 not_found: dec count1 cpi count1, 0 brne loop end: Παράδειγμα 3: Πρόγραμμα εντοπισμού μέγιστου και ελάχιστου Το πρόγραμμα εντοπίζει τον μέγιστο και τον ελάχιστο σε περιοχή της μνήμης προγράμματος όπου έχουν αποθηκευθεί συνολικά 256 δεδομένα (των 8 bit). Η μνήμη είναι αποθηκευμένη σε πίνακα Array. .def max = r16 .def min = r17 .def data = r18 .def count = r19 start: ldi ZH, HIGH(Array*2) ldi ZL, LOW(Array*2) lpm data, z+ ; use first element for min and max!! clr count mov max, data mov min, data loop: inc count cpi count, 255 breq end lpm data, z+ cp data, max brge update_max cp data, min brlo update_min rjmp loop update_max: mov max, data rjmp loop update_min: mov min, data rjmp loop end: ; branch if greater than or equal ; branch if lower Παράδειγμα 4: Διάβασμα/Εγγραφή των Ports Στο παράδειγμα αυτό διαβάζονται διακόπτες που είναι συνδεδεμένοι στο PORTD και ανάβουν leds που είναι συνδεδεμένα στο PORTB με την εξής λογική: • • • • • Αν ο διακόπτης sw0 είναι ON τότε ανάβει το led0. Αν ο διακόπτης sw1 είναι ON τότε ανάβουν τα led0 και led1. Αν ο διακόπτης sw2 είναι ON τότε ανάβουν τα led0, led1 και led2. ... Αν ο διακόπτης sw7είναι ON τότε ανάβουν τα led0, led1, led2 , led3 , led4 , led5 , led6 , led7. Μόνο ένας διακόπτης είναι ΟΝ κάθε φορά. Η υλοποίηση του κώδικα βασίζεται στη δημιουργία ενός πίνακα στη μνήμη προγράμματος (codesegment) που περιέχει τα δεδομένα για την έξοδο PORTB. (Ενναλακτικά η έξοδος PORTB θα μπορούσε να υπολογιστεί με απλούστερο τρόπο από τη σχέση led=2*sw-1) .INCLUDE "m16def.inc" ; δήλωση μικροελεγκτή .LIST ;ενεργοποιεί την εμφάνιση του listingτου κώδικα κατά τη μεταγλώττιση .DEF reg=R0 ; η εντολή LPM χειρίζεται τον καταχωρητή R0 .DEF temp=R16 table: ; Πίνακας με τους συνδυασμούς των leds(κάθε byte αντιστοιχεί σε ένα συνδυασμό). ; Για τα led υποθέτουμε ότι ισχύει αρνητική λογική(είναι αναμμένα με έξοδο 0) ; Η χρήση του .DB xxστη μνήμη προγράμματος πάντα προσθέτει ένα μηδενικό byte. ; Προτιμάται η χρήση του .DW xxyyόπου 2 byte ενώνονται σε μια λέξη. ; Όταν προσθέτουμε μια λέξη όπως FEFFhσε πίνακα, το 1ο byte του πίνακα που ; λαμβάνεται μέσω της εντολής LPM είναι το LSB (FFh), και όχι το MSB (Feh). .DW 0xFEFF ; 1 Led, 0 Leds .DW 0xF8FC ; 3 Leds, 2 Leds .DW 0xE0F0 ; 5 Leds, 4 Leds .DW 0x80C0 ; 7 Leds, 6 Leds .DW 0x0000 ; 8 Leds, 8 Leds ; Η πρόσβαση στον πίνακα μέσω της εντολής lpm γίνεται κατά byte, ενώ οι διευθύνσεις ; οργανώνονται κατά λέξεις .EQU array=table*2 start: clr temp ; PORTD ως είσοδος των διακοπτών out DDRD,temp dectemp ; φόρτωση τιμής 0xFF στον καταχωρητή temp out DDRB,temp ; PORTB ως έξοδος των Leds out PORTD,temp ; ενεργοποίηση εσωτερικών αντιστάσεων πρόσδεσης loop: ; χρήση καταχωρητή δείκτη Ζ(ZL(R30) και ZH(R31)) που έχει οριστεί στο αρχείο m16def.inc ldi ZL,LOW(array) ; ο Z δείχνει στο 1ο byte (FF) του πίνακα. ldi ZH,HIGH(array) in temp,PIND ; ανάγνωση διακοπτών cpi temp,0xFF ; αν όλοι οι διακόπτες είναι off(αρνητική λογική διακοπτών), τότε breq diavasma ; άλμα στο diavasma(πρώτη θέση του πίνακα -όλα τα Leds off) inc_pointer: adiw ZL, 1 ; Αυξάνεται το Z μέχρι να βρεθεί η πρώτη μονάδα rortemp ; καθώς ολισθαίνει δεξιά η τιμή των διακοπτών. brloinc_pointer ; Όσο το C=1 έχουμε άλμα και αύξηση του Z (ενναλακτικά brcs). diavasma: lpm ; Το περιεχόμενο της θέσης που δείχνει ο Ζ εγγράφεται στον reg(R0). out PORTB,reg ; Μεταφορά του δεδομένου στα leds. rjmploop Παράδειγμα 5: Χειρισμός leds με χρονική καθυστέρηση .INCLUDE "m16def.inc" ; δηλώνουμε μικροελεγκτή .DEF reg= R16 .defDelay= r17 ; καταχωρητής μεταβλητής Delay .defDelay2 = r18 ; καταχωρητής μεταβλητής Delay2 main: ldireg,0b11111111 outDDRB,reg ; ορίζουμε το PORTB ως έξοδο DLY: decDelay ; Καθυστέρηση για να γίνουν ορατές οι αλλαγές brne DLY dec Delay2 brne DLY ldi reg,0x00 ; (0=on, 1=off). out PORTB,reg ; άναμμα των leds DLY2: decDelay ; Καθυστέρηση για να γίνουν ορατές οι αλλαγές brne DLY2 dec Delay2 brne DLY2 ldi reg,0xFF out PORTB,reg ; σβήσιμο των leds και ατέρμων βρόχος rjmpmain ; για συνεχή επανάληψη της διαδικασίας Παράδειγμα 6: Χειρισμός στα leds με βάση τη τιμή των switches Στο παράδειγμα γίνεται χειρισμός στα leds που συνδέονται στη θύρα PORTB με βάση τη τιμή των switches που συνδέονται στη θύρα PORTD. Η συμπεριφορά των leds καθορίζεται με βάση ποιος διακόπτης πιέζεται σύμφωνα με τον παρακάτω πίνακα: • • • Δεν πιέζονται ταυτόχρονα περισσότεροι του ενός διακόπτες. Αρχική κατάσταση OFF για όλα τα LEDS. Αρνητική λογική για τα led. .include"m16def.inc" .defTemp=r16 ; προσωρινός καταχωρητής .defDelay=r17 ; καταχωρητής μεταβλητής Delay .defDelay2 =r18 ; καταχωρητής μεταβλητής Delay2 RESET: clrTemp ; αρχικοποίηση του PORTD outDDRD,Temp ; ως θύρας εισόδου ser Temp ; αρχικοποίηση του PORTB out DDRB,Temp ; ως θύρας εξόδου outPORTD,Temp ; ενεργοποίηση των αντιστάσεων πρόσδεσης LOOP: sbic PIND,0x00 ; Αν (Port D, pin0 = 1)τότε incTemp ; μέτρηση LEDS μια μονάδα προς τα κάτω sbic PIND,0x01 ; Αν (Port D, pin1 = 1)τότε decTemp ; μέτρηση LEDS μια μονάδα προς τα πάνω sbic PIND,0x02 ; Αν (Port D, pin2 = 1)τότε rorTemp ; ολίσθηση LEDS μία θέση δεξιά sbic PIND,0x03 ; Αν (Port D, pin3 = 1)τότε rolTemp ; ολίσθηση LEDS μία θέση αριστερά sbic PIND,0x04 ; Αν (Port D, pin4 = 1)τότε com Temp ; αντιστροφή LEDS sbic PIND,0x05 ; Αν (Port D, pin5 = 1)τότε neg Temp ; αντιστροφή LEDS και πρόσθεση 1 sbic PIND,0x06 ; Αν (Port D, pin6 = 1) τότε swapTemp ; εναλλαγή των τμημάτων highκαι lowτων LEDS sbic PIND,0x07 ; Αν (Port D, pin7 = 1)τότε ldi Temp,0xF0 ; Τα 4 LSB ONκαι τα 4 MSB OFF out PORTB,Temp ; ενημέρωση των LEDS DLY: decDelay ; καθυστέρηση για να γίνουν ορατές οι αλλαγές brne DLY dec Delay2 brne DLY rjmpLOOP ; επανάληψη βρόχου Παράδειγμα 7: Χειρισμός διακοπτών και led Με το πάτημα ενός διακόπτη (ή και περισσότερων) από τα 4 δεξιότερα switches (PORTD) ανάβει το αντίστοιχο led (PORTB) και όσο είναι πατημένο να το περιστρέφει κυκλικά προς τα δεξιά. Αντίστοιχα, πάτημα ενός διακόπτη από τα 4 αριστερότερα να προκαλεί αριστερή περιστροφή. Όταν δεν είναι πατημένο κανένα switch να σβήνουν όλα τα leds. .include “m16def.inc” .def temp = r16 .def temp2 = r17 .def switches = r20 start: clr temp out DDRD, temp ; PORTD set as input ser temp out DDRB, temp ; PORTB set as output clr temp out PORTB, temp read_switches: in switches, PORTD cpi switches, 1 brge switches_on rjmp read_switches switches_on: mov temp, switches andi temp, 0b00001111 ; check if any of the 4 lsb’s are pressed by masking brne rotate_right rotate_left: out PORTB, temp rol call delay in temp2, PORTD cp temp2, temp brne start rjmp rotate_left rotate_right: out PORTB, temp ror call delay in temp2, PORTD cp temp2, temp brne start rjmp rotate_right Παράδειγμα 8: Χειρισμός θυρών I/O Συνεχής ανάγνωση θύρας PortD. Όταν διαπιστώνεται ότι άλλαξε η τιμή της, τότε κάθε νέα τιμή αποθηκεύεται σε διαδοχικές θέσεις μνήμης SRAM αρχίζοντας από τη διεύθυνση $0060 ως την $0150. .include “m16def.inc” .def port_value = r16 .def temp = r17 .def reg = r18 start: ldi reg, LOW(RAMEND) out SPL, reg ldi reg, HIGH(RAMEND) out SPH, reg clr temp out DDRD, temp ser temp out PORTD, temp ; set pull-up switches to prevent floating input ld xl, 0x60 clr xh ; initialize x registers in port_value, PIND ; initial value loop: in temp, PIND cp temp, port_value breq loop store_value: nop st x+, temp cp xh nop ; waste one clock cycle cpi xl, $51 brne loop reset: ; keep reading and replacing addresses starting from $0060 ld xl 0x60 clr xh rjmp loop Παράδειγμα 9: Μεταφορά δεδομένων Μεταφορά ενός πίνακα των 20 bytes από το τμήμα κώδικα(cseg)στο τμήμα δεδομένων(dseg). Εξετάζεται κάθε στοιχείο του πίνακα και αν πρόκειται για περιττό αποθηκεύεται σε πίνακα στη διεύθυνση $0060, ενώ αν πρόκειται για άρτιο σε πίνακα στη διεύθυνση $0074 του τμήματος δεδομένων. .INCLUDE"m16def.inc" .DEF count=R18 .EQU Location1 = 0x0060 .EQU Location2 = 0x0074 ; Ορίζουμε όνομα καταχωρητή-μετρητή ; Διεύθυνση αποθήκευσης περιττών ; Διεύθυνση αποθήκευσης άρτιων start: ldi count,21 ldi ZH, HIGH(Table*2) ldi ZL,LOW(Table*2) ldi XH,HIGH(Location1) ldi XL,LOW(Location1) ldi YH,HIGH(Location2) ldi YL,LOW(Location2) ; Μετρητής για μεταφορά 20 στοιχείων ; Φόρτωμα του Ζ με τη διεύθυνση του πίνακα ; από το τμήμα κώδικα ; Η διεύθυνση του πίνακα περιττών στον Χ ; Η διεύθυνση του πίνακα άρτιων στον Υ loop: lpm r20,z+ dec count breq end sbrs r20,0 rjmp even ; Φόρτωση μνήμης προγράμματος και ; αύξηση δείκτη πρόσβασης επόμενης θέσης. ; Μείωση μετρητή και άλμα όταν ; μεταφερθούν όλα τα στοιχεία. ; Αν πρόκειται για περιττό (R20.0=1) skip ; Αλλιώς άρτιος και άλμα στην ετικέτα even. ST X+, R20 rjmp loop ; Ο αριθμός είναι περιττός. ; Αποθήκευση περιττών και ; επανάληψη με φόρτωση νέου αριθμού. ST Y+, R20 rjmp loop ; Αποθήκευση άρτιων και επανάληψη. ; επανάληψη με φόρτωση νέου αριθμού. odd: even: .cseg ; Επιλογή μνήμης κώδικα Table: ; πίνακας δεδομένων. .DW0x0100,0x0706,0x1713,0x3326,0x27C6 .DW 0x5042,0x7A61,0xA2F1,0xE0D7,0x89FD end: .exit Παράδειγμα 10: Κατάταξη Αριθμών Κατάταξη 256 αριθμών που βρίσκονται στη μνήμη δεδομένων (διεύθυνση $0060) να τεθούν σε σειρά αύξοντος μεγέθους. Εφαρμόζουμε τη μέθοδο των φυσαλίδων σύμφωνα με την οποία πραγματοποιούμε διαδοχικά περάσματα στην ακολουθία των αριθμών και αντιστρέφουμε τους διαδοχικούς αριθμούς που δεν βρίσκονται σε αύξουσα σειρά. Η διαδικασία επαναλαμβάνεται έως ότου να μην χρειαστεί εναλλαγή .INCLUDE "m16def.inc" .DEF reg1=R18 .DEF reg2=R20 .EQU Location1 = 0x0060 .DEF count=r16 .EQU numb= 0x100 sort: LDI XH, HIGH(Location1) ; διεύθυνση μνήμης δεδομένων LDI XL, LOW(Location1) ldi count, numb ; πλήθος αριθμών clt ; αρχικοποίηση δείκτης αλλαγής (Τ=0) loop: ld reg1,x+ ld reg2,x cp reg2,reg1 brge no_change st x,reg1 st-x,reg2 adiwxl,1 set no_change: dec count brne loop brbs6,sort .exit ; φόρτωση διαδοχικών τιμών από μνήμη ; δεδομένων σε δυο καταχωρητές ; σύγκριση διαδοχικών τιμών(reg2 - reg1) ; εναλλαγή για αύξουσα διάταξη ; x+ ; θέτουμε δείκτη αλλαγής (T=1) ; σάρωση όλου του πίνακα ; επανάληψη σε περίπτωση εναλλαγής (T=1) ;(εναλλακτικά brts sort) Παράδειγμα 11: Χειρισμός διακοπτών εισόδου και led εξόδου Αν ένα από τα switch 0 ή 1 είναι πατημένο, τότε ανάβει τα αντίστοιχο led. Αν ένα από τα switch 2-6 είναι πατημένο, τότε ανάβουν τα leds2 -7. Αν το switch 7 είναι πατημένο, τότε σβήνουν όλα τα leds. Ανάστροφη λογική στα switch(0 για πατημένο switch) Ανάστροφη λογική στα Led(0 για αναμμένο led) ΘΕΛΟΥΜΕ ΠΑΡΑΛΛΗΛΙΑ ΤΩΝ ΠΑΡΑΠΑΝΩ ΣΥΝΘΗΚΩΝ! .include “m16def.inc” .def reg = r16 main: ldi reg,LOW(RAMEND) ; αρχικοποίηση δείκτη στοίβας στη out SPL, reg ldi reg,HIGH(RAMEND) ; τελευταία θέση της SRAM(RAMEND) out SPH, reg clr reg ; PortD ως είσοδος out DDRD, reg ser reg out PORTD, reg ; Port D (pull-ups) out DDRB, reg ; PortB ως έξοδος out PORTB, reg ; σβήσιμο leds (ανάστροφη λογική) loop: sbis PIND, 0 rcall light0 ; ΑΝΑΓΝΩΣΗ SWITCH 0 ; rcall is more efficient for subroutines close in memory! ; Ανάγνωση switch1 με διαφορετικό τρόπο: ; Η πρόσβαση σε μια θύρα μπορεί να γίνει με εντολές διευθυνσιοδότησης της SRAM. ; Η διεύθυνση SRAM είναι 32 bytes υψηλότερα από την αντίστοιχη διεύθυνση θύρας. ; Ορίζουμε το X(XH:XL, R27:R26) ως δείκτη αυτής της θύρας. ; Η μέθοδος αυτή παρέχει μεγαλύτερη ευελιξία στο πρόγραμμα .EQU mem_name=PIND+ $20 ; διεύθυνση στην sram ldi R26,LOW(mem_name) ldi R27,HIGH(mem_name) ld reg,X ; φόρτωσε καταχωρητή regαπό καταχωρητή δείκτη sbrs reg,1 ; Έλεγχος Pin1 (switch1-Bit1 ). Αν είναι 0 που rcall light1 ; σημαίνει ότι πατήθηκε, γίνεται κλήση της Light1. ; Αλλιώς παρακάμπτεται για να γίνει ο έλεγχος των Switches2 ως 6 in reg, PIND ori reg, 0b10000011 ; ori is best used for multiple bits, andi is best used for single bits cpi reg, 0b11111111 ; if none are pressed don’t branch breq sw7 in reg, PIND andi reg, 0b00000011 out PORTB, reg light0: in reg, PORTB ; read current state of the output andi reg, 0b11111110 ; PRESERVES LOGIC IN ACTIVE LOW CIRCUITS out PORTB, reg ret light1: in reg, PORTB andi reg, 0b11111101 out PORTB, reg ret sw7: in reg, PORTD rol reg ; rotate left and keep carry = D7 bcrs loop ; if carry is set (=1) keep looping mvi reg, 0xFF ; else turn off all leds out PORTB, reg rjmp loop Παράδειγμα 12: Οδήγηση κυλιόμενης πλατφόρμας Σε ένα Μικροελεγκτή AVR που το PORTB ορίζεται ως θύρα εισόδου και το PORTD ως θύρα εξόδου να υλοποιηθεί ένα σύστημα οδήγησης κυλιόμενης πλατφόρμα μονής κατεύθυνσης η οποία να ενεργοποιείται από το φωτοκύτταρο S. Συγκεκριμένα, αν ένας επιβάτης εισέρχεται στην πλατφόρμα, όταν είναι ακίνητη, διακόπτει δέσμη φωτός (γίνεται S=0) και τότε τίθεται σε κίνηση η πλατφόρμα με το σήμα εξόδου Μ (για Μ=1 έχουμε κίνηση). Η κίνηση να σταματά ~10 sec μετά την τελευταία διακοπή του φωτοκυττάρου S (χρόνος για να αδειάσει η πλατφόρμα από επιβάτες). Δίνεται ρουτίνα χρονοκαθυστέρησης DSEC των 100 msec. Μακροεντολές Παράδειγμα 13: Άναμμα των LEDs 0, 1, 2 με χρήση μακροεντολής .INCLUDE "m16def.inc" .LIST .DEF temp=R18 .MACRO FOUR_INC inc temp inc temp inc temp inc temp .ENDMACRO ; Κύριο πρόγραμμα main: ser temp out DDRB,temp clr temp FOUR_INC FOUR_INC Delay4 Delay4 Delay4 dec temp com temp out PORTB,temp LOOP: rjmp LOOP ; δήλωση μακροεντολής ; ο καταχωρητής temp=0xFF ; PortB(LEDs) ως έξοδος ; μηδενισμός καταχωρητή temp ; εισαγωγή μακροεντολής (4 INCs) ; άλλα 4 INCs, temp=0000 1000 ; Συνολικά καθυστέρηση 12 NOP ; temp=0000 1000=>0000 0111 ; αναστροφή για απεικόνιση στα LEDs ; άναμμα των leds Παράδειγμα 14: Χρήσης ετικετών και μακροεντολών .INCLUDE "m16def.inc" .DEF temp=R18 .MACRO TestMacro ; δήλωση μακροεντολής inc temp ; αύξηση καταχωρητή temp brne macro_jump ; παράκαμψη επόμενης εντολής αν δεν προκύψει υπερχείλιση rjmp OVERFLOW ; άλμα σε περίπτωση υπερχείλισης σε ετικέτα εκτός μακροεντολής macro_jump: ; ετικέτα εντός μακροεντολής .ENDMACRO main: ser temp ; κύριο πρόγραμμα out DDRB,temp ; PortB(LEDs) ως έξοδος ldi temp,0xFE ; θέτουμε καταχωρητή=254 ΤestΜacro ; εισαγωγή μακροεντολής (ένα INC), temp=255 ΤestΜacro ; ξανά εισαγωγή μακροεντολής (άλλο ένα INC), temp=0 και overflow ; Η λειτουργία των μακροεντολών θα προκαλέσει υπερχείλιση (λόγω INC), ; οπότε η επόμενη εντολή δεν θα εκτελεστεί. Εάν εκτελείτο θα άναβε όλα τα LEDs. outp: out PORTB,temp ; άναμμα των leds LOOP: rjmp LOOP OVERFLOW: ; λόγω υπερχείλισης θα εκτελεστεί ο παρακάτω κώδικας και ser temp ;τα LEDs PB.0 -PB.7 θα σβήσουν out PORTB,temp rjmp LOOP Παράδειγμα 15: Χρήση παραμέτρων σε μακροεντολή .INCLUDE "m16def.inc" .DEF temp=R18 .MACRO TestMacro ; δήλωση μακροεντολής ldi temp,@0 ; πρώτη παράμετρο @0 ως τιμή του καταχωρητή temp inc temp ; αύξηση καταχωρητή temp brne macro_jump ; παράκαμψη επόμενης εντολής αν δεν προκύψει υπερχείλιση rjmp OVERFLOW ; Αλλιώς άλμα σε περίπτωση υπερχείλισης σε ετικέτα εκτός μακροεντολής macro_jump: ; Ετικέτα εντός μακροεντολής .ENDMACRO main: ser temp ; κύριο πρόγραμμα out DDRB,temp ; PortB(LEDs) ως έξοδος ; πέρασμα τιμής 0xFFστην μακροεντολή, αύξηση κατά μια μονάδα και άλμα στην overflow ; αφού προέκυψε υπερχείλιση και εισαγωγή μακροεντολής TestMacro(0xff) ; Η λειτουργία της μακροεντολής θα προκαλέσει υπερχείλιση (λόγω INC), ; και η επόμενη εντολή δεν θα εκτελεστεί. Εάν εκτελείτο θα άναβε όλα τα ; LEDs. outp: out PORTB,temp ; άναμμα των leds LOOP: rjmp LOOP OVERFLOW: ; λόγω υπερχείλισης θα εκτελεστεί ο παρακάτω κώδικας και ser temp ;τα LEDs PB.0 -PB.7 θα σβήσουν out PORTB,temp rjmp LOOP
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )