Sadržaj:

AVR Assembler Tutorial 3: 9 Koraci
AVR Assembler Tutorial 3: 9 Koraci

Video: AVR Assembler Tutorial 3: 9 Koraci

Video: AVR Assembler Tutorial 3: 9 Koraci
Video: CodevisionAVR - кнопки и eeprom (урок 9) 2024, Novembar
Anonim
Vodič za AVR asembler 3
Vodič za AVR asembler 3

Dobro došli u vodič 3!

Prije nego što počnemo, želim iznijeti filozofsku poentu. Ne bojte se eksperimentirati sa sklopovima i kodom koji konstruiramo u ovim vodičima. Promijenite žice, dodajte nove komponente, izvadite komponente, promijenite redove koda, dodajte nove redove, izbrišite linije i pogledajte što će se dogoditi! Vrlo je teško bilo što slomiti, a ako to učinite, koga briga? Ništa što koristimo, uključujući mikrokontroler, nije jako skupo i uvijek je edukativno vidjeti kako stvari mogu propasti. Ne samo da ćete sljedeći put saznati šta ne treba učiniti, nego je, što je još važnije, znati zašto to ne učiniti. Ako ste išta poput mene, kad ste bili klinac i dobili ste novu igračku, nije prošlo mnogo vremena prije nego što ste je dobili u komadima da vidite šta ju je otkucalo? Ponekad je igračka završila nepopravljivo oštećena, ali ništa strašno. Dozvoljavanje djetetu da istraži svoju znatiželju čak do polomljenih igračaka ono je što ga pretvara u naučnika ili inženjera umjesto u mašinu za pranje posuđa.

Danas ćemo ožičiti vrlo jednostavno kolo, a zatim ćemo se malo pozabaviti teorijom. Žao nam je zbog ovoga, ali potrebni su nam alati! Obećavam da ćemo to nadoknaditi u vodiču 4 gdje ćemo raditi ozbiljnije izgradnje kola, a rezultat će biti prilično kul. Međutim, način na koji trebate izvesti sve ove vodiče je na vrlo spor, kontemplativan način. Ako samo prođete kroz nju, napravite krug, kopirate i zalijepite kôd i pokrenete ga, onda će to sigurno uspjeti, ali ništa nećete naučiti. Morate razmisliti o svakom retku. Pauza. Eksperiment. Izumiti. Ako to učinite na taj način, do kraja petog vodiča nećete graditi kul stvari i više vam neće trebati podučavanje. Inače jednostavno gledate, a ne učite i stvarate.

U svakom slučaju, dovoljno filozofije, počnimo!

U ovom vodiču trebat će vam:

  1. vašu ploču za izradu prototipova
  2. LED
  3. spojne žice
  4. otpornik oko 220 do 330 ohma
  5. Priručnik s uputama: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Tehnički list: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. drugi kristalni oscilator (opcionalno)

Evo veze do kompletne zbirke vodiča:

Korak 1: Konstruisanje kola

Konstruisanje kola
Konstruisanje kola

Krug u ovom vodiču je krajnje jednostavan. U osnovi ćemo napisati "blink" program pa nam je potrebno samo sljedeće.

Priključite LED na PD4, zatim na otpornik od 330 ohma, pa na uzemljenje. tj.

PD4 - LED - R (330) - GND

i to je to!

Teorija će biti teška, ali …

Korak 2: Zašto su nam potrebni komentari i datoteka M328Pdef.inc?

Mislim da bismo trebali početi pokazujući zašto su datoteka uključivanja i komentari korisni. Ništa od njih zapravo nije potrebno i možete pisati, sastavljati i učitavati kôd na isti način bez njih i on će raditi savršeno dobro (iako bez datoteke za uključivanje možete dobiti neke žalbe od asemblera - ali bez grešaka)

Evo koda koji ćemo danas napisati, osim što sam uklonio komentare i datoteku include:

.uređaj ATmega328P

.org 0x0000 jmp. cbi 0x0b, 0x04 rcall c rjmp bc: clr r17 d: cpi r17, 0x1e brne d ret e: inc r17 cpi r17, 0x3d brne PC+2 clr r17 reti

prilično jednostavno zar ne? Haha. Ako ste sastavili i učitali ovu datoteku, LED dioda će treperiti brzinom od 1 treptaja u sekundi, pri čemu će treptaj trajati 1/2 sekunde, a pauza između treptaja će trajati 1/2 sekunde.

Međutim, gledanje u ovaj kod nije nimalo prosvjetljujuće. Ako biste pisali ovakav kôd i htjeli biste ga izmijeniti ili prenamijeniti u budućnosti, bilo bi vam teško.

Pa stavimo komentare i uvrstimo datoteku natrag kako bismo to mogli smisliti.

Korak 3: Blink.asm

Evo koda o kojem ćemo danas razgovarati:

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

; napisao: 1o_o7; datum:; verzija: 1.0; datoteka sačuvana kao: blink.asm; za AVR: atmega328p; frekvencija takta: 16MHz (opcionalno); ************************************; Funkcija programa: ---------------------; odbrojava sekunde trepćući LED diodu;; PD4 - LED - R (330 ohma) - GND;; --------------------------------------.nolist.include "./m328Pdef.inc".list; ==============; Deklaracije:.def temp = r16.def overflows = r17.org 0x0000; memorija (PC) lokacija rukovaoca resetovanja rjmp Resetovanje; jmp košta 2 ciklusa procesora, a rjmp samo 1; pa osim ako ne morate preskočiti više od 8 kB; potreban vam je samo rjmp. Stoga samo neki mikrokontroleri; imati rjmp a ne jmp.org 0x0020; memorijska lokacija upravljača preljeva Timer0 rjmp overflow_handler; idite ovdje ako dođe do prekida prelijevanja timer0; ============ Reset: ldi temp, 0b00000101 out TCCR0B, temp; postavite bitove za odabir sata CS00, CS01, CS02 na 101; ovo postavlja mjerač vremena 0, TCNT0 u način rada FCPU/1024; pa otkucava na CPU freq/1024 ldi temp, 0b00000001 sts TIMSK0, temp; postavite bit za omogućavanje prekida prekidanja tajmera (TOIE0); registra tajmer prekida maske (TIMSK0) sei; omogući globalne prekide - ekvivalent "sbi SREG, I" clr temp out TCNT0, temp; inicijalizirati mjerač vremena/brojač na 0 sbi DDRD, 4; postavite PD4 na izlaz; ======================; Glavni sadržaj programa: blink: sbi PORTD, 4; uključite LED na PD4 kašnjenju poziva; kašnjenje će biti 1/2 sekunde cbi PORTD, 4; isključiti LED na kašnjenju poziva PD4; kašnjenje će biti 1/2 sekunde rjmp treptaja; petlja nazad na kašnjenje početka: clr se preliva; postavite prelive na 0 sec_count: cpi prelivanja, 30; uporedi broj preljeva i 30 brnih sec_count; grananje natrag na sec_count ako nije jednako ret; ako se dogodilo 30 prelivanja, vratite se na treptanje overflow_handler: inc prelivi; dodaj 1 varijabli overflows cpi overflows, 61; uporedi sa 61 brne PC+2; Brojač programa + 2 (preskočite sljedeći red) ako nisu jednaki clr preljevi; ako se dogodio 61 preliv, resetujte brojač na nulu reti; povratak iz prekida

Kao što vidite, moji komentari su sada malo kraći. Jednom kada saznamo koje naredbe u skupu uputa radimo, ne moramo to objašnjavati u komentarima. Moramo samo objasniti šta se dešava sa stanovišta programa.

Po komadu ćemo raspravljati o tome što sve ovo radi, ali prvo pokušajmo steći globalnu perspektivu. Glavni deo programa funkcioniše na sledeći način.

Prvo postavljamo bit 4 za PORTD sa "sbi PORTD, 4" koji šalje 1 do PD4 koji postavlja napon na 5V na tom pinu. Ovo će uključiti LED diodu. Zatim prelazimo na potprogram "odgode" koja odbrojava 1/2 sekunde (kasnije ćemo objasniti kako to radi). Zatim se vraćamo na trepćući i jasan bit 4 na PORTD -u koji postavlja PD4 na 0V i stoga isključuje LED. Zatim odlažemo još 1/2 sekunde, a zatim se ponovo vraćamo na početak treptanja sa "rjmp blink".

Trebali biste pokrenuti ovaj kôd i vidjeti da li radi ono što bi trebao.

I evo ga! To je sve što ovaj kod fizički radi. Unutrašnja mehanika onoga što mikrokontroler radi je malo uključenija i zato radimo ovaj vodič. Pa razgovarajmo redom o svakom odjeljku.

Korak 4:.org Direktive o asembleru

Već znamo što.nolist,.list,.include i.def asemblerske direktive rade iz naših prethodnih vodiča, pa pogledajmo prvo 4 retka koda koji dolaze nakon toga:

.org 0x0000

jmp Reset.org 0x0020 jmp overflow_handler

Naredba.org govori asembleru gdje u "Memoriji programa" staviti sljedeću naredbu. Dok se vaš program izvršava, "Brojač programa" (skraćeno kao PC) sadrži adresu trenutne linije koja se izvršava. Dakle, u ovom slučaju kada je računar na 0x0000 vidjet će naredbu "jmp Reset" koja se nalazi na toj memorijskoj lokaciji. Razlog zašto želimo postaviti jmp Reset na tu lokaciju je taj što kada program počne ili se čip resetira, PC počinje izvršavati kod na ovom mjestu. Dakle, kao što vidimo, upravo smo mu rekli da odmah "skoči" na odjeljak s oznakom "Resetiraj". Zašto smo to učinili? To znači da se posljednja dva gornja retka samo preskaču! Zašto?

Pa tu stvari postaju zanimljive. Sada ćete morati otvoriti preglednik PDF -a sa potpunim podatkovnim listom ATmega328p na koji sam ukazao na prvoj stranici ovog vodiča (zato je to stavka 4 u odjeljku "trebat će vam"). Ako je vaš ekran premalen ili imate već otvoreno previše prozora (kao što je slučaj sa mnom), mogli biste učiniti ono što ja radim i staviti ga na Ereader ili na svoj Android telefon. Koristit ćete ga cijelo vrijeme ako planirate pisati sklopni kod. Zgodno je to što su svi mikrokontroleri organizirani na vrlo slične načine, pa će vam se, kad se naviknete čitati podatkovne tablice i kodirati ih, učiniti gotovo trivijalnim učiniti isto za drugi mikrokontroler. Dakle, zapravo učimo kako koristiti sve mikrokontrolere, a ne samo atmega328p.

Ok, okrenite se na stranicu 18 u podatkovnom listu i pogledajte sliku 8-2.

Ovako se postavlja memorija programa u mikrokontroleru. Možete vidjeti da počinje s adresom 0x0000 i podijeljen je u dvije sekcije; flash odjeljak aplikacije i boot flash odjeljak. Ako se kratko osvrnete na stranicu 277 tablica 27-14, vidjet ćete da odjeljak flash aplikacije zauzima lokacije od 0x0000 do 0x37FF, a odjeljak za pokretanje računara zauzima preostale lokacije od 0x3800 do 0x3FFF.

Vježba 1: Koliko lokacija postoji u memoriji programa? Tj. pretvorite 3FFF u decimalni broj i dodajte 1 budući da počinjemo brojati na 0. Budući da je svaka memorijska lokacija široka 16 bita (ili 2 bajta), koliki je ukupan broj bajtova memorije? Sada to pretvorite u kilobajte, imajući na umu da u kilobajtu ima 2^10 = 1024 bajta. Odeljak boot flash -a ide od 0x3800 do 0x37FF, koliko je ovo kilobajta? Koliko kilobajta memorije nam preostaje za pohranu našeg programa? Drugim riječima, koliko veliki program može biti? Konačno, koliko linija koda možemo imati?

U redu, sada kada znamo sve o organizaciji memorije flash programa, nastavimo s raspravom o.org izjavama. Vidimo da prva memorijska lokacija 0x0000 sadrži naše upute za prelazak na odjeljak koji smo označili kao Reset. Sada vidimo šta radi izraz ".org 0x0020". Kaže da želimo da instrukcija u sljedećem redu bude smještena na memorijsku lokaciju 0x0020. Uputstvo koje smo tamo postavili je skok na odjeljak u našem kodu koji smo označili kao "overflow_handler" … zašto bismo, dovraga, tražili da se ovaj skok postavi na memorijsku lokaciju 0x0020? Da bismo to saznali, okrećemo se stranici 65 u podatkovnom listu i pregledavamo tablicu 12-6.

Tablica 12-6 je tablica "Reset i Interrupt Vectors" i pokazuje gdje će PC otići kada primi "prekid". Na primjer, ako pogledate vektorski broj 1. "Izvor" prekida je "RESET" koji je definiran kao "vanjski pin, resetiranje pri uključivanju, reset smeđe boje i resetiranje nadzornog sistema", što znači, ako postoji kada se to dogodi našem mikrokontroleru, PC će početi izvršavati naš program na lokaciji programske memorije 0x0000. Šta je sa našom.org direktivom? Pa, postavili smo naredbu na memorijsku lokaciju 0x0020 i ako pogledate dolje u tablicu vidjet ćete da će se, ako dođe do preljeva Timer/Counter0 (dolazi iz TIMER0 OVF), izvršiti sve što se nalazi na lokaciji 0x0020. Dakle, kad god se to dogodi, računar će skočiti na mjesto koje smo označili kao "overflow_handler". Kul zar ne? Ubrzo ćete vidjeti zašto smo to učinili, ali prvo završimo ovaj korak vodiča sa strane.

Ako želimo učiniti naš kôd urednijim i urednijim, zaista bismo trebali zamijeniti 4 retka o kojima trenutno raspravljamo sljedećim (vidi stranicu 66):

.org 0x0000

rjmp Reset; PC = 0x0000 reti; PC = 0x0002 reti; PC = 0x0004 reti; PC = 0x0006 reti; PC = 0x0008 reti; PC = 0x000A… reti; PC = 0x001E jmp overflow_handler: PC = 0x0020 reti: PC = 0x0022… reti; PC = 0x0030 reti; PC = 0x0032

Tako da ako dođe do datog prekida, on će samo "reti" što znači "povratak iz prekida" i ništa se drugo neće dogoditi. Ali ako nikada ne omogućimo ove različite prekide, oni se neće koristiti i programski kod možemo staviti na ta mjesta. U našem trenutnom programu "blink.asm" omogućit ćemo samo prekid prelivanja timer0 (i naravno prekid resetiranja koji je uvijek omogućen), tako da se nećemo zamarati ostalim.

Kako onda možemo "omogućiti" prekid prelivanja timer0? … to je tema našeg sljedećeg koraka u ovom vodiču.

Korak 5: Odbrojavanje/brojač 0

Tajmer/brojač 0
Tajmer/brojač 0

Pogledajte gornju sliku. Ovo je proces donošenja odluka "PC -a" kada neki vanjski utjecaj "prekine" tok našeg programa. Prva stvar koju učini kad dobije signal izvana da je došlo do prekida je provjera da li smo postavili bit "interrupt enable" za tu vrstu prekida. Ako nismo, onda samo nastavlja izvršavati naš sljedeći red koda. Ako smo postavili taj bit za omogućavanje prekida (tako da na toj lokaciji bita postoji 1 umjesto 0), tada će provjeriti jesmo li omogućili "globalne prekide", ako ne, opet će ići na sljedeći red koda i nastavi. Ako smo omogućili i globalne prekide, ona će otići na lokaciju programske memorije te vrste prekida (kao što je prikazano u Tablici 12-6) i izvršiti sve naredbe koje smo tamo postavili. Pa da vidimo kako smo sve ovo implementirali u naš kod.

Odjeljak s oznakom Reset našeg koda počinje sa sljedeća dva retka:

Resetovati:

ldi temp, 0b00000101 out TCCR0B, temp

Kao što već znamo, ovo učitava u temp (tj. R16) broj koji slijedi, a to je 0b00000101. Zatim zapisuje ovaj broj u registar pod nazivom TCCR0B pomoću naredbe "out". Šta je ovaj registar? Pa, prijeđimo na stranicu 614 lista s podacima. Ovo je u sredini tabele koja rezimira sve registre. Na adresi 0x25 pronaći ćete TCCR0B. (Sada znate odakle je došao red "out 0x25, r16" u mojoj verziji koda bez komentara). Vidimo prema gore navedenom segmentu koda da smo postavili 0 -ti bit i 2 -i bit i izbrisali sve ostalo. Gledajući tablicu možete vidjeti da to znači da smo postavili CS00 i CS02. Pređimo sada na poglavlje u listu sa podacima „8-bitni mjerač vremena/brojač0 sa PWM-om“. Konkretno, idite na stranicu 107 tog poglavlja. Vidjet ćete isti opis registra "Registar mjerača vremena/brojača B" (TCCR0B) koji smo upravo vidjeli u sažetoj tablici registra (pa smo mogli doći ovdje, ali htio sam da vidite kako se koriste zbirne tablice za buduću upotrebu). Tehnički list nastavlja da daje opis svakog od bitova u tom registru i šta oni rade. Za sada ćemo sve to preskočiti i okrenuti stranicu na tablicu 15-9. Ova tablica prikazuje "Opis bita za odabir sata". Sada gledajte dolje u tu tablicu dok ne pronađete liniju koja odgovara bitovima koje smo upravo postavili u tom registru. Linija kaže "clk/1024 (od predskalera)". To znači da želimo da Timer/Counter0 (TCNT0) otkucava brzinom koja je frekvencija procesora podijeljena na 1024. Budući da naš mikrokontroler napaja kristalni oscilator od 16 MHz, to znači da je brzina kojom naš CPU izvršava upute 16 miliona instrukcija u sekundi. Dakle, brzina koju će naš brojač TCNT0 otkucati je tada 16 miliona/1024 = 15625 puta u sekundi (pokušajte s različitim bit -ovima za odabir sata i vidite što će se dogoditi - sjećate li se naše filozofije?). Zadržimo broj 15625 u mislima za kasnije i pređimo na sljedeća dva retka koda:

ldi temp, 0b00000001

sts TIMSK0, temp

Ovo postavlja 0 -ti bit registra koji se zove TIMSK0 i briše sve ostalo. Ako pogledate stranicu 109 u podatkovnom listu, vidjet ćete da TIMSK0 označava "Registar maske tajmera/brojača prekida maske 0", a naš kôd je postavio 0 -ti bit koji se zove TOIE0 što znači "Omogućivanje prekidača mjerača vremena/brojača0" … Tamo! Sada vidite o čemu se radi. Sada imamo "bit prekida za omogućavanje postavljen" kako smo htjeli od prve odluke na našoj slici na vrhu. Dakle, sve što trebamo učiniti je omogućiti "globalne prekide" i naš će program moći odgovoriti na ovu vrstu prekida. Uskoro ćemo omogućiti globalne prekide, ali prije nego što to učinite možda vas je nešto zbunilo.. zašto sam dovraga upotrijebio naredbu "sts" za kopiranje u registar TIMSK0 umjesto uobičajenog "out"?

Kad god me vidite kako koristim uputstvo koje prije niste vidjeli, prvo što trebate učiniti je okrenuti se na stranicu 616 u podatkovnom listu. Ovo je "Sažetak skupa uputa". Sada pronađite uputu "STS" koju sam koristio. Kaže da uzima broj iz R registra (koristili smo R16) i "Spremi direktno na SRAM" lokaciju k (u našem slučaju TIMSK0). Pa zašto smo morali koristiti "sts" koji traje 2 ciklusa takta (vidi posljednju kolonu u tabeli) za spremanje u TIMSK0, a trebali smo samo "out", koji traje samo jedan ciklus sata, za pohranu u TCCR0B prije? Da bismo odgovorili na ovo pitanje, moramo se vratiti na tablicu sažetka registra na stranici 614. Vidite da je registar TCCR0B na adresi 0x25, ali i na (0x45), zar ne? To znači da se radi o registru u SRAM -u, ali je također i o određenoj vrsti registra koji se naziva "port" (ili i/o registar). Ako pogledate tablicu sažetaka uputa pored naredbe "out", vidjet ćete da uzima vrijednosti iz "radnih registara" poput R16 i šalje ih u PORT. Tako možemo koristiti "out" pri pisanju u TCCR0B i uštedjeti si ciklus takta. Ali sada potražite TIMSK0 u tablici registra. Vidite da ima adresu 0x6e. Ovo je izvan raspona portova (koji su samo prve 0x3F lokacije SRAM -a) pa se morate vratiti korištenju naredbe sts i poduzimanju dva takta CPU -a za to. Molimo vas da sada pročitate napomenu 4 na kraju tablice sažetka uputstava na stranici 615. Također primijetite da se svi naši ulazni i izlazni portovi, poput PORTD -a, nalaze pri dnu tablice. Na primjer, PD4 je bit 4 na adresi 0x0b (sada vidite odakle su sve stvari 0x0b u mom komentiranom komentaru!).. u redu, brzo pitanje: jeste li promijenili "sts" u "out" i vidite šta se dešava? Sjetite se naše filozofije! slomi to! ne vjerujte mi samo na riječ.

U redu, prije nego što nastavimo, okrenite se na stranicu 19 u tehničkom listu na minutu. Videćete sliku memorije podataka (SRAM). Prvih 32 registra u SRAM -u (od 0x0000 do 0x001F) su "radni registri opće namjene" R0 do R31 koje cijelo vrijeme koristimo kao varijable u našem kodu. Sljedećih 64 registra su I/O portovi do 0x005f (tj. Oni o kojima smo govorili koji imaju one adrese bez zagrada pored njih u tabeli registra koje možemo koristiti "out" naredbom umjesto "sts") Konačno sljedeći odjeljak SRAM -a sadrži sve ostale registre u zbirnoj tablici do adrese 0x00FF, i na kraju ostatak je interni SRAM. Hajdemo sada na trenutak na stranicu 12. Tamo vidite tablicu "radnih registara opće namjene" koju uvijek koristimo kao naše varijable. Vidite debelu liniju između brojeva R0 do R15, a zatim R16 do R31? Ta linija je razlog zašto uvijek koristimo R16 kao najmanji, a o tome ću detaljnije govoriti u sljedećem vodiču gdje će nam trebati i tri 16-bitna registra indirektnih adresa, X, Y i Z. Neću ipak uđite u to budući da nam to sada ne treba, a ovdje smo dovoljno zaglavljeni.

Vratite jednu stranicu na stranicu 11 u listu s podacima. Vidjet ćete dijagram registra SREG u gornjem desnom kutu? Vidite da se bit 7 tog registra zove "I". Sada idite dolje na stranicu i pročitajte opis Bit 7…. jej! To je bit za omogućavanje globalnog prekida. To je ono što moramo postaviti kako bismo prošli kroz drugu odluku u našem gornjem dijagramu i dopustili prekide tajmera/brojača u našem programu. Dakle, sljedeća linija našeg programa trebala bi glasiti:

sbi SREG, I

koji postavlja bit nazvan "I" u SREG registru. Međutim, umjesto ovoga koristili smo upute

sei

umjesto toga. Ovaj dio je toliko često postavljen u programima da su upravo napravili jednostavniji način za to.

U redu! Sada imamo prekide prelivanja spremni za pokretanje tako da će se naš "jmp overflow_handler" izvršavati kad god se pojavi.

Prije nego krenemo dalje, kratko pogledajte SREG registar (Statusni registar) jer je vrlo važan. Pročitajte šta svaka od zastavica predstavlja. Konkretno, mnoga uputstva koja koristimo će postavljati i provjeravati ove zastavice cijelo vrijeme. Na primjer, kasnije ćemo koristiti naredbu "CPI" što znači "uporedi odmah". Pogledajte tablicu sažetka uputa za ovu upute i primijetite koliko zastavica postavlja u koloni "zastavice". Ovo su sve zastavice u SREG -u i naš kôd će ih postaviti i stalno provjeravati. Uskoro ćete vidjeti primjere. Konačno, posljednji bit ovog odjeljka koda je:

clr temp

izlaz TCNT0, temp sbi DDRD, 4

Zadnji red ovdje je prilično očit. On samo postavlja četvrti bit registra smjerova podataka za PortD uzrokujući da PD4 bude OUTPUT.

Prva postavlja varijablu temp na nulu, a zatim je kopira u registar TCNT0. TCNT0 je naš mjerač vremena/brojač0. Ovo ga postavlja na nulu. Čim PC izvrši ovu liniju, timer0 će početi s nulom i brojat će 15625 puta svake sekunde. Problem je sljedeći: TCNT0 je "8-bitni" registar, zar ne? Dakle, koji je najveći broj koji može imati 8-bitni registar? Pa to je 0b11111111. Ovo je broj 0xFF. Što je 255. Vidite li šta se dešava? Tajmer se zip -om povećava 15625 puta u sekundi i svaki put kad dosegne 255 "prelijeva" i vraća se na 0 ponovo. U isto vrijeme kada se vraća na nulu, šalje signal prekida tajmera zbog prelijevanja. Računar ovo shvata i znate šta sada radi, zar ne? Da. Odlazi na lokaciju programske memorije 0x0020 i izvršava instrukcije koje tamo pronađe.

Odlično! Ako ste još uvijek sa mnom, onda ste neumorni superheroj! Idemo dalje…

Korak 6: Rukovatelj preljeva

Pretpostavimo da je tajmer/counter0 registar upravo preplavljen. Sada znamo da program prima signal prekida i izvršava 0x0020 koji govori Programskom brojaču, računaru da skoči na oznaku "overflow_handler", sljedeći je kod koji smo napisali nakon te oznake:

overflow_handler:

inc overflows cpi overflows, 61 brne PC+2 clr overflows reti

Prvo što učini je povećanje varijable "overflows" (što je naš naziv za opći namjenski radni registar R17), zatim "uspoređuje" sadržaj overflow -a sa brojem 61. Način na koji cpi funkcionira je da jednostavno oduzima dva broja i ako je rezultat nula postavlja zastavicu Z u registar SREG (rekao sam vam da ćemo ovaj registar vidjeti cijelo vrijeme). Ako su dva broja jednaka, Z zastava će biti 1, ako dva broja nisu jednaka, bit će 0.

Sljedeći red kaže "brne PC+2" što znači "grana ako nije jednaka". U osnovi, provjerava Z zastavu u SREG -u i ako NIJE jedan (tj. Dva broja nisu jednaka, ako su jednaki, nulta zastavica bi bila postavljena) PC se grana na PC+2, što znači da preskače sljedeći line i ide ravno u "reti" koja se vraća iz prekida na bilo koje mjesto u kodu kada je stigao prekid. Ako bi instrukcija brne pronašla 1 u nultom bitu zastavice, ne bi se granala, već bi samo nastavila na sljedeći red koji bi clr prelijevao resetirajući je na 0.

Koji je neto rezultat svega ovoga?

Pa vidimo da svaki put kada dođe do prelivanja timera ovaj rukovatelj povećava vrijednost "preljeva" za jedan. Dakle, varijabla "overflows" broji broj preljeva koji se pojave. Kad god broj dosegne 61, vraćamo ga na nulu.

Zašto bismo, zaboga, to učinili?

Da vidimo. Sjetite se da je naša brzina takta za naš CPU 16MHz i "predskalirali" smo ga pomoću TCCR0B tako da se mjerač vremena broji samo po 15625 brojeva u sekundi, zar ne? Svaki put kada tajmer dosegne broj od 255, on se prelijeva. To znači da se prelijeva 15625/256 = 61,04 puta u sekundi. Pratimo broj prelivanja sa našom promenljivom "overflows" i upoređujemo taj broj sa 61. Tako vidimo da će "overflows" biti jednak 61 jednom svake sekunde! Tako će naš rukovatelj resetirati "overflows" na nulu jednom svake sekunde. Dakle, ako bismo jednostavno nadzirali varijablu "overflows" i bilježili svaki put kad se vrati na nulu, računali bismo sekundu po sekundu u stvarnom vremenu (Imajte na umu da ćemo u sljedećem vodiču pokazati kako doći do preciznijeg kašnjenje u milisekundama na isti način na koji radi Arduino rutina "odgode").

Sada smo "obradili" prekide prelijevanja tajmera. Uvjerite se da razumijete kako ovo funkcionira, a zatim prijeđite na sljedeći korak u kojem koristimo ovu činjenicu.

Korak 7: Odgoda

Sada kada smo vidjeli da će naša rutina "overflow_handler" preusmjeravanja prekoračenja timera postaviti varijablu "overflows" na nulu jednom svake sekunde, možemo koristiti ovu činjenicu za dizajniranje potprograma "odgode".

Pogledajte sljedeći kôd ispod našeg kašnjenja: label

kašnjenje:

clr overflows sec_count: cpi overflows, 30 brne sec_count ret

Ovu potprogramu ćemo pozvati svaki put kad nam zatreba kašnjenje u programu. Način na koji radi je da prvo postavlja varijablu "overflows" na nulu. Zatim ulazi u područje s oznakom "sec_count" i upoređuje prelive sa 30, ako nisu jednaki, grana se nazad do oznake sec_count i upoređuje ponovo, i ponovo, itd. Sve dok konačno ne budu jednaki (zapamtite da sve ovo traje na našem tajmeru rukovatelj prekida prekida nastavlja povećavati promjenjive preljeve pa se mijenja svaki put kada se krećemo ovdje. Kada je preljev konačno jednak 30, on izlazi iz petlje i vraća se gdje god smo nazvali kašnjenje: od. Neto rezultat je kašnjenje od 1/2 sekunde

Vježba 2: Promijenite rutinu overflow_handler na sljedeće:

overflow_handler:

inc preliva reti

i pokrenite program. Je li nešto drugačije? Zašto ili zašto ne?

Korak 8: Trepnite

Na kraju, pogledajmo rutinu treptanja:

treptaj:

sbi PORTD, 4 rcall call cbi PORTD, 4 rcall delay rjmp trepće

Prvo uključujemo PD4, zatim pozivamo našu potprogram odgode. Koristimo rcall tako da kada računar dođe do "ret" naredbe da će se vratiti na red koji slijedi rcall. Zatim rutinsko odgađanje odlaže 30 brojeva u promenljivoj prelivanja, kao što smo videli, a to je skoro tačno 1/2 sekunde, zatim isključujemo PD4, odlažemo još 1/2 sekunde, pa se ponovo vraćamo na početak.

Neto rezultat je trepćuća LED dioda!

Mislim da ćete se sada složiti da "blink" vjerovatno nije najbolji "hello world" program na asemblerskom jeziku.

Vježba 3: Promijenite različite parametre u programu tako da LED dioda treperi različitom brzinom, poput sekunde ili 4 puta u sekundi, itd. Vježba 4: Promijenite je tako da LED svijetli i gasi različito vrijeme. Na primjer, uključite na 1/4 sekunde, a zatim isključite na 2 sekunde ili nešto slično. Vježba 5: Promijenite odabir bita sata TCCR0B na 100, a zatim nastavite uzlazno po tablici. U kojem trenutku se ne razlikuje od našeg programa "hello.asm" iz vodiča 1? Vježba 6 (izborno): Ako imate drugi kristalni oscilator, poput 4 MHz ili 13,5 MHz ili bilo što drugo, promijenite svoj oscilator od 16 MHz na ploči za novu i pogledajte kako to utječe na brzinu treptanja LED -a. Sada biste trebali moći proći kroz precizan izračun i točno predvidjeti kako će to utjecati na stopu.

Korak 9: Zaključak

Čestitamo svima vama koji ste uspjeli do sada, čestitamo!

Shvaćam da je prilično teško gnjaviti kada više čitate i gledate gore nego što oživljavate i eksperimentirate, ali nadam se da ste naučili sljedeće važne stvari:

  1. Kako radi memorija programa
  2. Kako SRAM radi
  3. Kako potražiti registre
  4. Kako potražiti upute i znati šta rade
  5. Kako implementirati prekide
  6. Kako CP izvršava kod, kako funkcioniše SREG i šta se dešava tokom prekida
  7. Kako raditi petlje i skokove i skakutati po kodu
  8. Koliko je važno pročitati tehnički list!
  9. Ako jednom znate kako to učiniti za mikrokontroler Atmega328p, bit će to relativna šetnja za učenje novih kontrolera koji vas zanimaju.
  10. Kako promijeniti vrijeme procesora u realno vrijeme i koristiti ga u rutinama odgode.

Sada kada imamo dosta teorije, možemo napisati bolji kod i kontrolirati složenije stvari. Dakle, sljedeći vodič ćemo raditi upravo to. Napravit ćemo kompliciraniji, zanimljiviji krug i kontrolirati ga na zabavne načine.

Vježba 7: "Razbijte" kôd na različite načine i pogledajte što se događa! Naučna znatiželja dušo! Vježba 8: Sastavite kôd pomoću opcije "-l" za generiranje datoteke s popisom. Tj. "avra -l blink.lst blink.asm" i pogledajte datoteku liste. Dodatni kredit: Kod bez komentara koji sam dao na početku i kodirani kod o kojem ćemo kasnije razgovarati razlikuju se! Postoji jedna linija koda koja se razlikuje. Možete li ga pronaći? Zašto ta razlika nije bitna?

Nadam se da ste se zabavili! Vidimo se sledeći put…

Preporučuje se: