Przykład 1 - Przerzutnik RSNajprostszym przykładem wkorzystania portów wejścia/wyjścia jest układ, który pełniłby funkcję przerzutnika RS. Naciskając przycisk S1 zapalamy kropkę dziesiętną wyświetlacza, a naciskając S2 gasimy ją. Algorytm działania programu jest przedstawiony na poniższym rysunku :
Kod Ľrdłowy programu przedstawiony jest poniżej :
; Kurs asemblera mikrokontrolerów AVR;; Przykład 1 - przerzutnik RS
.include "2313def.inc".def acc = r16 ; nadanie rejestrowi r16 nazwy symbolicznej.cseg.org 0x00
ldi acc, 0b11111111 ; załaduj do acc liczbę 255 (0xff)out DDRB, acc ; wpisanie do DDRB samych jedynek; powoduje ustawienie pinów jako wyjśćout PORTB, acc
ldi acc, 0b1111100 ; out DDRD, acc ; piny D0 i D1 są ustawione jako wejścia; pozostałe jako wyjścialdi acc, 0b1110111 out PORTD, acc ; włączony pierwszy wyświetlacz, wejścia; podciągnięte do zasilania
sprawdz_klawisze:sbis PIND, 0 ; jeśli pin D0 = 1, to pomiń następną instrukcjęcbi PORTB, 7sbis PIND, 1 ; jeśli pin D1 = 1, to pomiń następną instrukcjęsbi PORTB, 7 rjmp sprawdz_klawisze ; skocz do początku sprawdzania klawiszy
Instrukcja LDI (Load Immediate) powoduje załadowanie do rejestru roboczego (r16-r31) ośmiobitowej stałej.
Składnia:
Operandy:
Licznik rozkazów:
(i) LDI Rd
16 <= d <=31, 0 <= K <= 255
PC <- PC + 1
Kod instrukcji:1110 KKKK dddd KKKK
Komentarz:W naszym przypadku ładujemy do rejestru r16 liczbę 255 (wpisujemy same jedynki).Instrukcja OUT (Store Register to I/O Location) powoduje zapamiętanie zawartości rejestru roboczego (r0-r31) w rejestrze z obszaru rejestrów wejścia-wyjścia.
Składnia
Operandy
Licznik rozkazów
(i) OUT A,Rr
0 <= r<= 31 0 <= A<= 63
PC = PC + 1
Kod instrukcji:1011 1Aar rrrr AAAA
Komentarz:Liczbę uprzednio wpisaną do rejestru r16 wpisujemy do rejestru DDRB, w celu określenia funkcji, jaką mają pełnić poszczególne piny portu B. Niestety nie można bezpośrednio załadować liczby do rejestrów wejścia-wyjścia. Następnie wpisujemy tą samą liczbę do rejestru PORTB, co spowoduje ustawienie na wszystkich pinach stanu wysokiego. Wszystkie segmenty wyświetlacza będą zgaszone.
Instrukcja SBIS (Skip if Bit in I/O Register is Set) powoduje pominięcie następnej instrukcji, jeżeli bit w rejestrze wejścia-wyjścia jest ustawiony.
(i)SBIS A,b
0 <=A <= 31, 0 <= b <= 7
PC = PC + 1, Warunek nie spełnionyPC = PC + 2, Pominięcie instrukcji jednosłowowejPC = PC + 3, Pominięcie instrukcji dwusłowowej
Kod instrukcji:1001 1011 AAAA Abbb
Komentarz:Piny D0 i D1 są skonfigurowane jako wejścia i podciągnięte do plusa zasilania przez wewnętrzne rezystory. Uaktywnienie wejścia jest interpretowane jako zwarcie go do masy, tak więc stan niski na danej końcówce wejściowej oznacza naciśnięcie przycisku. Jeżeli pin wejściowy jest w stanie wysokim, oznacza to, że przycisk nie został naciśnięty. Tak więc nie powinna zostać podjęta żadna akcja, czyli mikrokontroler pomija kolejną instrukcję. W naszym przypadku reakcja na naciśnięcie przycisku jest obsłużona przez jedną instrukcję. W przypadku, gdy konieczne jest zastosowanie większej liczby instrukcji, należy zorganizować fragment obsługi w trochę inny sposób. Mianowicie za instrukcją SBIS należy umieścić instrukcję wywołującą procedurę, np. RCALL, i cały fragment kodu obsługujący sposób reakcji na naciśnięcie przycisku należy umiescić w tej procedurze.
Instrukcja CBI (Clear Bit in I/O Register) powoduje wyzerowanie bitu w rejestrze wejścia-wyjścia.
CBI A, b
0 <= A <= 31, 0 <= b <= 7
PC <= PC + 1
Kod instrukcji:1001 1000 AAAA Abbb
Komentarz:Chcemy, aby po naciśnięciu przycisku S1 zapalona została kropka dziesiętna na wyświetlaczu. Aby to osiągnąć należy ustawić na pinie B7 stan niski.
Instrukcja SBI (Set Bit in I/O Register) powoduje ustawienie bitu w rejestrze wejścia-wyjścia.
SBI A, b
Kod instrukcji:1001 1010 AAAA Abbb
Komentarz:Po naciśnięciu przycisku S2 należy zgasić wcześniej zapaloną kropkę. Realizujemy to poprzez ustawienie pinu B7 w stan wysoki.
Instrukcja RJMP (Relative Jump) jest instrukcją bezwarunkowego skoku względnego pod adres z zakresu od PC - 2K + 1 do PC + 2K.
Operand
Stos
(i) RJMP k
2K <=k < 2K
PC =PC + k + 1
Niezmieniony
Kod instrukcji:1100 kkkk kkkk kkkk
Czas wykonania tej instrukcji - 2 cykle zegarowe.
Program zajmuje 24 bajty w pamięci programu. Dla porównania program realizujący identyczną funkcję napisany w Bascomie zajmuje 138 bajtów, czyli prawie sześciokrotnie więcej! Ukazuje to ogromną przewagę asemblera nad Bascomem, czy w ogóle nad językami wyższego poziomu. Poza tym progam napisany w asemblerze wykonuje się szybciej, a już na pewno mamy pełną kontrolę nad czasem wykonania poszczególnych fragmentów programu, czego nie można powiedzieć o Bascomie.
Przykład 2 - Układ czasowyPo naciśnięciu przycisku S1 kropka wyświetlacza zapala się na ok. 1 sekundę. Algorytm działania programu przedstawiono na poniższym rysunku :
Kod źródłowy zamieszczony jest poniżej :
; Kurs asemblera mikrokontrolerów AVR; ; Przykład 2 - Układ czasowy
ldi acc, 0b11111111 ; załaduj do acc liczbę 255 (0xff)out DDRB, acc ; wpisanie do DDRB samych jedynek; powoduje ustawienie pinów jako wyjścieout PORTB, acc
ldi acc, 0b1111100 ; out DDRD, acc ; piny D0 i D1 są ustawione jako wejścia; pozostałe jako wyjścialdi r16, 0b1110111 out PORTD, accldi acc, 127 ;out SPL, acc ; ustawienie wskaˇnika stosu na 127sprawdz_klawisze:sbis PIND, 0 ; jeśli przycisk S1 nie jest wciśnięty; to pomiń następną instrukcjęrcall led ; w przeciwnym wypadku wywołaj procedurę; zapalającą diodęrjmp sprawdz_klawisze ; powróć na początek sprawdzania klawiszyled:cbi PORTB, 7 ; zapal kropkę wyświetlaczaldi acc, 250 ; załaduj do acc czas opóˇnieniarcall waitms ; i wywołaj procedurę opóˇniającąrcall waitms ; czterokrotne wywołanie opóˇnienia rcall waitms ; 250ms da w sumie opóˇnienie ok 1srcall waitms ;sbi PORTB, 7 ret
; Przybliżone czasy opóźnień są podane dla; rezonatora 4MHz.def licz1 = r20.def licz2 = r21.def licz3 = r22waitms:mov licz3, acc ; ustaw czas opóĽnienia; powtórz n razy pętlę L,; co da opóˇnienie ok. n * 1 ms L:; powtórz 10 razy pętlę L0,; co da opóĽnienie ok 1ms ldi licz2, 10 L0:; powtórz 100 razy pętlę L1,; co da opóĽnienie ok. 100 us ldi licz1, 100 L1: nop ; 1 cykl dec licz1 ; 1 cykl brne l1 ; 2 cykle; koniec pętli L1 dec licz2 brne l0; koniec pętli L0 dec licz3 brne L; koniec pętli L ret
Pierwsza część programu składa się z instrukcji poznanych w przykładzie wcześniejszym, więc nie będę ich tutaj ponownie opisywał.
Instrukcja MOV (Copy registers) kopiuje zawartość jednego rejestru do drugiego. Zawartość rejestru ˇródłowego nie ulega zmianie.
Składnia rozkazu
(i) MOV Rd,Rr
0 <= d <=31, 0 <= r <= 31
Kod operacji :
0010 11rd dddd rrrr
Komentarz:Wartość opóˇnienia przekazujemy przez rejestr r16 i kopiujemy jego zawartość do r22, czyli do licznika pętli. Takie rozwiązanie zostało zastosowane, w celu zachowania zawartości rejestru r16. Pozwoli to na kilkukrotne wywołąnie procedury opóˇniającej o tą samą wartość opóˇnienia bez konieczności każdorazowego ładowania czasu opóˇnienia.
Instrukcja NOP (No operation) nie wykonuję żadnych czynności poza zwiększeniem zawartości licznika rozkazów o 1. Instrukcja jest wykorzystywana do generowania opóˇnień czasowych.
(i) NOP
brak
0000 0000 0000 0000
Komentarz:Pętla L1 ma wnosić opóˇnienie 100us. Ponieważ cykl rozkazowy przy częstotliwości taktowania mikrokontrolera równej 4MHz wynosi 250ns jeden przebieg pętli musi wnieść opóˇnienie 1us, czyli 4 cykli. Ponieważ instrukcje DEC r20 i BRNE L1 razem trwają 3 cykle (przy niespełnieniu waruknu instrukcji BRNE) to za pomocą intrukcji NOP wstawiamy ten jeden brakujący cykl.
Instrukcja BRNE (Branch if not equal) wykonuje skok warunkowy, jeśli flaga zera (Z) jest wyzerowana.
BRNE k
-64 <= k <= +63
KeithFlint