Sisukord:

AVR Assembleri õpetus 3: 9 sammu
AVR Assembleri õpetus 3: 9 sammu

Video: AVR Assembleri õpetus 3: 9 sammu

Video: AVR Assembleri õpetus 3: 9 sammu
Video: Программирование AVR на языке Ассемблер, часть 5 1"Бегущие огни" 2024, Juuli
Anonim
AVR Assembleri õpetus 3
AVR Assembleri õpetus 3

Tere tulemast õppetundi number 3!

Enne alustamist tahan teha filosoofilise mõtte. Ärge kartke katsetada ahelate ja koodiga, mida me nendes õpetustes konstrueerime. Muutke juhtmeid ümber, lisage uusi komponente, võtke komponente välja, muutke koodiridu, lisage uusi ridu, kustutage ridu ja vaadake, mis juhtub! Midagi murda on väga raske ja kui te seda teete, siis keda see huvitab? Miski, mida me kasutame, sealhulgas mikrokontroller, pole väga kallis ja alati on hariv näha, kuidas asjad võivad ebaõnnestuda. Mitte ainult ei saa järgmine kord teada, mida mitte teha, vaid mis veelgi tähtsam - teate, miks mitte seda teha. Kui olete midagi minusugust, siis kui olite laps ja saite uue mänguasja, ei läinud kaua aega, enne kui selle osadeks saite, et näha, mis selle õigesti tiksuma pani? Mõnikord sattus mänguasi korvamatult kahjustatud, kuid pole suurt midagi. Võimaldades lapsel uurida oma uudishimu isegi mänguasjade purunemiseni, saab temast nõudepesumasina asemel teadlane või insener.

Täna ühendame väga lihtsa vooluahela ja hakkame siis teooriasse pisut üle minema. Vabandame selle pärast, kuid me vajame tööriistu! Ma luban, et korvame selle õppetükis 4, kus teeme tõsisemat ringraja ehitamist ja tulemus on päris lahe. Kõigi nende õpetuste tegemise viis on aga väga aeglane ja mõtisklev. Kui te lihtsalt künnate, ehitate vooluringi, kopeerite ja kleepite koodi ning käivitate selle, siis see kindlasti töötab, kuid te ei õpi midagi. Peate mõtlema igale reale. Paus. Katse. Leiuta. Kui teete seda nii, siis viienda õpetuse lõpuks lõpetate lahedate asjade ehitamise ja ei vaja enam juhendamist. Vastasel juhul sa lihtsalt vaatad, mitte ei õpi ega loo.

Igal juhul piisavalt filosoofiat, alustame!

Selles õpetuses vajate:

  1. teie prototüüpimislaud
  2. LED
  3. ühendavad juhtmed
  4. takisti umbes 220 kuni 330 oomi
  5. Kasutusjuhend: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Andmeleht: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. erinev kristallostsillaator (valikuline)

Siin on link õpetuste täielikule kogumikule:

Samm: vooluringi ehitamine

Ringkonnakohtu ehitamine
Ringkonnakohtu ehitamine

Selle õpetuse skeem on äärmiselt lihtne. Kirjutame sisuliselt "vilkumise" programmi, nii et vajame ainult järgmist.

Ühendage LED PD4 -ga, seejärel 330 oomi takistiga ja seejärel maandusega. st.

PD4 - LED - R (330) - GND

ja see ongi!

Teooria saab olema karm, kuid…

Samm: miks me vajame kommentaare ja faili M328Pdef.inc?

Arvan, et peaksime alustuseks näitama, miks kaasamisfail ja kommentaarid on kasulikud. Ükski neist pole tegelikult vajalik ja saate ilma nendeta koodi kirjutada, kokku panna ja üles laadida samamoodi ning see töötab suurepäraselt (kuigi ilma kaasamisfailita võite kogujalt mõningaid kaebusi saada, kuid vigu pole)

Siin on kood, mille me täna kirjutame, välja arvatud see, et olen kommentaarid ja kaasamisfaili eemaldanud:

.seade ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 out 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 out 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0x04 b: sbi 0x0b, cx 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

päris lihtne eks? Haha. Kui olete selle faili kokku pannud ja üles laadinud, hakkab LED vilkuma kiirusega 1 vilkumine sekundis, vilkumine kestab 1/2 sekundit ja vilgutuste vaheline paus kestab 1/2 sekundit.

Selle koodi vaatamine aga vaevalt valgustab. Kui kirjutaksite sellist koodi ja tahaksite seda tulevikus muuta või uuesti kasutada, oleks teil raske.

Paneme siis kommentaarid ja lisame faili tagasi, et saaksime sellest aru saada.

3. samm: Blink.asm

Siin on kood, mida me täna arutame:

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

; kirjutas: 1o_o7; kuupäev:; versioon: 1.0; fail salvestatud järgmiselt: blink.asm; AVR jaoks: atmega328p; taktsagedus: 16MHz (valikuline); ***********************************; Programmi funktsioon: ---------------------; loeb sekundeid LED -i vilkumisega;; PD4 - LED - R (330 oomi) - GND;; --------------------------------------.nolist.include "./m328Pdef.inc".list; ==============; Deklaratsioonid:.def temp = r16.def overflows = r17.org 0x0000; mälu (arvuti) lähtestamiskäitleja asukoht rjmp Lähtesta; jmp maksab 2 protsessoritsüklit ja rjmp maksab ainult 1; nii et kui teil pole vaja hüpata üle 8 k baiti; vajate ainult rjmp -i. Seetõttu on ainult mõned mikrokontrollerid; on rjmp ja mitte jmp.org 0x0020; mälu asukoht Timer0 ülevoolu käitleja rjmp overflow_handler; mine siia, kui tekib taimer0 ülevoolu katkestus; ============ Lähtesta: ldi temp, 0b00000101 out TCCR0B, temp; seadke kella valiku bittide CS00, CS01, CS02 väärtuseks 101; see lülitab taimeriloenduri 0, TCNT0 režiimi FCPU/1024; nii et see tiksub CPU sagedusel/1024 ldi temp, 0b00000001 silmust TIMSK0, temp; määrake bitt Timer Overflow Interrupt Enable (TOIE0); taimeri katkestusmaski registri (TIMSK0) sei; globaalsete katkestuste lubamine - samaväärne "sbi SREG, I" clr temp out TCNT0, temp; lähtestage taimer/loendur väärtusele 0 sbi DDRD, 4; määrake PD4 väljundiks; ======================; Programmi põhiosa: vilgub: sbi PORTD, 4; lülitage PD4 rcall viivitus sisse; viivitus on 1/2 sekundit cbi PORTD, 4; lülitage PD4 rcall viivitus välja; viivitus on 1/2 sekundit rjmp vilgub; loop tagasi algusviivituse juurde: clr ülevoolab; määrake ülevoolude väärtuseks 0 sek_arv: cpi ülevoolud, 30; võrrelda ülevoolude arvu ja 30 brne sec_count; haru tagasi sek_count kui mitte võrdne ret; kui on tekkinud 30 ülevoolu, pöörduge tagasi vilkuma overflow_handler: lisage ülevoolud; lisage 1 ülevooludele muutuja cpi ülevoolud, 61; võrrelda 61 brne PC+2 -ga; Programmiloendur + 2 (jätke järgmine rida vahele), kui see pole võrdne clr -ülevool; kui tekkis 61 ülevoolu, lähtestage loendur nulli reti; katkestusest tagasi

Nagu näete, on minu kommentaarid nüüd veidi lühemad. Kui me teame, millised käsud käsukorras on, ei pea me seda kommentaarides selgitama. Meil on vaja ainult programmi seisukohast selgitada, mis toimub.

Me arutame tükkhaaval, mida see kõik teeb, kuid kõigepealt proovime saada globaalse perspektiivi. Programmi põhiosa töötab järgmiselt.

Esiteks seadsime PORTDi bitti 4 "sbi PORTD, 4", see saadab PD4 -le 1, mis paneb selle tihvti pinge 5 V. See lülitab LED -i sisse. Seejärel liigume alamprogrammi "viivitus" juurde, mis loeb 1/2 sekundit (selgitame, kuidas see hiljem toimub). Seejärel naaseme PORTD -i vilkuva ja selge bitti 4 juurde, mis seab PD4 väärtuseks 0V ja lülitab seega LED -i välja. Seejärel viivitame veel 1/2 sekundit ja hüppame seejärel uuesti vilkumise algusesse "rjmp vilkuma".

Peaksite selle koodi käivitama ja nägema, et see teeb seda, mida peaks.

Ja seal see on! See on kõik, mida see kood füüsiliselt teeb. Mikrokontrolleri sisemine mehaanika on natuke rohkem kaasatud ja seetõttu teeme seda õpetust. Nii et arutame iga osa järjest.

Samm 4:.org Assembleri direktiivid

Me juba teame, mida teevad.nolist,.list,.include ja.def kokkupanijadirektiivid meie eelmistest õpetustest, nii et vaatame kõigepealt nelja koodirida, mis järgnevad:

.org 0x0000

jmp Lähtesta.org 0x0020 jmp overflow_handler

Lause.org ütleb monteerijale, kuhu "Programmi mällu" järgmine lause panna. Kui teie programm käivitub, sisaldab "Programmiloendur" (lühendatult PC) praeguse täidetava rea aadressi. Nii et sel juhul, kui arvuti on 0x0000, näeb see mälu asukohas käsku "jmp Reset". Põhjus, miks me soovime jmp Reset sellesse kohta panna, on see, et kui programm käivitub või kiip lähtestatakse, hakkab arvuti selles kohas koodi täitma. Niisiis, nagu näeme, oleme just käskinud tal kohe "hüpata" jaotisse nimega "Lähtesta". Miks me seda tegime? See tähendab, et ülaltoodud kaks viimast rida jäetakse lihtsalt vahele! Miks?

Noh, seal lähevad asjad huvitavaks. Nüüd peate avama pdf -vaataja koos täieliku ATmega328p andmelehega, millele osutasin selle õpetuse esimesel lehel (sellepärast on see jaotises „vajate” punkt 4). Kui teie ekraan on liiga väike või teil on juba liiga palju aknaid avatud (nagu minu puhul), võite teha seda, mida mina teen, ja panna selle Ereaderile või oma Android -telefonile. Kasutate seda kogu aeg, kui plaanite koostamiskoodi kirjutada. Lahe on see, et kõik mikrokontrollid on korraldatud väga sarnasel viisil ja seega, kui olete harjunud andmelehti lugema ja nendelt kodeerima, on peaaegu tühine teha sama teise mikrokontrolleri puhul. Seega õpime tegelikult kasutama kõiki mikrokontrollereid teatud mõttes ja mitte ainult atmega328p.

Olgu, pöörduge andmelehe lehekülje 18 poole ja vaadake joonist 8-2.

Nii on mikrokontrolleri programmimälu seadistatud. Näete, et see algab aadressiga 0x0000 ja on jagatud kaheks osaks; rakenduse välklambi sektsioon ja alglaadimise välkjaotis. Kui viitate lühidalt leheküljele 277 tabelile 27-14, näete, et rakenduse välklambi sektsioon võtab asukohad vahemikus 0x0000 kuni 0x37FF ja alglaadimise välkjaotis võtab ülejäänud asukohad vahemikus 0x3800 kuni 0x3FFF.

Harjutus 1: Mitu asukohta on programmi mälus? S.t. teisendage 3FFF kümnendkohaks ja lisage 1, kuna hakkame loendama nulliga. Kuna iga mälukoht on 16 bitti (või 2 baiti) lai, siis milline on mälu baitide koguarv? Teisendage see nüüd kilobaitideks, pidades meeles, et kilobaitides on 2^10 = 1024 baiti. Alglaadimise välklambi osa läheb vahemikku 0x3800 kuni 0x37FF, mitu kilobaiti see on? Mitu kilobaiti mälu saame kasutada oma programmi salvestamiseks? Teisisõnu, kui suur võib olla meie programm? Lõpuks, kui palju koodiridu meil võib olla?

Olgu, nüüd, kui me kõik teame välkprogrammi mälu korraldamisest, jätkame arutelu.org avalduste üle. Näeme, et esimene mälu asukoht 0x0000 sisaldab meie juhiseid hüpata meie sektsiooni, mille nimetasime Reset. Nüüd näeme, mida avaldus ".org 0x0020" teeb. See ütleb, et soovime, et järgmise rea juhised paigutataks mälu asukohta 0x0020. Juhend, mille oleme sinna paigutanud, on hüpe meie koodi jaotisesse, mille oleme märkinud "ülevooluhalduriks" … miks peaksime siis nõutama selle hüppe paigutamist mälupaika 0x0020? Selle selgitamiseks pöördume andmelehe 65. lehekülje poole ja vaatame tabelit 12-6.

Tabel 12-6 on tabel "Vektorite lähtestamine ja katkestamine" ning see näitab täpselt, kuhu arvuti läheb, kui saab "katkestuse". Näiteks kui vaatate vektori numbrit 1. Katkestuse allikas on „RESET”, mis on määratletud kui „väline tihvt, sisselülitamise lähtestamine, väljalülitamine ja valvesüsteemi lähtestamine”, mis tähendab, et kui kui need asjad juhtuvad meie mikrokontrolleriga, hakkab arvuti meie programmi käivitama programmi mälu kohas 0x0000. Kuidas on siis meie.org direktiiviga? Noh, me panime käsu mälu asukohta 0x0020 ja kui vaatate tabelit allapoole, näete, et kui ilmneb taimer/loendur0 ülevool (pärineb TIMER0 OVF -ist), täidab see kõik asukoha 0x0020. Nii et kui see juhtub, hüppab arvuti kohale, mille nimetasime "overflow_handler". Lahe eks? Mõne minuti pärast näete, miks me seda tegime, kuid kõigepealt lõpetame selle õpetuse sammu kõrvale.

Kui me tahame muuta oma koodi puhtamaks ja korrektsemaks, peaksime praegu asendatavad 4 rida asendama järgmisega (vt lk 66):

.org 0x0000

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

Nii et kui antud katkestus toimub, siis see lihtsalt "loobub", mis tähendab "katkestusest naasmist" ja midagi muud ei juhtu. Aga kui me neid erinevaid katkestusi kunagi ei luba, siis neid ei kasutata ja saame nendesse kohtadesse programmi koodi panna. Meie praeguses programmis "blink.asm" lubame ainult timer0 ülevoolu katkestamise (ja muidugi lähtestamise katkestuse, mis on alati lubatud) ja seega ei hakka me teistega tülitama.

Kuidas me siis "lubame" timer0 ülevoolu katkestuse? … See on meie järgmise õpetuse samm.

Samm: taimer/loendur 0

Taimer/loendur 0
Taimer/loendur 0

Vaadake ülaltoodud pilti. See on "arvuti" otsustusprotsess, kui mõni väline mõju "katkestab" meie programmi voo. Esimene asi, mida ta teeb, kui saab väljastpoolt signaali, et katkestus on toimunud, kontrollib, kas oleme selle katkestuse tüübi jaoks seadistanud "katkestamise lubamise" biti. Kui me pole seda teinud, jätkab see lihtsalt meie järgmise koodirea täitmist. Kui oleme seadistanud selle konkreetse katkestamise lubamise biti (nii, et selles biti asukohas on 0 asemel 1), siis kontrollib see, kas oleme lubanud "globaalsed katkestused" või mitte, siis läheb see uuesti järgmisele reale koodi ja jätkake. Kui oleme lubanud ka ülemaailmsed katkestused, läheb see seda tüüpi katkestuse programmimälu asukohta (nagu on näidatud tabelis 12-6) ja täidab mis tahes käsu, mille oleme sinna sisestanud. Nii et vaatame, kuidas oleme selle kõik oma koodis rakendanud.

Meie koodi jaotis Lähtesta märgistusega algab kahe järgmise reaga:

Lähtesta:

ldi temp, 0b00000101 välja TCCR0B, temp

Nagu me juba teame, laadib see temp (st R16) kohe järgneva numbri, mis on 0b00000101. Seejärel kirjutab ta selle numbri registrisse nimega TCCR0B, kasutades käsku "out". Mis see register on? Lähme edasi andmelehe leheküljele 614. See on tabeli keskel, mis võtab kokku kõik registrid. Aadressilt 0x25 leiate TCCR0B. (Nüüd teate, kust minu koodi kommenteerimata versioonis tuli rida "out 0x25, r16"). Ülaltoodud koodisegmendi järgi näeme, et oleme seadnud 0 -nda ja 2 -nda bitti ning kustutanud kõik ülejäänud. Tabelit vaadates näete, et see tähendab, et oleme seadistanud CS00 ja CS02. Nüüd läheme edasi andmelehe peatükki "8-bitine taimer/loendur0 koos PWM-iga". Eelkõige minge selle peatüki leheküljele 107. Näete registri "Timer/Counter Control Register B" (TCCR0B) sama kirjeldust, mida me just nägime registrite kokkuvõtte tabelis (nii et oleksime võinud otse siia tulla, aga ma tahtsin, et te näeksite, kuidas kokkuvõtlikke tabeleid kasutada edaspidiseks kasutamiseks). Andmelehel kirjeldatakse jätkuvalt iga registri bitti ja nende tegevust. Jätame selle kõik praegu vahele ja pöörame lehe tabelisse 15-9. See tabel näitab "Kella valimise biti kirjeldust". Nüüd vaadake seda tabelit allapoole, kuni leiate rea, mis vastab bittidele, mille me sellesse registrisse määrasime. Rida ütleb "clk/1024 (eelkvalifitseerijalt)". See tähendab, et me tahame, et taimer/loendur0 (TCNT0) tiksuks kiirusega, mis on CPU sagedus jagatud 1024 -ga. Kuna meie mikrokontrollerit toidab 16 MHz kristallostsillaator, tähendab see, et meie protsessori juhiste täitmise kiirus on 16 miljonit juhist sekundis. Nii et meie TCNT0 loenduri tiksumise määr on siis 16 miljonit/1024 = 15625 korda sekundis (proovige seda erinevate kellavaliku bittidega ja vaadake, mis juhtub - mäletate meie filosoofiat?). Jätame numbri 15625 hilisemaks meelde ja liigume kahe järgmise koodirida juurde:

ldi temp, 0b00000001

silmused TIMSK0, temp

See määrab registri TIMSK0 0. bitti ja kustutab kõik ülejäänud. Kui vaatate andmelehe lehekülge 109, näete, et TIMSK0 tähistab "Timer/Counter Interrupt Mask Register 0" ja meie kood on seadnud 0 -nda biti, mille nimi on TOIE0, mis tähistab "Timer/Counter0 Overflow Interrupt Enable" … Seal! Nüüd näete, mis see on. Nüüd on meil "katkestamise lubamise bittide komplekt", nagu me tahtsime esimesest otsusest meie pildil ülaosas. Nüüd peame vaid lubama "globaalsed katkestused" ja meie programm suudab seda tüüpi katkestustele reageerida. Lubame globaalsed katkestused peagi, kuid enne seda võib teid miski segadusse ajada.. miks ma kasutasin tavalise "väljas" asemel käsku "sts", et kopeerida TIMSK0 registrisse?

Kui näete mind, kasutage juhiseid, mida te pole enne näinud, esimese asjana peaksite pöörduma andmelehe lehekülje 616 poole. See on "juhiste komplekti kokkuvõte". Nüüd leidke juhis "STS", mida ma kasutasin. See ütleb, et see võtab numbri R -registrist (kasutasime R16) ja "Store otse SRAM -i" asukohast k (meie puhul antud TIMSK0). Miks me siis pidime TIMSK0 -s salvestamiseks kasutama "silmuseid", mille jaoks kulub 2 kellatsüklit (vt tabeli viimast veergu), ja me vajasime ainult TCCR0B -s salvestamiseks ainult "out", mis võtab vaid ühe kellatsükli? Sellele küsimusele vastamiseks peame minema tagasi meie registrite kokkuvõtte tabelisse leheküljel 614. Näete, et TCCR0B register asub aadressil 0x25, aga ka (0x45)? See tähendab, et see on SRAM -i register, kuid see on ka teatud tüüpi register, mida nimetatakse "pordiks" (või i/o -registriks). Kui vaatate käsu "out" kõrval olevat juhendite kokkuvõtte tabelit, näete, et see võtab väärtused "töötavatest registritest" nagu R16 ja saadab need PORT -i. Seega saame TCCR0B -le kirjutamisel kasutada "out" ja säästa endale kellatsükli. Nüüd aga otsi registrite tabelist üles TIMSK0. Näete, et selle aadress on 0x6e. See on väljaspool sadamate vahemikku (mis on ainult SRAM -i esimesed 0x3F asukohad) ja seega peate tagasi pöörduma sts -käsu kasutamise ja selle tegemiseks kahe protsessorikellaga. Palun lugege kohe märkust 4 juhiste kokkuvõtte tabeli lõpus lk 615. Pange tähele, et kõik meie sisend- ja väljundpordid, nagu PORTD, asuvad tabeli allosas. Näiteks PD4 on bit 4 aadressil 0x0b (nüüd näete, kust kõik 0x0b asjad tulid minu kommenteerimata koodis!).. okei, kiire küsimus: kas muutsite "silmused" "välja" ja vaadake, mis juhtub? Pidage meeles meie filosoofiat! murda ära! ära võta sõna lihtsalt asjadeks.

Olgu, enne kui edasi liigume, pöörduge minutiks andmelehe leheküljele 19. Näete pilti andmemälust (SRAM). Esimesed 32 registrit SRAM -is (vahemikus 0x0000 kuni 0x001F) on "üldotstarbelised tööregistrid" R0 kuni R31, mida kasutame kogu aeg oma koodi muutujatena. Järgmised 64 registrit on I/O pordid kuni 0x005f (st need, millest me rääkisime ja mille registritabelis on need sulgudes aadressid, mida saame kasutada "out" asemel käsku "out") SRAM -i järgmine jaotis sisaldab kõiki teisi kokkuvõtva tabeli registreid kuni aadressini 0x00FF ja lõpuks on ülejäänud sisemine SRAM. Nüüd kiiresti, pöördume hetkeks 12. leheküljele. Seal näete tabelit "üldotstarbeliste tööregistrite" kohta, mida me alati oma muutujatena kasutame. Näete paksu joont numbrite R0 kuni R15 ja seejärel R16 kuni R31 vahel? Selle rida on see, miks me kasutame alati R16-d väikseimana ja ma lähen sellesse õpetusse veidi lähemalt, kus vajame ka kolme 16-bitist kaudset aadressiregistrit X, Y ja Z. asuge sellesse veel, kuigi me ei vaja seda praegu ja oleme siin piisavalt takerdunud.

Pöörake üks leht tagasi andmelehe 11. leheküljele. Kas näete paremas ülanurgas SREG -registri diagrammi? Näete, et selle registri bitti 7 nimetatakse "mina". Nüüd minge lehele alla ja lugege Bit 7 kirjeldust…. jeee! See on globaalse katkestamise lubamise bitt. Selle peame seadma, et läbida meie ülaltoodud diagrammi teine otsus ja lubada meie programmis taimeri/loenduri ülevoolu katkestused. Seega peaks meie programmi järgmine rida olema järgmine:

sbi SREG, I

mis määrab bitti nimega "I" SREG registris. Selle asemel kasutasime aga juhiseid

sei

selle asemel. See bitt on programmides nii sageli seatud, et nad tegid lihtsalt lihtsama viisi.

Okei! Nüüd oleme ülevoolu katkestused valmis saatma, nii et meie "jmp overflow_handler" käivitatakse igal ajal.

Enne kui edasi läheme, heitke kiire pilk SREG registrile (Status Register), sest see on väga oluline. Lugege, mida iga lipp tähistab. Eelkõige seavad ja kontrollivad paljud meie kasutatavad juhised neid lippe kogu aeg. Näiteks kasutame hiljem käsku "CPI", mis tähendab "kohe võrrelda". Vaadake selle juhendi juhiste kokkuvõtlikku tabelit ja märkige, mitu lippu see veerus "lipud" seab. Need on kõik SREG -i lipud ja meie kood määrab need ja kontrollib neid pidevalt. Näete varsti näiteid. Lõpuks on selle jaotise viimane osa järgmine:

clr temp

välja TCNT0, temp sbi DDRD, 4

Viimane rida on siin ilmselge. See määrab lihtsalt PortD andmesuunaregistri neljanda biti, mis põhjustab PD4 väljundi.

Esimene määrab muutuva temperatuuri nulliks ja kopeerib selle seejärel TCNT0 registrisse. TCNT0 on meie taimer/loendur0. See seab selle nulli. Niipea kui arvuti selle rea täidab, algab taimer0 nullist ja loeb kiirusega 15625 korda sekundis. Probleem on järgmine: TCNT0 on "8-bitine" register? Mis on siis suurim arv, mida 8-bitine register mahutab? Noh 0b11111111 on see. See on number 0xFF. Mis on 255. Nii et näete, mis juhtub? Taimer tõmbab kiirust 15625 korda sekundis ja iga kord, kui see jõuab 255 -ni, "ületab" ja läheb uuesti 0 -le. Samal ajal kui see nullini läheb, saadab taimer ülevoolu katkestuse signaali. Arvuti saab sellest aru ja teate, mida see nüüd teeb? Jep. See läheb programmimälu asukohta 0x0020 ja täidab sealt leitud käsu.

Suurepärane! Kui sa oled ikka veel minuga, oled sa väsimatu superkangelane! Jätkame…

6. samm: ülevoolu käitleja

Seega oletame, et taimerite/loendurite register on just üle jooksnud. Nüüd teame, et programm saab katkestussignaali ja käivitab 0x0020, mis käsib programmiloenduril, arvutil, hüpata sildile "overflow_handler", järgmine kood on see, mille kirjutasime pärast seda silti:

overflow_handler:

inc ülevoolud cpi ülevoolud, 61 brne PC+2 clr ülevoolu pension

Esimene asi, mida ta teeb, on suurendada muutujat "ülevoolud" (mis on meie üldotstarbelise tööregistri nimi R17), seejärel "võrrelda" ülevoolude sisu numbriga 61. Käsk cpi töötab nii, et see lihtsalt lahutab kaks numbrit ja kui tulemus on null, seab see SREG registrisse lipu Z (ma ütlesin teile, et näeme seda registrit kogu aeg). Kui kaks numbrit on võrdsed, on Z lipp 1, kui need kaks pole võrdsed, siis 0.

Järgmine rida ütleb "brne PC+2", mis tähendab "haru kui mitte võrdne". Põhimõtteliselt kontrollib see SREG -is Z -lippu ja kui see EI ole üks (st kaks numbrit pole võrdsed, kui need oleksid võrdsed, oleks nulllipp määratud), hargneb arvuti PC+2 -ks, mis tähendab, et jätab järgmise vahele rida ja läheb otse "reti", mis naaseb katkestusest mis tahes kohta, kus see koodis oli, kui katkestus saabus. Kui brne -juhis leiaks nulli lipu bitist 1, ei hargneks see ja jätkaks selle asemel järgmisele reale, mis nihutaks ülevoolu, lähtestades selle väärtuseks 0.

Mis on selle kõige lõpptulemus?

Noh, me näeme, et iga kord, kui on taimeri ülevool, suurendab see käitleja "ülevoolu" väärtust ühe võrra. Seega loeb muutuja "ülevoolud" ülevoolude arvu nende tekkimisel. Kui arv jõuab 61 -ni, lähtestame selle nulli.

Miks me siis maailmas seda teeksime?

Vaatame. Tuletame meelde, et meie protsessori taktsagedus on 16 MHz ja me "skaleerisime" selle TCCR0B abil nii, et taimer loeb ainult kiirusega 15625 loendit sekundis? Ja iga kord, kui taimer jõuab 255 -ni, voolab see üle. See tähendab, et see ületab 15625/256 = 61,04 korda sekundis. Jälgime oma muutujaga "ülevoolud" ülevoolude arvu ja võrdleme seda arvu 61. Seega näeme, et "ülevoolud" võrduvad 61 -ga üks kord sekundis! Seega lähtestab meie käitleja "ülevoolud" nulli üks kord sekundis. Nii et kui me lihtsalt jälgiksime muutujat "ülevoolud" ja võtaksime teadmiseks iga kord, kui see nullitakse, loeksime reaalajas sekundite kaupa (pange tähele, et järgmises õpetuses näitame, kuidas täpsemat saada viivitus millisekundites samamoodi nagu Arduino "viivituse" rutiin töötab).

Nüüd oleme "käsitsenud" taimeri ülevoolu katkestusi. Veenduge, et saate aru, kuidas see toimib, ja liikuge järgmise sammu juurde, kus me seda fakti kasutame.

Samm: viivitus

Nüüd, kui oleme näinud, et meie taimerite ülevoolu katkestamise käitleja "overflow_handler" rutiin määrab muutuja "ülevoolud" nulli üks kord sekundis, saame seda fakti kasutada "viivituse" alamprogrammi kujundamiseks.

Vaadake meie viivituse alt järgmist koodi: silt

viivitus:

clr ületäitmine sec_count: cpi ülevool, 30 brne sec_count ret

Seda alamprogrammi kutsume iga kord, kui vajame oma programmi viivitust. Toimimisviis on see, et kõigepealt seab muutuja "ülevool" nulli. Seejärel siseneb see piirkonda nimega "sec_count" ja võrdleb ülevoolusid 30 -ga, kui need pole võrdsed, hargneb see tagasi sildile sec_count ja võrdleb uuesti ja uuesti jne, kuni need on lõpuks võrdsed (pidage meeles, et kogu see aeg meie taimeriga katkestamise käitleja jätkab muutujate ülevoolu suurendamist ja seega muutub see iga kord, kui me siin ringi läheme. Kui ülevoolud lõpuks võrduvad 30 -ga, väljub see silmusest ja naaseb kõikjale, kuhu me viivituseks helistasime: alates. Neto tulemus on viivitus 1/2 sekundit

Harjutus 2: muutke overflow_handler rutiin järgmiseks:

overflow_handler:

inc ületäitumised reti

ja käivitage programm. Kas midagi on teisiti? Miks või miks mitte?

8. samm: vilgutage

Lõpuks vaatame pilgutamisrutiini:

vilkuma:

sbi PORTD, 4 rcall delay cbi PORTD, 4 rcall delay rjmp vilgub

Esmalt lülitame sisse PD4, seejärel kutsume oma viivituse alamprogrammi. Kasutame rcallit, nii et kui arvuti jõuab "ret" avalduseni, naaseb see rcallile järgnevale reale. Seejärel viivitab rutiinne viivitus 30 korda ülevoolu muutujaga, nagu oleme näinud, ja see on peaaegu täpselt 1/2 sekundit, seejärel lülitame PD4 välja, viivitame veel 1/2 sekundit ja läheme siis uuesti algusesse.

Tulemuseks on vilkuv LED!

Arvan, et nüüd nõustute, et "vilkumine" pole ilmselt parim kokkupanekukeelne "tere maailma" programm.

Harjutus 3: Muutke programmi erinevaid parameetreid nii, et valgusdiood vilguks erineva kiirusega nagu sekund või 4 korda sekundis jne. Harjutus 4: muutke seda nii, et valgusdiood oleks erinevatel aegadel sisse ja välja lülitatud. Näiteks sisse lülitatud 1/4 sekundiks ja seejärel välja lülitatud 2 sekundiks või midagi sellist. Harjutus 5: muutke TCCR0B kella valiku bitti 100 -le ja jätkake siis tabeli tõstmist. Millisel hetkel muutub see eristamatuks meie "hello.asm" programmist juhendist 1? Harjutus 6 (valikuline): kui teil on erinev kristallostsillaator, näiteks 4 MHz või 13,5 MHz või mis iganes, vahetage oma 16 MHz ostsillaator välja leivalaual uue jaoks ja vaadake, kuidas see LED -i vilkumiskiirust mõjutab. Nüüd peaksite saama täpse arvutuse läbida ja täpselt ennustada, kuidas see määra mõjutab.

9. samm: järeldus

Palju õnne neile, kes te olete paadunud, nii kaugele jõudsite!

Ma saan aru, et kui loete ja otsite rohkem üles kui juhtmeid ühendate ja katsetate, on see üsna raske, kuid loodan, et olete õppinud järgmisi olulisi asju:

  1. Kuidas programmimälu töötab
  2. Kuidas SRAM töötab
  3. Kuidas registreid otsida
  4. Kuidas otsida juhiseid ja teada, mida nad teevad
  5. Katkestuste rakendamine
  6. Kuidas CP täidab koodi, kuidas SREG töötab ja mis juhtub katkestuste ajal
  7. Kuidas teha silmuseid ja hüppeid ning koodis ringi põrgata
  8. Kui tähtis on andmelehe lugemine!
  9. Kuidas te teate, kuidas seda kõike Atmega328p mikrokontrolleri jaoks teha, on suhteline koogikäik, et õppida tundma uusi teid huvitavaid kontrollereid.
  10. Kuidas muuta protsessori aega reaalajas ja kasutada seda viivitusrutiinides.

Nüüd, kui meil on palju teooriat, oleme võimelised paremat koodi kirjutama ja keerukamaid asju juhtima. Nii et järgmises õpetuses teeme just seda. Ehitame keerukama, huvitavama vooluringi ja juhime seda lõbusatel viisidel.

Harjutus 7: "Katkesta" kood mitmel viisil ja vaata, mis juhtub! Teaduslik uudishimu beebi! Kas keegi teine saab nõusid õigesti pesta? Harjutus 8: Pange kood kokku, kasutades loendifaili loomiseks valikut "-l". S.t. "avra -l blink.lst blink.asm" ja heitke pilk loendifailile. Lisakrediit: kommenteerimata kood, mille ma alguses andsin, ja kommenteeritav kood, millest me hiljem räägime, erinevad! On üks koodirida, mis on erinev. Kas leiate selle üles? Miks see erinevus pole oluline?

Loodetavasti oli teil lõbus! Näeme järgmine kord…

Soovitan: