Spisu treści:

Samouczek asemblera AVR 2: 4 kroki
Samouczek asemblera AVR 2: 4 kroki

Wideo: Samouczek asemblera AVR 2: 4 kroki

Wideo: Samouczek asemblera AVR 2: 4 kroki
Wideo: ЯЗЫК АССЕМБЛЕРА за 3 МИНУТЫ 2024, Lipiec
Anonim
Samouczek asemblera AVR 2
Samouczek asemblera AVR 2

Ten samouczek jest kontynuacją „Samouczka AVR Assembler 1”

Jeśli nie przeszedłeś przez Samouczek 1, powinieneś przerwać teraz i zrobić to jako pierwszy.

W tym samouczku będziemy kontynuować nasze badanie programowania w asemblerze atmega328p używanego w Arduino.

Będziesz potrzebować:

  1. płytka stykowa Arduino lub zwykłe Arduino jak w tutorialu 1
  2. dioda LED
  3. rezystor 220 omów
  4. przycisk
  5. przewody łączące do wykonania obwodu na płytce stykowej
  6. Instrukcja zestawu instrukcji: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Karta katalogowa: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Kompletny zbiór moich samouczków można znaleźć tutaj:

Krok 1: Budowanie obwodu

Budowanie obwodu
Budowanie obwodu

Najpierw musisz skonstruować obwód, który będziemy studiować w tym samouczku.

Oto sposób, w jaki jest podłączony:

PB0 (cyfrowy pin 8) - LED - R (220 ohm) - 5V

PD0 (pin cyfrowy 0) - przycisk - GND

Możesz sprawdzić, czy dioda LED jest prawidłowo zorientowana, podłączając ją do GND zamiast PB0. Jeśli nic się nie dzieje, odwróć orientację i lampka powinna się zapalić. Następnie podłącz go ponownie do PB0 i kontynuuj. Zdjęcie pokazuje jak jest podłączone moje arduino płytki stykowej.

Krok 2: Pisanie kodu montażowego

Pisanie kodu montażowego
Pisanie kodu montażowego

Napisz następujący kod w pliku tekstowym o nazwie pushbutton.asm i skompiluj go za pomocą avra, tak jak to zrobiłeś w samouczku 1.

Zauważ, że w tym kodzie mamy mnóstwo komentarzy. Za każdym razem, gdy asembler zobaczy średnik, pominie resztę linii i przejdzie do następnej linii. Dobrą praktyką programistyczną (zwłaszcza w języku asemblerowym!) jest silne komentowanie kodu tak, że gdy powrócisz do niego w przyszłości będziesz wiedział, co robiłeś. Zamierzam dużo komentować rzeczy w pierwszych kilku samouczkach, abyśmy dokładnie wiedzieli, co się dzieje i dlaczego. Później, kiedy już trochę lepiej nauczymy się kodowania w asemblerze, skomentuję to nieco mniej szczegółowo.

;************************************

; napisane przez: 1o_o7; data: 23.10.2014;****************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; wyznacz rejestr roboczy r16 jako temp rjmp Init; pierwsza linia wykonana

W tym:

temp. sera; ustaw wszystkie bity w temp na jedynki. wyjście DDRB, temp; ustawienie bitu na 1 na I/O kierunku danych; zarejestruj się dla PortB, który jest DDRB, ustawia to; pin jako wyjście, 0 ustawi ten pin jako wejście; więc tutaj wszystkie piny PortB są wyjściami (ustawionymi na 1) ldi temp, 0b11111110; załaduj 'natychmiastową' liczbę do rejestru temp; gdyby to był tylko ld, to drugi argument; musiałaby być lokalizacją w pamięci zamiast DDRD, temp; mv temp do DDRD, wynik jest taki, że PD0 jest na wejściu; a reszta to wyjścia clr temp; wszystkie bity w temp są ustawione na 0's out PortB, temp; ustaw wszystkie bity (tj. piny) w porcie B na 0V ldi temp, 0b00000001; załaduj numer natychmiastowy do temp out PortD, temp; przenieś temp do PortD. PD0 posiada rezystor podciągający; (tj. ustawiony na 5 V), ponieważ ma 1 w tym bicie; reszta to 0V od zera.

Główny:

w temp, PinD; PinD przechowuje stan PortD, skopiuj to do temp; jeśli przycisk jest podłączony do PD0 będzie to; 0 gdy przycisk jest wciśnięty, 1 w przeciwnym razie ponieważ; PD0 ma rezystor podciągający, normalnie jest na 5V na wyjściu PortB, temp; wysyła zer i jedynki odczytane powyżej do PortB; oznacza to, że chcemy, aby dioda LED była podłączona do PB0,; gdy PD0 jest LOW, ustawia PB0 na LOW i włącza; na diodzie LED (ponieważ druga strona diody jest; podłączony do 5V i to ustawi PB0 na 0V więc; prąd będzie płynął) rjmp Main; pętla z powrotem do początku Main

Zauważ, że tym razem mamy nie tylko dużo więcej komentarzy w naszym kodzie, ale mamy również sekcję nagłówka, która zawiera informacje o tym, kto i kiedy to napisał. Reszta kodu jest również podzielona na sekcje.

Po skompilowaniu powyższego kodu należy go załadować do mikrokontrolera i sprawdzić, czy działa. Dioda LED powinna się włączyć, gdy naciskasz przycisk, a następnie zgasnąć, gdy puścisz. Pokazałem jak to wygląda na zdjęciu.

Krok 3: Analiza kodu linijka po linijce

Pominę wiersze, które są jedynie komentarzami, ponieważ ich cel jest oczywisty.

.nolist

.include "m328Pdef.inc".list

Te trzy wiersze zawierają plik zawierający definicje rejestru i bitu dla ATmega328P, który programujemy. Polecenie.nolist mówi asemblerowi, aby nie włączał tego pliku do pliku pushbutton.lst, który tworzy podczas asemblacji. Wyłącza opcję aukcji. Po dołączeniu pliku ponownie włączamy opcję listingu za pomocą polecenia.list. Powodem, dla którego to robimy, jest to, że plik m328Pdef.inc jest dość długi i tak naprawdę nie musimy go widzieć w pliku listy. Nasz asembler, avra, nie generuje automatycznie pliku z listą i gdybyśmy go chcieli, asemblowalibyśmy za pomocą następującego polecenia:

avra -l przycisk.lst przycisk.asm

Jeśli to zrobisz, wygeneruje plik o nazwie pushbutton.lst i jeśli przeanalizujesz ten plik, zobaczysz, że pokazuje on kod twojego programu wraz z dodatkowymi informacjami. Jeśli spojrzysz na dodatkowe informacje, zobaczysz, że wiersze zaczynają się od C:, po którym następuje względny adres w szesnastce miejsca, w którym kod jest umieszczony w pamięci. Zasadniczo zaczyna się od 000000 przy pierwszym poleceniu i rośnie z każdym kolejnym poleceniem. Druga kolumna po względnym miejscu w pamięci to kod szesnastkowy polecenia, po którym następuje kod szesnastkowy argumentu polecenia. Lista plików zostanie omówiona dalej w przyszłych samouczkach.

.def temp = r16; wyznacz rejestr roboczy r16 jako temp

W tej linii używamy dyrektywy assemblera ".def" do zdefiniowania zmiennej "temp" jako równej "rejestrowi roboczemu" r16. Użyjemy rejestru r16 jako tego, który przechowuje liczby, które chcemy skopiować do różnych portów i rejestrów (do których nie można zapisać bezpośrednio).

Ćwiczenie 1: Spróbuj skopiować liczbę binarną bezpośrednio do portu lub specjalnego rejestru, takiego jak DDRB i zobacz, co się stanie, gdy spróbujesz złożyć kod.

Rejestr zawiera bajt (8 bitów) informacji. Zasadniczo jest to zwykle zbiór zatrzasków SR, z których każdy jest "bitem" i zawiera 1 lub 0. Możemy o tym omówić (a nawet zbudować jeden!) w dalszej części tej serii. Być może zastanawiasz się, co to jest „rejestr roboczy” i dlaczego wybraliśmy r16. Omówimy to w przyszłym samouczku, kiedy zagłębimy się w grzęzawisko wnętrza chipa. Na razie chcę, abyś zrozumiał, jak robić takie rzeczy, jak pisanie kodu i programowanie fizycznego sprzętu. Wtedy będziesz miał układ odniesienia z tego doświadczenia, który ułatwi zrozumienie właściwości pamięci i rejestru mikrokontrolera. Zdaję sobie sprawę, że większość podręczników wprowadzających i dyskusji robi to na odwrót, ale odkryłem, że granie w grę wideo przez jakiś czas, aby uzyskać globalną perspektywę przed przeczytaniem instrukcji obsługi, jest znacznie łatwiejsze niż najpierw przeczytanie instrukcji.

rjmp Init; pierwsza linia wykonana

Ta linia jest "względnym skokiem" do etykiety "Init" i nie jest tutaj potrzebna, ponieważ następne polecenie jest już w Init, ale dołączamy je do wykorzystania w przyszłości.

W tym:

temp. sera; ustaw wszystkie bity w temp na jedynki.

Po etykiecie Init wykonujemy polecenie „ustaw rejestr”. Ustawia to wszystkie 8 bitów w rejestrze „temp” (który przypominasz sobie r16) na jedynki. Więc temp zawiera teraz 0b11111111.

wyjście DDRB, temp; ustawienie bitu na 1 w rejestrze We/Wy kierunku danych

; dla PortB, którym jest DDRB, ustawia ten pin jako wyjście; 0 ustawi ten pin jako wejście; więc tutaj wszystkie piny PortB są wyjściami (ustawione na 1)

Rejestr DDRB (rejestr kierunku danych dla PortB) mówi, które piny na PortB (tj. PB0 do PB7) są oznaczone jako wejście, a które są oznaczone jako wyjście. Ponieważ mamy pin PB0 podłączony do naszej diody LED, a reszta nie jest podłączona do niczego, ustawimy wszystkie bity na 1, co oznacza, że wszystkie są wyjściami.

temperatura ldi, 0b11111110; załaduj `natychmiastową' liczbę do rejestru tymczasowego

; gdyby był tylko ld, to drugi argument byłby; musi być miejscem w pamięci

Ta linia ładuje liczbę binarną 0b11111110 do rejestru temp.

wyjście DDRD, temp; mv temp do DDRD, wynik jest taki, że PD0 jest wejściem i

; reszta to wyjścia

Teraz ustawiamy rejestr kierunku danych dla PortD z temp, ponieważ temp nadal zawiera 0b11111110, widzimy, że PD0 zostanie wyznaczony jako pin wejściowy (ponieważ w skrajnym prawym miejscu jest 0), a reszta jest oznaczona jako wyjścia, ponieważ istnieją Jestem w tych miejscach.

klr temp; wszystkie bity w temp są ustawione na 0

wyjście PortB, temp; ustaw wszystkie bity (tj. piny) w porcie B na 0V

Najpierw „czyścimy” rejestr temp, czyli ustawiamy wszystkie bity na zero. Następnie kopiujemy to do rejestru PortB, który ustawia 0V na wszystkich tych pinach. Zero na bicie PortB oznacza, że procesor utrzyma ten pin na 0V, a jedynka na bicie spowoduje ustawienie tego pinu na 5V.

Ćwiczenie 2: Użyj multimetru, aby sprawdzić, czy wszystkie piny na porcie B są rzeczywiście zerowe. Czy z PB1 dzieje się coś dziwnego? Masz pomysł, dlaczego tak jest? (podobne do ćwiczenia 4 poniżej, a następnie postępuj zgodnie z kodem…) Ćwiczenie 3: Usuń powyższe dwie linie z kodu. Czy program nadal działa poprawnie? Czemu?

ldi temp, 0b0000001; załaduj numer natychmiastowy do temp

wyjście PortD, temp; przenieś temp do PortD. PD0 jest na 5V (ma rezystor podciągający); ponieważ ma 1 w tym bicie, reszta to 0V. Ćwiczenie 4: Usuń powyższe dwie linie z kodu. Czy program nadal działa poprawnie? Czemu? (Różni się to od ćwiczenia 3 powyżej. Zobacz diagram pinów. Jakie jest domyślne ustawienie DDRD dla PD0? (Patrz strona 90 arkusza danych

Najpierw "załaduj natychmiast" numer 0b00000001 do temp. Część „natychmiastowa” istnieje, ponieważ ładujemy prostą liczbę do temp, a nie wskaźnik do lokalizacji w pamięci zawierającej liczbę do załadowania. W takim przypadku użylibyśmy po prostu "ld" zamiast "ldi". Następnie wysyłamy ten numer do PortD, który ustawia PD0 na 5V, a resztę na 0V.

Teraz ustawiliśmy piny jako wejście lub wyjście i ustawiliśmy ich stany początkowe jako 0V lub 5V (NISKI lub WYSOKI) i teraz wchodzimy do naszego programu "pętla".

Główny: w temp, PinD; PinD przechowuje stan PortD, skopiuj to do temp

; jeśli przycisk jest podłączony do PD0 to będzie to; 0 gdy przycisk jest wciśnięty, 1 w przeciwnym razie ponieważ; PD0 ma rezystor podciągający, zwykle wynosi 5V

Rejestr PinD zawiera aktualny stan pinów PortD. Na przykład, jeśli podłączyłeś przewód 5V do PD3, to w następnym cyklu zegara (co dzieje się 16 milionów razy na sekundę, ponieważ mikrokontroler jest podłączony do sygnału zegara 16MHz) bit PinD3 (z aktualnego stanu PD3) zmieniłoby się na 1 zamiast 0. Więc w tym wierszu kopiujemy aktualny stan pinów do temp.

wyjście PortB, temp; wysyła zera i jedynki odczytane powyżej do PortB

; oznacza to, że chcemy, aby dioda LED była podłączona do PB0, więc; gdy PD0 jest NISKI, ustawi PB0 na NISKI i obróci się; na diodzie (druga strona diody jest podłączona; do 5V i to ustawi PB0 na 0V więc prąd płynie)

Teraz wysyłamy stan pinów w PinD na wyjście PortB. W praktyce oznacza to, że PD0 wyśle 1 do PortD0, chyba że zostanie naciśnięty przycisk. W takim przypadku, ponieważ przycisk jest podłączony do masy, pin będzie miał 0 V i wyśle 0 do PortB0. Teraz, jeśli spojrzysz na schemat obwodu, 0 V na PB0 oznacza, że dioda LED będzie się świecić, ponieważ po drugiej stronie jest napięcie 5 V. Jeśli nie naciskamy przycisku tak, że 1 jest wysyłane do PB0, oznacza to, że mamy 5V na PB0 i 5V po drugiej stronie diody, więc nie ma różnicy potencjałów i nie płynie prąd, a więc Dioda LED nie będzie świecić (w tym przypadku jest to dioda LED, która jest diodą, a więc prąd płynie tylko w jednym kierunku niezależnie od tego, ale cokolwiek).

rjmp Główny; pętle z powrotem do Start

Ten skok względny zapętla nas z powrotem do etykiety Main: i ponownie sprawdzamy PinD i tak dalej. Sprawdzanie co 16 milionowych sekundy, czy przycisk jest wciśnięty i odpowiednio ustawia PB0.

Ćwiczenie 5: Zmodyfikuj swój kod tak, aby dioda LED była podłączona do PB3 zamiast PB0 i zobacz, czy działa. Ćwiczenie 6: Podłącz diodę LED do GND zamiast 5V i odpowiednio zmodyfikuj swój kod.

Krok 4: Wniosek

W tym samouczku dokładniej zbadaliśmy język asemblera dla ATmega328p i dowiedzieliśmy się, jak sterować diodą LED za pomocą przycisku. W szczególności nauczyliśmy się następujących poleceń:

ser register ustawia wszystkie bity rejestru na jedynki

rejestr clr ustawia wszystkie bity rejestru na 0's

w rejestrze, rejestr we/wy kopiuje numer z rejestru we/wy do rejestru roboczego

W następnym samouczku przyjrzymy się strukturze ATmega328p oraz zawartych w niej różnych rejestrach, operacjach i zasobach.

Zanim przejdę do tych samouczków, poczekam i zobaczę poziom zainteresowania. Jeśli jest wielu ludzi, którzy faktycznie czerpią przyjemność z nauki kodowania programów dla tego mikroprocesora w języku asemblerowym, to będę kontynuował i konstruował bardziej skomplikowane obwody i używał bardziej niezawodnego kodu.

Zalecana: