Sadržaj:

AVR asemblerski vodič 3: 9 koraka
AVR asemblerski vodič 3: 9 koraka

Video: AVR asemblerski vodič 3: 9 koraka

Video: AVR asemblerski vodič 3: 9 koraka
Video: Автомобильный генератор для генератора с самовозбуждением с использованием ДИОДА 2024, Studeni
Anonim
Vodič za sastavljač AVR -a 3
Vodič za sastavljač AVR -a 3

Dobro došli u vodič 3!

Prije nego što počnemo, želim istaknuti filozofsko mišljenje. Nemojte se bojati eksperimentirati sa sklopovima i kodom koji stvaramo u ovim vodičima. Promijenite žice, dodajte nove komponente, izvadite komponente, promijenite redove koda, dodajte nove retke, izbrišite retke i pogledajte što će se dogoditi! Vrlo je teško bilo što slomiti i ako to učinite, koga briga? Ništa što koristimo, uključujući mikrokontroler, nije jako skupo i uvijek je poučno vidjeti kako stvari mogu propasti. Ne samo da ćete sljedeći put saznati što ne trebate 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 dijete i dobili ste novu igračku, nije prošlo mnogo vremena prije nego što ste je dobili u komadima kako biste vidjeli što ju je otkucalo? Ponekad je igračka završila nepopravljivo oštećena, ali ništa strašno. Dopuštanje djetetu da istraži svoju znatiželju čak do razbijenih igračaka ono je što ga pretvara u znanstvenika ili inženjera umjesto u perilicu posuđa.

Danas ćemo ožičiti vrlo jednostavan sklop, a zatim ćemo se malo pozabaviti teorijom. Žao nam je zbog ovoga, ali potrebni su nam alati! Obećavam da ćemo to nadoknaditi u tutorialu 4 gdje ćemo raditi ozbiljniju izgradnju sklopova, a rezultat će biti prilično kul. Međutim, način na koji trebate izvesti sve ove upute je na vrlo spor, kontemplativan način. Ako samo prođete, napravite krug, kopirate i zalijepite kôd, a zatim ga pokrenete, sigurno će 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, krenimo!

U ovom vodiču trebat će vam:

  1. vašu ploču za izradu prototipova
  2. LED dioda
  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 (izborno)

Evo poveznice na kompletnu zbirku vodiča:

Korak 1: Konstruiranje kruga

Konstrukcija sklopa
Konstrukcija sklopa

Krug u ovom vodiču izuzetno je 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, a zatim na masu. 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 include 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 include možete dobiti neke žalbe od asemblera - ali bez pogrešaka)

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

.uređaj ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 izlaz 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 izlaz 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0x04 b: sbi 0x0b, 0 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 u trajanju od 1/2 sekunde.

Međutim, gledanje u ovaj kôd teško je prosvijetliti. Ako biste napisali ovakav kod, željeli biste ga izmijeniti ili prenamijeniti u budućnosti, bilo bi vam teško.

Pa stavimo komentare i vratimo 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 spremljena kao: blink.asm; za AVR: atmega328p; frekvencija takta: 16MHz (izborno); **********************************; 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) mjesto rukovatelja resetiranja rjmp Resetiranje; jmp košta 2 ciklusa procesora, a rjmp samo 1; pa osim ako trebate preskočiti više od 8 kB; trebate samo rjmp. Stoga samo neki mikrokontroleri; imati rjmp a ne jmp.org 0x0020; memorijsko mjesto upravljača preljeva Timer0 rjmp overflow_handler; idite ovdje ako dođe do prekida preljeva timer0; ============ Reset: ldi temp, 0b00000101 out TCCR0B, temp; postavite bitove za odabir sata CS00, CS01, CS02 na 101; ovo postavlja Brojač 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 preljeva timera (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 dio programa: treptaj: sbi PORTD, 4; uključite LED na kašnjenju poziva PD4; kašnjenje će biti 1/2 sekunde cbi PORTD, 4; isključite LED na kašnjenju poziva PD4; kašnjenje će biti 1/2 sekunde rjmp treptati; petlja natrag na kašnjenje početka: clr se prelijeva; postavite preljeve na 0 sec_count: cpi preljevi, 30; usporedite broj preljeva i 30 brnih sec_count; grananje natrag na sec_count ako nije jednako ret; ako se dogodilo 30 preljeva, vratite se na treptaj overflow_handler: inc preljevi; varijabli preljeva dodati 1 promjenu cpi preljevima, 61; usporedi sa 61 brne PC+2; Brojač programa + 2 (preskočite sljedeći redak) ako nisu jednaki clr preljevi; ako se dogodio 61 preljev, resetirajte brojač na nulu reti; povratak iz prekida

Kao što vidite, moji su komentari sada malo kraći. Kad saznamo koje naredbe u skupu uputa radimo, ne moramo to objašnjavati u komentarima. Moramo samo objasniti što se događa sa stajališta programa.

Po komadu ćemo raspravljati o tome što sve ovo čini, ali prvo pokušajmo dobiti globalnu perspektivu. Glavni dio programa radi na sljedeć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. Zatim prelazimo na potprogram "odgode" koja broji 1/2 sekunde (kasnije ćemo objasniti kako to radi). Zatim se vraćamo na bljeskanje i brisanje bita 4 na PORTD -u koji postavlja PD4 na 0V i stoga isključuje LED. Zatim odlažemo još 1/2 sekunde, a zatim se opet vraćamo na početak treptanja s "rjmp blink".

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

I eto ga! To je sve što ovaj kod fizički radi. Unutarnja mehanika onoga što mikrokontroler radi malo je 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 Resetiraj.org 0x0020 jmp overflow_handler

Naredba.org govori asembleru gdje u "Memoriju programa" staviti sljedeću naredbu. Dok se vaš program izvršava, "Brojač programa" (skraćeno kao PC) sadrži adresu trenutnog retka koji se izvršava. Dakle, u ovom slučaju kada je računalo na 0x0000 vidjet će naredbu "jmp Reset" koja se nalazi na tom memorijskom mjestu. Razlog zašto želimo postaviti jmp Reset na to mjesto je taj što kada program započne ili se čip resetira, računalo počinje izvršavati kôd na ovom mjestu. Dakle, kao što vidimo, upravo smo mu rekli da odmah "skoči" na odjeljak s oznakom "Poništi". Zašto smo to učinili? To znači da se posljednja dva retka gore samo preskaču! Zašto?

Pa tu stvari postaju zanimljive. Sada ćete morati otvoriti preglednik PDF -a s potpunom podatkovnom tablicom ATmega328p na koju sam ukazao na prvoj stranici ovog vodiča (zato je to stavka 4 u odjeljku "trebat će vam"). Ako je vaš zaslon 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 svoj Android telefon. Koristit ćete ga cijelo vrijeme ako namjeravate 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 beznačajnim učiniti isto za drugi mikrokontroler. Dakle, zapravo učimo kako koristiti sve mikrokontrolere u određenom smislu, 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 dva odjeljka; odjeljak flash aplikacije i odjeljak boot flash. 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 sustava zauzima preostala mjesta od 0x3800 do 0x3FFF.

Vježba 1: Koliko mjesta postoji u memoriji programa? Tj. pretvoriti 3FFF u decimalni broj i dodati 1 budući da počinjemo brojati na 0. Budući da je svako memorijsko mjesto široko 16 bita (ili 2 bajta), koliki je ukupan broj bajtova memorije? Sada to pretvorite u kilobajte, sjetite se da u kilobajtu ima 2^10 = 1024 bajta. Odjeljak za pokretanje računara ide od 0x3800 do 0x37FF, koliko je ovo kilobajta? Koliko kilobajta memorije nam preostaje za pohranu našeg programa? Drugim riječima, koliko naš program može biti velik? Konačno, koliko redaka koda možemo imati?

U redu, sada kada znamo sve o organizaciji memorije flash programa, nastavimo s raspravom o.org izjavama. Vidimo da prvo memorijsko mjesto 0x0000 sadrži naše upute za skok na odjeljak koji smo označili kao Reset. Sada vidimo što radi izjava ".org 0x0020". Piše da želimo da se instrukcija u sljedećem retku smjesti na memorijsko mjesto 0x0020. Uputa koju 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 memorijsko mjesto 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 računalo otići kada primi "prekid". Na primjer, ako pogledate vektorski broj 1. "Izvor" prekida je "RESET" koji je definiran kao "Vanjski pin, Ponovno uključivanje, Vraćanje smeđe boje i Vraćanje sustava čuvara", što znači, ako postoji ako se to dogodi našem mikrokontroleru, računalo će početi izvršavati naš program na programskoj memoriji 0x0000. Što je onda s našom.org direktivom? Pa, postavili smo naredbu na memorijsko mjesto 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 je na lokaciji 0x0020. Dakle, kad god se to dogodi, računalo ć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 dotjeranijim, zaista bismo trebali zamijeniti 4 retka o kojima trenutno raspravljamo sljedećim (vidi stranicu 66):

.org 0x0000

rjmp Resetiraj; 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 danog 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 možemo postaviti programski kod na ta mjesta. U našem trenutnom programu "blink.asm" omogućit ćemo samo prekid prelijevanja timer0 (i naravno prekid resetiranja koji je uvijek omogućen), pa se nećemo zamarati s ostalima.

Kako tada "omogućiti" prekid prelijevanja timer0? … to je tema našeg sljedećeg koraka u ovom vodiču.

Korak 5: Odbrojavanje/brojač 0

Mjerač vremena/brojač 0
Mjerač vremena/brojač 0

Pogledajte gornju sliku. Ovo je proces donošenja odluka "računala" kada neki vanjski utjecaj "ometa" tijek našeg programa. Prva stvar koju učini kad dobije signal izvana da je došlo do prekida jest provjera jesmo li za tu vrstu prekida postavili bit "interrupt enable". Ako nismo, onda samo nastavlja izvršavati naš sljedeći redak koda. Ako smo postavili taj bit za omogućavanje prekida (tako da na tom mjestu bita postoji 1 umjesto 0), tada će provjeriti jesmo li omogućili "globalne prekide", ako ne, opet će ići na sljedeći redak koda i nastavi. Ako smo omogućili i globalne prekide, tada ć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:

Poništi:

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 taj broj zapisuje u registar pod nazivom TCCR0B pomoću naredbe "out". Što je ovaj registar? Pa, prijeđimo na stranicu 614 podatkovnog lista. Ovo je u sredini tablice koja sažima 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. Prijeđimo sada na poglavlje u podatkovnoj tablici pod nazivom "8-bitni mjerač vremena/brojač0 s PWM-om". Konkretno, idite na stranicu 107 tog poglavlja. Vidjet ćete isti opis registra "Timer/Counter Control Register B" (TCCR0B) registar koji smo upravo vidjeli u tablici sažetka registra (pa smo mogli doći ovdje, ali htio sam da vidite kako se koriste sažetke tablica za buduću referencu). Tehnički list nastavlja opisivati svaki od bitova u tom registru i što oni rade. Za sada ćemo sve to preskočiti i okrenuti stranicu na tablicu 15-9. Ova tablica prikazuje "Opis sata za odabir sata". Sada gledajte dolje u tu tablicu dok ne pronađete redak koji 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 milijuna uputa u sekundi. Dakle, stopa koju će naš brojač TCNT0 otkucati tada je 16 milijuna/1024 = 15625 puta u sekundi (isprobajte s različitim bitovima za odabir sata i vidite što će se dogoditi - sjetite se naše filozofije?). Zadržimo broj 15625 u mislima za kasnije i prijeđimo na sljedeća dva retka koda:

ldi temp, 0b00000001

st TIMSK0, temp

Time se 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 za odbrojavanje vremena brojača/brojača 0", a naš kôd je postavio 0. … Tamo! Sada vidite o čemu se ovdje radi. Sada imamo "bit prekida za omogućavanje prekida" kako smo željeli od prve odluke na našoj slici na vrhu. Dakle, sve što moramo 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 upute 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 izravno na SRAM" lokaciju k (u našem slučaju TIMSK0). Pa zašto smo morali koristiti "sts" koji traje 2 ciklusa takta (vidi zadnji stupac u tablici) 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 prvi 0x3F položaji 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 uputa na stranici 615. Također imajte na umu da se svi naši ulazni i izlazni priključci, 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 što događa se? Sjetite se naše filozofije! slomi to! ne vjerujte mi samo na riječ.

U redu, prije nego što nastavimo, okrenite se minutu na stranicu 19 u podatkovnom listu. Vidjet ćete sliku podatkovne memorije (SRAM). Prva 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 tablici 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. A sada brzo, okrenimo se na stranicu 12 na trenutak. 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 ja ću o tome malo više govoriti u sljedećem vodiču gdje će nam također trebati tri 16-bitna registra neizravnih adresa, X, Y i Z. Neću ipak uđite u to budući da nam to sada ne treba i dovoljno smo zaglibili ovdje.

Vratite jednu stranicu na stranicu 11 u podatkovnom listu. Vidjet ćete dijagram registra SREG gore desno? Vidite da se bit 7 tog registra naziva "I". Sada idite dolje na stranicu i pročitajte opis Bit 7…. yay! 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 odbrojavanja timera/brojača u našem programu. Dakle, sljedeći redak našeg programa trebao bi glasiti:

sbi SREG, I

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

sei

umjesto toga. Taj se dio toliko često postavlja u programe da su upravo napravili jednostavniji način za to.

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

Prije nego što krenemo dalje, kratko pogledajte SREG registar (Statusni registar) jer je vrlo važan. Pročitajte što svaka od zastava predstavlja. Konkretno, mnoge upute koje koristimo postavljat će i provjeravati te zastavice cijelo vrijeme. Na primjer, kasnije ćemo koristiti naredbu "CPI" što znači "usporedi odmah". Pogledajte sažetak tablice uputa za ovu uputu i primijetite koliko zastavica postavlja u stupcu "zastavice". Sve su to zastavice u SREG -u i naš će ih kôd postavljati i stalno provjeravati. Uskoro ćete vidjeti primjere. Konačno, posljednji dio ovog odjeljka koda je:

clr temp

izlaz TCNT0, temp sbi DDRD, 4

Zadnji redak ovdje je prilično očit. On samo postavlja 4. bit registra uputstava 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. Time se postavlja na nulu. Čim računalo izvrši ovu liniju, timer0 počet će 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 sadržavati 8-bitni registar? Pa to je 0b11111111. Ovo je broj 0xFF. Što je 255. Vidite li što se događa? Odbrojavanje se povećava brzinom od 15625 puta u sekundi i svaki put kad dosegne 255 "prelijeva se" i ponovno se vraća na 0. U isto vrijeme kada se vraća na nulu, šalje signal prekida prekida prebrojavanja vremena. Računalo to razumije i znate što radi do sada, zar ne? Da. Odlazi na lokaciju programske memorije 0x0020 i izvršava upute koje tamo pronađe.

Sjajno! Ako ste još uvijek sa mnom, onda ste neumorni superjunak! Idemo dalje…

Korak 6: Rukovatelj preljeva

Pretpostavimo dakle da je registar timer/counter0 upravo preplavljen. Sada znamo da program prima signal prekida i izvršava 0x0020 koji govori Programskom brojaču, računalu 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 čini je povećanje varijable "overflows" (što je naš naziv za opći namjenski radni registar R17), zatim "uspoređuje" sadržaj overflow -a s brojem 61. Način na koji cpi radi tako 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 zastavica će biti 1, ako dva broja nisu jednaka, bit će 0.

Sljedeći redak kaže "brne PC+2" što znači "grana ako nije jednaka". U biti, 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 redak i ide ravno u "reti" koja se vraća iz prekida na bilo koje mjesto koje je bilo u kodu kada je prekid stigao. Ako je instrukcija brne pronašla 1 u nultom bitu zastavice, ne bi se granala, već bi samo nastavila na sljedeći redak koji bi clr prelijevao resetirajući je na 0.

Koji je neto rezultat svega ovoga?

Pa vidimo da svaki put kad dođe do prelijevanja timera ovaj rukovatelj povećava vrijednost "preljeva" za jedan. Dakle, varijabla "preljevi" broji broj preljeva u trenutku njihovog pojavljivanja. 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 mjerač vremena dosegne broj od 255, on se prelijeva. To znači da se prelijeva 15625/256 = 61,04 puta u sekundi. Pratimo broj preljeva s našom varijablom "overflows" i uspoređujemo taj broj sa 61. Dakle vidimo da će "overflows" biti jednak 61 jednom svake sekunde! Dakle, naš rukovatelj će 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 točnijeg kašnjenje u milisekundama na isti način na koji radi Arduino rutina "odgode").

Sada smo "obradili" prekide preljevanja timera. Uvjerite se da razumijete kako to 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 preljeva timera postaviti varijablu "overflows" na nulu jednom svake sekunde, možemo koristiti ovu činjenicu za dizajn potprograma "odgode".

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

odgoditi:

clr prelijevanja sec_count: cpi prelijevanja, 30 brne sec_count ret

Ovu ćemo potprogram pozvati svaki put kad nam zatreba kašnjenje u programu. Način na koji radi je da prvo varijablu "overflows" postavlja na nulu. Zatim ulazi u područje s oznakom "sec_count" i uspoređuje preljeve s 30, ako nisu jednaki, grana se natrag do oznake sec_count i uspoređuje opet, i opet itd. Sve dok konačno ne budu jednaki (zapamtite da cijelo vrijeme ovo ide na našem timeru rukovatelj prekida prekida nastavlja povećavati promjenjive preljeve pa se mijenja svaki put kad 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 prelijeva reti

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

Korak 8: Trepnite

Na kraju, pogledajmo rutinu treptanja:

treptati:

sbi PORTD, 4 rcall call cbi PORTD, 4 rcall delay rjmp treptaj

Prvo uključujemo PD4, zatim pozivamo našu potprogram odgode. Koristimo rcall tako da kad računalo dođe do "ret" izraza vratit će se na red koji slijedi rcall. Zatim rutinsko odgađanje odgađa 30 brojeva u varijabli preljeva, kao što smo vidjeli, a to je gotovo točno 1/2 sekunde, zatim isključujemo PD4, odgađamo još 1/2 sekunde, a zatim se opet vraćamo na početak.

Neto rezultat je LED koji treperi!

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

Vježba 3: Promijenite različite parametre u programu tako da LED dioda treperi različitim brzinama 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čeno na 1/4 sekunde, a zatim isključeno na 2 sekunde ili nešto slično. Vježba 5: Promijenite odabir bitova sata TCCR0B na 100, a zatim nastavite uzlazno po tablici. U kojem se trenutku više 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 provjerite 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 onima od vas koji ste uspjeli stići dovde!

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 što rade
  5. Kako implementirati prekide
  6. Kako CP izvršava kôd, kako SREG radi i što se događa tijekom prekida
  7. Kako raditi petlje i skokove i skakutati po kodu
  8. Koliko je važno pročitati podatkovnu tablicu!
  9. Kad 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 CPU -a 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. Izgradit ćemo kompliciraniji, zanimljiviji krug i kontrolirati ga na zabavne načine.

Vježba 7: "Razbijte" kôd na razne načine i pogledajte što će se dogoditi! Znanstvena 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 popisa. Dodatni kredit: Nekomentirani kôd koji sam dao na početku i komentirani kôd o kojem ćemo kasnije razgovarati razlikuju se! Postoji jedna linija koda koja je drugačija. Možete li ga pronaći? Zašto ta razlika nije bitna?

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

Preporučeni: