Përmbajtje:

Tutoriali i AVR Assembler 3: 9 Hapa
Tutoriali i AVR Assembler 3: 9 Hapa

Video: Tutoriali i AVR Assembler 3: 9 Hapa

Video: Tutoriali i AVR Assembler 3: 9 Hapa
Video: Программирование AVR на языке Ассемблер, часть 3. "Настройка программатора" 2024, Korrik
Anonim
Udhëzues AVR Assembler 3
Udhëzues AVR Assembler 3

Mirësevini në tutorialin numër 3!

Para se të fillojmë, dua të bëj një pikë filozofike. Mos kini frikë të eksperimentoni me qarqet dhe kodin që po ndërtojmë në këto mësime. Ndryshoni telat përreth, shtoni përbërës të rinj, hiqni komponentët, ndryshoni linjat e kodit, shtoni rreshta të rinj, fshini rreshtat dhe shihni se çfarë ndodh! Veryshtë shumë e vështirë të prishësh ndonjë gjë dhe nëse e bën, kujt i intereson? Asgjë që ne po përdorim, përfshirë mikrokontrolluesin, nuk është shumë e shtrenjtë dhe është gjithmonë edukative të shihet se si gjërat mund të dështojnë. Jo vetëm që do të zbuloni se çfarë të mos bëni herën tjetër, por, më e rëndësishmja, do të dini pse të mos e bëni. Nëse jeni diçka si unë, kur ishit fëmijë dhe kishit një lodër të re, nuk kaloi shumë kohë para se ta kishit copë -copë për të parë se çfarë e bëri atë të funksionojë? Ndonjëherë lodra përfundonte e dëmtuar në mënyrë të pariparueshme, por jo ndonjë gjë e madhe. Lejimi i një fëmije të eksplorojë kuriozitetin e tij edhe deri në lodrat e thyera është ajo që e kthen atë në shkencëtar ose inxhinier në vend të pjatalarëse.

Sot do të lidhim një qark shumë të thjeshtë dhe pastaj do të futemi pak në teori. Na vjen keq për këtë, por ne kemi nevojë për mjetet! Unë premtoj se do ta kompensojmë këtë në tutorialin 4 ku do të bëjmë një ndërtim qarku më serioz dhe rezultati do të jetë mjaft i lezetshëm. Sidoqoftë, mënyra se si duhet të bëni të gjitha këto mësime është në një mënyrë shumë të ngadaltë, soditëse. Nëse thjesht lundroni, ndërtoni qarkun, kopjoni dhe ngjisni kodin dhe drejtojeni atë, sigurisht, do të funksionojë, por nuk do të mësoni asgjë. Ju duhet të mendoni për secilën rresht. Pauzë. Eksperimentoni Shpik Nëse e bëni atë në atë mënyrë, atëherë në fund të mësimit të 5 -të do të jeni duke ndërtuar gjëra të lezetshme dhe nuk keni nevojë për më tutorizim. Përndryshe ju thjesht po shikoni sesa të mësoni dhe krijoni.

Në çdo rast, mjaft filozofi, le të fillojmë!

Në këtë tutorial do t'ju duhet:

  1. bordi juaj prototipues
  2. një LED
  3. telat lidhës
  4. një rezistencë rreth 220 deri në 330 ohms
  5. Manuali i Setit të Udhëzimeve: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Fleta e të dhënave: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. një oshilator kristal të ndryshëm (opsional)

Këtu keni një lidhje me koleksionin e plotë të mësimeve:

Hapi 1: Ndërtimi i qarkut

Ndërtimi i qarkut
Ndërtimi i qarkut

Qarku në këtë tutorial është jashtëzakonisht i thjeshtë. Ne do të shkruajmë në thelb programin "blink", kështu që gjithçka që na nevojitet është në vijim.

Lidhni një LED në PD4, pastaj në një rezistencë 330 ohm, pastaj në Ground. dmth

PD4 - LED - R (330) - GND

dhe kjo është ajo!

Edhe pse teoria do të jetë e ashpër …

Hapi 2: Pse na duhen komentet dhe skedari M328Pdef.inc?

Unë mendoj se duhet të fillojmë duke treguar pse skedari i përfshirjes dhe komentet janë të dobishëm. Asnjëra prej tyre nuk është në të vërtetë e nevojshme dhe ju mund të shkruani, montoni dhe ngarkoni kodin në të njëjtën mënyrë pa to dhe do të funksionojë në mënyrë perfekte (edhe pse pa skedarin e përfshirë mund të merrni disa ankesa nga montuesi - por pa gabime)

Këtu është kodi që do të shkruajmë sot, përveç që kam hequr komentet dhe skedarin e përfshirë:

.pajisja ATmega328P

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

mjaft e thjeshte apo jo? Haha. Nëse e keni mbledhur dhe ngarkuar këtë skedar ju do të bëni që drita LED të ndizet me një shpejtësi prej 1 vezullim në sekondë me një ndezje që zgjat 1/2 sekondë dhe një pauzë midis pulsimeve që zgjat 1/2 sekondë.

Sidoqoftë, shikimi i këtij kodi nuk është aspak ndriçues. Nëse do të shkruanit një kod si ky dhe do të donit ta modifikonit ose ta ripërdoroni në të ardhmen do ta kishit të vështirë.

Pra, le të vendosim komentet dhe të përfshijmë skedarin përsëri në mënyrë që të kemi kuptim.

Hapi 3: Blink.asm

Këtu është kodi për të cilin do të diskutojmë sot:

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

; shkruar nga: 1o_o7; data:; version: 1.0; skedari i ruajtur si: blink.asm; për AVR: atmega328p; frekuenca e orës: 16MHz (opsionale); **********************************; Funksioni i programit: ---------------------; numëron sekonda duke ndezur një LED;; PD4 - LED - R (330 ohm) - GND;; --------------------------------------. nolist. Përfshi "./m328Pdef.inc". lista; ===============; Deklaratat:.def temp = r16.def tejkalon = r17.org 0x0000; vendndodhja e kujtesës (PC) e mbajtësit të rivendosur rjmp Reset; jmp kushton 2 cikle cpu dhe rjmp kushton vetëm 1; kështu nëse nuk keni nevojë të hidheni më shumë se 8k bajt; ju duhet vetëm rjmp. Prandaj disa mikrokontrollues vetëm; kanë rjmp dhe jo jmp.org 0x0020; vendndodhja e kujtesës së mbajtësit të mbingarkesës Timer0 rjmp overflow_handler; shkoni këtu nëse ndodh një ndërprerje e tejkalimit të timer0; ============ Rivendosni: ldi temp, 0b00000101 jashtë TCCR0B, temp; vendosni Bitet e Zgjedhësit të Orës CS00, CS01, CS02 në 101; kjo vendos Timer Counter0, TCNT0 në modalitetin FCPU/1024; kështu që shënon në frekuencën e CPU/1024 ldi temp, 0b00000001 sts TIMSK0, temp; vendosni bitin Timer Overflow Interrupt Enable (TOIE0); të Regjistrit të Maskave të Ndërprerjes së Kohëmatësit (TIMSK0) sei; aktivizoni ndërprerjet globale - ekuivalente me "sbi SREG, I" clr temp out TCNT0, temp; inicializoni Kohëmatësin/Numëruesin në 0 sbi DDRD, 4; vendosni PD4 në dalje; ======================; Trupi kryesor i programit: blink: sbi PORTD, 4; ndizni LED në vonesën e thirrjes PD4; vonesa do të jetë 1/2 sekondë cbi PORTD, 4; fikni LED në vonesën e thirrjes së PD4; vonesa do të jetë 1/2 sekondë rjmp blink; lak prapa në vonesën e fillimit: clr derdhet; vendosni vërshimet në 0 sec_count: cpi overflows, 30; krahaso numrin e daljeve dhe 30 brne sec_count; degë në back to sec_count nëse nuk është e barabartë ret; nëse kanë ndodhur 30 vërshime, kthehen në ndezje overflow_handler: inc overflows; shtoni 1 në përmbytjet e ndryshueshme të përmbytjeve të cpi, 61; krahaso me 61 brne PC+2; Programi Counter + 2 (kapërceni rreshtin tjetër) nëse nuk është e barabartë me clr; nëse ndodhin 61 vërshime, rivendoseni numëruesin në zero reti; kthehet nga ndërprerja

Siç mund ta shihni, komentet e mia janë pak më të shkurtra tani. Pasi të dimë se cilat janë komandat në grupin e udhëzimeve, nuk kemi nevojë ta shpjegojmë atë në komente. Ne vetëm duhet të shpjegojmë se çfarë po ndodh nga pikëpamja e programit.

Ne do të diskutojmë se çfarë bën e gjithë kjo pjesë për pjesë, por së pari le të përpiqemi të marrim një perspektivë globale. Trupi kryesor i programit funksionon si më poshtë.

Së pari ne vendosim bitin 4 të PORTD me "sbi PORTD, 4" kjo dërgon një 1 në PD4 e cila vendos tensionin në 5V në atë kunj. Kjo do të ndezë LED. Pastaj kalojmë në nënrutinën "vonesë" e cila numëron 1/2 e sekondës (do të shpjegojmë se si e bën këtë më vonë). Ne pastaj kthehemi të vezullojmë dhe pastrojmë bitin 4 në PORTD i cili vendos PD4 në 0V dhe kështu fik LED. Ne pastaj vonojmë për 1/2 sekondë tjetër, dhe pastaj hidhemi përsëri në fillim të mbylljes së syrit përsëri me "rjmp blink".

Ju duhet ta ekzekutoni këtë kod dhe të shihni se ai bën atë që duhet.

Dhe ja ku e keni! Kjo është gjithçka që bën ky kod fizikisht. Mekanika e brendshme e asaj që po bën mikrokontrolluesi janë pak më të përfshirë dhe kjo është arsyeja pse ne po bëjmë këtë tutorial. Pra, le të diskutojmë secilën pjesë me radhë.

Hapi 4:.org Direktivat e Assembler.org

Ne tashmë e dimë se çfarë bëjnë direktivat e.nolist,.list,.include dhe.def assembler nga mësimet tona të mëparshme, kështu që së pari le të hedhim një vështrim në 4 rreshtat e kodit që vijnë pas kësaj:

.org 0x0000

jmp Reset.org 0x0020 jmp overflow_handler

Deklarata.org i thotë montuesit se ku në "Program Memory" të vendos deklaratën tjetër. Ndërsa programi juaj ekzekutohet, "Counter Program" (i pakësuar si PC) përmban adresën e linjës aktuale që po ekzekutohet. Pra, në këtë rast kur kompjuteri është në 0x0000 do të shohë komandën "jmp Reset" që banon në atë vendndodhje të kujtesës. Arsyeja që ne duam të vendosim jmp Reset në atë vend është sepse kur programi fillon, ose çipi rivendoset, kompjuteri fillon të ekzekutojë kodin në këtë vend. Pra, siç mund ta shohim, sapo i kemi thënë që menjëherë të "hidhet" në pjesën e etiketuar "Rivendos". Pse e bëmë këtë? Kjo do të thotë që dy rreshtat e fundit më sipër po anashkalohen! Pse?

Epo, këtu gjërat bëhen interesante. Tani do të duhet të hapni një shikues pdf me fletën e të dhënave të plotë ATmega328p që tregova në faqen e parë të këtij tutoriali (kjo është arsyeja pse është pika 4 në seksionin "do t'ju duhet"). Nëse ekrani juaj është shumë i vogël, ose keni shumë dritare të hapura tashmë (siç është rasti me mua) ju mund të bëni atë që bëj unë dhe ta vendosni në një Ereader, ose telefonin tuaj Android. Do ta përdorni gjatë gjithë kohës nëse planifikoni të shkruani kodin e asamblesë. Gjëja interesante është se të gjithë mikrokontrolluesit janë të organizuar në mënyra shumë të ngjashme dhe kështu pasi të mësoheni me leximin e fletëve të të dhënave dhe kodimin e tyre, do ta shihni pothuajse të parëndësishme të bëni të njëjtën gjë për një mikrokontrollues të ndryshëm. Pra, ne në fakt po mësojmë se si të përdorim të gjithë mikrokontrolluesit në një kuptim dhe jo vetëm atmega328p.

Në rregull, kthehuni në faqen 18 në fletën e të dhënave dhe hidhini një sy Figurës 8-2.

Kështu është ngritur Memoria e Programit në mikrokontrollues. Mund të shihni që fillon me adresën 0x0000 dhe ndahet në dy seksione; një seksion flash aplikacioni dhe një seksion flash boot. Nëse i referoheni shkurtimisht faqes 277 tabela 27-14 do të shihni se seksioni flash i aplikacionit merr vendndodhjet nga 0x0000 në 0x37FF dhe seksioni flash boot merr vendet e mbetura nga 0x3800 në 0x3FFF.

Ushtrimi 1: Sa vende ka në memorien e Programit? Dmth shndërroni 3FFF në dhjetore dhe shtoni 1 pasi fillojmë të numërojmë me 0. Meqenëse secila vendndodhje e kujtesës është e gjerë 16 bit (ose 2 bajt) sa është numri i përgjithshëm i bajtëve të kujtesës? Tani konvertojeni këtë në kilobajt, duke kujtuar se ka 2^10 = 1024 byte në një kilobajt. Seksioni i boot flash shkon nga 0x3800 në 0x37FF, sa kilobajt është kjo? Sa kilobajt memorie na mbetet të përdorim për të ruajtur programin tonë? Me fjalë të tjera, sa i madh mund të jetë programi ynë? Së fundi, sa rreshta kodesh mund të kemi?

Në rregull, tani që dimë gjithçka për organizimin e kujtesës së programit flash, le të vazhdojmë me diskutimin tonë të deklaratave.org. Ne shohim që vendndodhja e parë e kujtesës 0x0000 përmban udhëzimet tona për të kaluar në pjesën tonë të etiketuar Reset. Tani ne shohim se çfarë bën deklarata ".org 0x0020". Ai thotë se ne duam që udhëzimi në rreshtin tjetër të vendoset në vendndodhjen e kujtesës 0x0020. Udhëzimi që kemi vendosur atje është një kërcim në një pjesë në kodin tonë që ne e kemi etiketuar "overflow_handler" … tani pse dreqin do të kërkojmë që ky kërcim të vendoset në vendndodhjen e kujtesës 0x0020? Për ta zbuluar, i drejtohemi faqes 65 në fletën e të dhënave dhe i hedhim një sy Tabelës 12-6.

Tabela 12-6 është një tabelë e "Reset and Interrupt Vectors" dhe tregon saktësisht se ku do të shkojë kompjuteri kur të marrë një "interrupt". Për shembull, nëse shikoni numrin Vector 1. "Burimi" i ndërprerjes është "RESET" i cili përcaktohet si "Pin i jashtëm, Reset i ndezjes, Rivendosja e Brown-out dhe rivendosja e sistemit Watchdog", nëse ka ndonjë ato gjëra ndodhin me mikrokontrolluesin tonë, kompjuteri do të fillojë të ekzekutojë programin tonë në vendndodhjen e kujtesës së programit 0x0000. Po në lidhje me direktivën tonë.org atëherë? Epo, ne vendosëm një komandë në vendndodhjen e kujtesës 0x0020 dhe nëse shikoni poshtë tabelës do të shihni se nëse ndodh një tejmbushje e Kohëmatësit/Counter0 (që vjen nga TIMER0 OVF) do të ekzekutojë gjithçka që është në vendndodhjen 0x0020. Pra, sa herë që të ndodhë kjo, kompjuteri do të hidhet në vendin që e etiketuam "overflow_handler". Ftohtë apo jo? Do të shihni në një minutë pse e bëmë këtë, por së pari le ta përfundojmë këtë hap të mësimit me një mënjanë.

Nëse duam ta bëjmë kodin tonë më të pastër dhe të rregullt, me të vërtetë duhet të zëvendësojmë 4 rreshtat që po diskutojmë aktualisht me sa vijon (shiko faqen 66):

.org 0x0000

rjmp Rivendos; 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

Kështu që nëse ndodh një ndërprerje e caktuar ajo thjesht do të "reti" që do të thotë "kthim nga ndërprerja" dhe asgjë tjetër nuk ndodh. Por nëse ne kurrë nuk "aktivizojmë" këto ndërprerje të ndryshme, atëherë ato nuk do të përdoren dhe ne mund të vendosim kodin e programit në këto pika. Në programin tonë aktual "blink.asm" ne do të mundësojmë vetëm ndërprerjen e daljes së timer0 (dhe natyrisht ndërprerjen e rivendosjes e cila është gjithmonë e aktivizuar) dhe kështu ne nuk do të shqetësohemi me të tjerët.

Si ta "aktivizojmë" ndërprerjen e tejkalimit të timer0 atëherë? … Kjo është tema e hapit tonë të ardhshëm në këtë tutorial.

Hapi 5: Kohëmatësi/Numëruesi 0

Kohëmatësi/Numëruesi 0
Kohëmatësi/Numëruesi 0

Hidhini një sy fotos së mësipërme. Ky është procesi i vendimmarrjes së "PC" kur një ndikim i jashtëm "ndërpret" rrjedhën e programit tonë. Gjëja e parë që bën kur merr një sinjal nga jashtë se ka ndodhur një ndërprerje është ajo që kontrollon nëse kemi vendosur bitin e "interrupt enable" për atë lloj ndërprerjeje. Nëse nuk e kemi bërë, atëherë ai thjesht vazhdon të ekzekutojë linjën tonë të ardhshme të kodit. Nëse e kemi vendosur atë bit të aktivizimit të ndërprerjes (në mënyrë që të ketë një 1 në atë vend bit në vend të 0), atëherë do të kontrollojë nëse kemi aktivizuar apo jo "ndërprerjet globale", nëse jo, përsëri do të kalojë në rreshtin tjetër të kodit dhe vazhdoni. Nëse kemi aktivizuar edhe ndërprerjet globale, atëherë do të shkojë në vendndodhjen e Program Memory të atij lloji të ndërprerjes (siç tregohet në Tabelën 12-6) dhe do të ekzekutojë çfarëdo komande që kemi vendosur atje. Pra, le të shohim se si i kemi zbatuar të gjitha këto në kodin tonë.

Seksioni i etiketuar Reset i kodit tonë fillon me dy rreshtat e mëposhtëm:

Rivendos:

ldi temp, 0b00000101 jashtë TCCR0B, temp

Siç e dimë tashmë, kjo ngarkon në temp (dmth. R16) numrin menjëherë pas, i cili është 0b00000101. Pastaj e shkruan këtë numër në regjistrin e quajtur TCCR0B duke përdorur komandën "jashtë". Çfarë është ky regjistër? Epo, le të kalojmë në faqen 614 të fletës së të dhënave. Kjo është në mes të një tabele që përmbledh të gjithë regjistrat. Në adresën 0x25 do të gjeni TCCR0B. (Tani e dini se nga vinte vija "jashtë 0x25, r16" në versionin tim të pa komentuar të kodit). Ne shohim nga segmenti i kodit më sipër që kemi vendosur bitin 0 dhe bitin e dytë dhe kemi pastruar të gjithë pjesën tjetër. Duke parë tabelën mund të shihni se kjo do të thotë që ne kemi vendosur CS00 dhe CS02. Tani le të kalojmë në kapitullin në fletën e të dhënave të quajtur "Timer 8-bit/Counter0 me PWM". Në veçanti, shkoni në faqen 107 të atij kapitulli. Do të shihni të njëjtin përshkrim të regjistrit "Regjistri i Kohëmatësit/Kontrollit B" (TCCR0B) që sapo pamë në tabelën përmbledhëse të regjistrit (kështu që ne mund të kishim ardhur këtu direkt, por doja që ju të shihni se si të përdorni tabelat përmbledhëse për referencë në të ardhmen). Fleta e të dhënave vazhdon të japë një përshkrim të secilës prej bitëve në atë regjistër dhe çfarë bëjnë ata. Ne do t'i kalojmë të gjitha ato tani për tani dhe do ta kthejmë faqen në Tabelën 15-9. Kjo tabelë tregon "Përshkrimi i bitit të orës së zgjedhur". Tani shikoni atë tabelë derisa të gjeni vijën që korrespondon me bitët që sapo kemi vendosur në atë regjistër. Linja thotë "clk/1024 (nga prescaler)". Çfarë do të thotë kjo është se ne duam që Timer/Counter0 (TCNT0) të shënojë së bashku me një normë që është frekuenca e CPU e ndarë me 1024. Meqenëse ne kemi mikrokontrolluesin tonë të ushqyer nga një oshilator kristal 16MHz kjo do të thotë se shkalla që CPU -ja jonë ekzekuton udhëzimet është 16 milion udhëzime në sekondë. Pra, shkalla që do të shënojë numëruesi ynë TCNT0 është atëherë 16 milion/1024 = 15625 herë në sekondë (provojeni me pjesë të ndryshme të orës të zgjedhura dhe shihni se çfarë ndodh - mbani mend filozofinë tonë?). Le ta mbajmë numrin 15625 në pjesën e prapme të mendjes sonë për më vonë dhe të kalojmë në dy rreshtat e ardhshëm të kodit:

ldi temp, 0b00000001

rr TIMSK0, temp

Kjo vendos bitin 0 të një regjistri të quajtur TIMSK0 dhe pastron të gjithë pjesën tjetër. Nëse i hidhni një sy faqes 109 në fletën e të dhënave, do të shihni që TIMSK0 qëndron për "Timer/Counter Interrupt Mask Register 0" dhe kodi ynë ka vendosur bitin 0 që quhet TOIE0 që qëndron për "Timer/Counter0 Overflow Interrupt Enable" … Aty! Tani e shihni se për çfarë bëhet fjalë. Tani kemi "grupin e aktivizimit të ndërprerjes së bitit" siç donim nga vendimi i parë në figurën tonë në krye. Pra, tani gjithçka që duhet të bëjmë është të aktivizojmë "ndërprerjet globale" dhe programi ynë do të jetë në gjendje t'i përgjigjet këtyre lloj ndërprerjesh. Ne do të mundësojmë ndërprerjet globale së shpejti, por para se ta bëjmë këtë ju mund të keni qenë të hutuar nga diçka.. pse dreqin e kam përdorur komandën "sts" për të kopjuar në regjistrin TIMSK0 në vend të "out" -it të zakonshëm?

Sa herë që më shihni të përdor një udhëzim që nuk e keni parë më parë, gjëja e parë që duhet të bëni është të ktheheni në faqen 616 në fletën e të dhënave. Kjo është "Përmbledhja e Setit të Udhëzimeve". Tani gjeni udhëzimin "STS" i cili është ai që kam përdorur. Thotë se merr një numër nga një regjistër R (kemi përdorur R16) dhe "Ruaj direkt në SRAM" vendndodhjen k (në rastin tonë të dhënë nga TIMSK0). Pra, pse na është dashur të përdorim "sts" i cili merr 2 cikle sahati (shiko kolonën e fundit në tabelë) për të ruajtur në TIMSK0 dhe na duhej vetëm "jashtë", e cila merr vetëm një cikël sahati, për të ruajtur në TCCR0B më parë? Për t'iu përgjigjur kësaj pyetjeje ne duhet të kthehemi në tabelën përmbledhëse të regjistrit tonë në faqen 614. E shihni që regjistri TCCR0B është në adresën 0x25 por edhe në (0x45) apo jo? Kjo do të thotë se është një regjistër në SRAM, por është gjithashtu një lloj regjistri i caktuar i quajtur "port" (ose regjistër i/o). Nëse shikoni tabelën përmbledhëse të udhëzimeve pranë komandës "jashtë" do të shihni se merr vlera nga "regjistrat e punës" si R16 dhe i dërgon ato në një PORT. Kështu që ne mund të përdorim "jashtë" kur i shkruajmë TCCR0B dhe të ruajmë një cikël orësh. Por tani kërkoni TIMSK0 në tabelën e regjistrit. E shihni që ka adresën 0x6e. Kjo është jashtë gamës së porteve (të cilat janë vetëm vendet e para 0x3F të SRAM) dhe kështu ju duhet të ktheheni në përdorimin e komandës sts dhe marrjen e dy cikleve të orës CPU për ta bërë këtë. Ju lutemi lexoni Shënimin 4 në fund të tabelës përmbledhëse të udhëzimeve në faqen 615 tani. Gjithashtu vini re se të gjitha portet tona hyrëse dhe dalëse, si PORTD janë të vendosura në fund të tabelës. Për shembull, PD4 është bit 4 në adresën 0x0b (tani e shihni se nga erdhën të gjitha gjërat 0x0b në kodin tim të pa komentuar!).. mirë, pyetje e shpejtë: a i ndryshuat "sts" në "out" dhe shihni se çfarë ndodh? Mos harroni filozofinë tonë! prish atë! mos e merr fjalën time vetëm për gjërat.

Në rregull, para se të vazhdojmë, kthehuni në faqen 19 në fletën e të dhënave për një minutë. Ju shihni një fotografi të kujtesës së të dhënave (SRAM). 32 regjistrat e parë në SRAM (nga 0x0000 në 0x001F) janë "regjistrat e punës me qëllime të përgjithshme" R0 deri në R31 që ne i përdorim gjatë gjithë kohës si ndryshore në kodin tonë.64 regjistrat e ardhshëm janë portat e hyrjes/daljes deri në 0x005f (dmth. Ato për të cilat po flisnim që kanë ato adresa pa kllapa pranë tyre në tabelën e regjistrit të cilat mund të përdorim komandën "jashtë" në vend të "sts") pjesa tjetër e SRAM përmban të gjithë regjistrat e tjerë në tabelën përmbledhëse deri në adresën 0x00FF, dhe së fundmi pjesa tjetër është SRAM e brendshme. Tani shpejt, le të kthehemi në faqen 12 për një sekondë. Aty shihni një tabelë të "regjistrave të punës me qëllime të përgjithshme" që ne i përdorim gjithmonë si variablat tanë. E shihni vijën e trashë midis numrave R0 në R15 dhe pastaj R16 në R31? Kjo linjë është arsyeja pse ne gjithmonë përdorim R16 si më të voglin dhe unë do të futem në të pak më shumë në tutorialin tjetër ku do të na duhen edhe tre regjistrat e adresave indirekte 16-bit, X, Y dhe Z. Unë nuk do futuni në atë akoma edhe pse meqenëse nuk kemi nevojë për të tani dhe po mbërthehemi mjaft këtu.

Kthejeni një faqe në faqen 11 të fletës së të dhënave. Do të shihni një diagram të regjistrit SREG në krye djathtas? Ju shikoni se bit 7 i atij regjistri quhet "Unë". Tani zbritni poshtë faqes dhe lexoni përshkrimin e Bit 7…. po Bitshtë biti i Ndërprerjes Globale të aktivizuar. Kjo është ajo që ne duhet të vendosim në mënyrë që të kalojmë përmes vendimit të dytë në diagramin tonë të mësipërm dhe të lejojmë ndërprerjet e tejkalimit të kohëmatësit/numëruesit në programin tonë. Pra, rreshti tjetër i programit tonë duhet të lexojë:

sbi SREG, I

e cila vendos bitin e quajtur "I" në regjistrin SREG. Sidoqoftë, në vend të kësaj ne kemi përdorur udhëzimin

sei

në vend të kësaj. Ky bit është vendosur aq shpesh në programet saqë ata thjesht bënë një mënyrë më të thjeshtë për ta bërë atë.

Mirë! Tani ne i kemi gati ndërprerjet e tejmbushjes në mënyrë që "jmp overflow_handler" ynë të ekzekutohet sa herë që ndodh.

Para se të vazhdojmë më tej, hidhini një sy regjistrit SREG (Regjistri i Statusit) sepse është shumë i rëndësishëm. Lexoni se çfarë përfaqëson secili prej flamujve. Në veçanti, shumë nga udhëzimet që ne përdorim do të vendosin dhe kontrollojnë këta flamuj gjatë gjithë kohës. Për shembull, më vonë do të përdorim komandën "CPI" që do të thotë "krahaso menjëherë". Hidhini një sy tabelës përmbledhëse të udhëzimeve për këtë udhëzim dhe vini re se sa flamuj vendos në kolonën "flamuj". Të gjithë këta janë flamuj në SREG dhe kodi ynë do t'i vendosë dhe kontrollojë vazhdimisht. Së shpejti do të shihni shembuj. Më në fund pjesa e fundit e këtij seksioni të kodit është:

clr temp

jashtë TCNT0, temp sbi DDRD, 4

Linja e fundit këtu është mjaft e qartë. Thjesht vendos bitin e 4 -të të Regjistrit të Drejtimit të të Dhënave për PortD duke bërë që PD4 të dalë.

E para vendos temperaturën e ndryshueshme në zero dhe pastaj e kopjon atë në regjistrin TCNT0. TCNT0 është Kohëmatësi/Counter0 ynë. Kjo e vendos atë në zero. Sapo kompjuteri të ekzekutojë këtë linjë, kohëmatësi0 do të fillojë në zero dhe do të llogaritet në një normë prej 15625 herë çdo sekondë. Problemi është ky: TCNT0 është një regjistër "8-bit", apo jo? Pra cili është numri më i madh që mund të mbajë një regjistër 8-bitësh? Epo 0b11111111 është. Ky është numri 0xFF. Cila është 255. Pra e shihni se çfarë ndodh? Kohëmatësi po zmadhohet duke u rritur 15625 herë në sekondë dhe sa herë që arrin 255 "mbinxeh" dhe kthehet përsëri në 0. Në të njëjtën kohë kur kthehet në zero, ai dërgon një sinjal të ndërprerjes së tejkalimit të kohëmatësit. PC e merr këtë dhe e dini se çfarë bën deri tani? Po Shkon në vendndodhjen e Program Memory 0x0020 dhe ekzekuton udhëzimin që gjen atje.

Shkëlqyeshëm! Nëse jeni akoma me mua, atëherë jeni një superhero i palodhur! Le të vazhdojmë…

Hapi 6: Trajtuesi i tejmbushjes

Pra, le të supozojmë se regjistri i kohëmatësit/counter0 sapo ka vërshuar. Tani e dimë që programi merr një sinjal ndërprerjeje dhe ekzekuton 0x0020 i cili i thotë Programit Counter, PC të hidhet në etiketën "overflow_handler", në vijim është kodi që kemi shkruar pas atij etikete:

administruesi i tejmbushjes:

inc tejkalon cpi tejkalon, 61 brne PC+2 clr tejmbush reti

Gjëja e parë që bën është rritja e ndryshores "overflows" (i cili është emri ynë për regjistrin e punës për qëllime të përgjithshme R17) pastaj "krahason" përmbajtjen e vërshimeve me numrin 61. Mënyra se si funksionon udhëzimi cpi është se thjesht zbret dy numrat dhe nëse rezultati është zero vendos flamurin Z në regjistrin SREG (ju thashë që do ta shihnim këtë regjistër gjatë gjithë kohës). Nëse të dy numrat janë të barabartë atëherë flamuri Z do të jetë 1, nëse të dy numrat nuk janë të barabartë atëherë do të jetë 0.

Rreshti tjetër thotë "brne PC+2" që do të thotë "degë nëse nuk është e barabartë". Në thelb, ai kontrollon flamurin Z në SREG dhe nëse NUK është një (dmth. Të dy numrat nuk janë të barabartë, nëse do të ishin të barabartë, flamuri zero do të vendoset) PC -ja degëzon në PC+2, që do të thotë se kalon tjetrën rresht dhe shkon direkt në "reti" e cila kthehet nga ndërprerja në çfarëdo vendi që ishte në kod kur erdhi ndërprerja. Nëse udhëzimi i brne gjen një 1 në bitin e flamurit zero, ai nuk do të degëzohet dhe në vend të tij ai thjesht do të vazhdojë në rreshtin tjetër i cili do të rrotullohet duke e rivendosur atë në 0.

Cili është rezultati neto i gjithë kësaj?

Epo ne shohim që çdo herë që ka një mbingarkesë të kohëmatësit ky mbajtës rrit vlerën e "tejmbushjeve" me një. Pra, variabla "tejmbushur" po numëron numrin e tejmbushjeve ashtu siç ndodhin. Sa herë që numri arrin 61 ne e rivendosim atë në zero.

Tani pse në botë do ta bënim këtë?

Le të shohim. Kujtoni që shpejtësia e orës sonë për CPU -në tonë është 16MHz dhe ne e "preskaluam" atë duke përdorur TCCR0B në mënyrë që kohëmatësi të llogarisë vetëm në një normë prej 15625 numërime për sekondë, apo jo? Dhe sa herë që kohëmatësi arrin një numër prej 255, ai mbinxeh. Pra, kjo do të thotë se vërshon 15625/256 = 61.04 herë në sekondë. Ne po mbajmë gjurmët e numrit të tejmbushjeve me variablin tonë "tejmbushje" dhe po e krahasojmë atë numër me 61. Pra, shohim që "tejmbushjet" do të jenë të barabarta me 61 një herë në sekondë! Pra, mbajtësi ynë do të rivendosë "vërshimet" në zero një herë në sekondë. Pra, nëse thjesht do të monitoronim variablin "vërshimet" dhe do të merrnim parasysh çdo herë që rivendoset në zero, ne do të numëronim sekondë pas sekonde në kohë reale (Vini re se në tutorialin tjetër do të tregojmë se si të marrim një më të saktë vonesa në milisekonda në të njëjtën mënyrë si funksionon rutina e "vonesës" Arduino).

Tani ne kemi "trajtuar" ndërprerjet e tejkalimit të kohëmatësit. Sigurohuni që e kuptoni se si funksionon kjo dhe pastaj kaloni në hapin tjetër ku ne e përdorim këtë fakt.

Hapi 7: Vonesa

Tani që kemi parë që rutina jonë e rregullimit të ndërprerjeve të tejmbushjes së kohëmatësit "overflow_handler" do të vendosë ndryshoren "overflows" në zero një herë në çdo sekondë, ne mund ta përdorim këtë fakt për të hartuar një nënrutinë "vonesë".

Hidhini një sy kodit të mëposhtëm nga vonesa jonë: etiketa

vonesa:

clr derdhet sek_count: cpi tejkalon, 30 brne sec_count ret

Ne do ta quajmë këtë nëngrup çdo herë që kemi nevojë për një vonesë në programin tonë. Mënyra se si funksionon është se së pari e vendos variablin "overflows" në zero. Pastaj futet në një zonë të etiketuar "sec_count" dhe krahason vërshimet me 30, nëse nuk janë të barabarta ajo degëzohet përsëri në etiketën sec_count dhe krahasohet përsëri, dhe përsëri, etj. Derisa të jenë përfundimisht të barabarta (mbani mend se gjatë gjithë kohës kjo po ndodh në kohëmatësin tonë mbajtësi i ndërprerjes po vazhdon të rrisë tejmbushjet e ndryshueshme dhe kështu po ndryshon sa herë që ne sillemi këtu. Kur vërshimet përfundimisht janë të barabarta me 30, del nga laku dhe kthehet atje ku ne e quajmë vonesë: nga. Rezultati neto është një vonesë prej 1/2 sekondë

Ushtrimi 2: Ndryshoni rutinën e overflow_handler në sa vijon:

administruesi i tejmbushjes:

inc tejmbush reti

dhe drejtojeni programin. A ka ndonje gje ndryshe? Pse ose pse jo?

Hapi 8: Blink

Së fundi, le të shohim rutinën e mbylljes së syve:

mbyll sytë:

sbi PORTD, 4 rcall vonesë cbi PORTD, 4 rcall vonesë rjmp blink

Së pari ndezim PD4, më pas thërrasim nënshtresën tonë të vonesës. Ne përdorim rcall në mënyrë që kur kompjuteri të arrijë në një deklaratë "ret" të kthehet në linjën pas rcall. Pastaj rutina e vonesës vonon për 30 numërime në ndryshoren e tejmbushjes siç e kemi parë dhe kjo është pothuajse saktësisht 1/2 sekondë, pastaj fikim PD4, vonojmë një 1/2 sekondë tjetër dhe pastaj kthehemi përsëri në fillim.

Rezultati neto është një LED i ndezur!

Unë mendoj se tani do të bini dakord se "blink" nuk është ndoshta programi më i mirë "hello world" në gjuhën e asamblesë.

Ushtrimi 3: Ndryshoni parametrat e ndryshëm në program në mënyrë që LED të pulsojë me ritme të ndryshme si një sekondë ose 4 herë në sekondë, etj. Ushtrimi 4: Ndryshoni atë në mënyrë që LED të ndizet dhe fiket për sasi të ndryshme kohe. Për shembull, ndezur për 1/4 sekondë dhe pastaj fikeni për 2 sekonda ose diçka të tillë. Ushtrimi 5: Ndryshoni orën TCCR0B zgjidhni bit në 100 dhe pastaj vazhdoni të ngjiteni në tryezë. Në cilën pikë bëhet e padallueshme nga programi ynë "hello.asm" nga mësimi 1? Ushtrimi 6 (opsional): Nëse keni një oshilator kristal të ndryshëm, si një 4 MHz ose një 13.5 MHz ose çfarëdo, ndryshoni oshilatorin tuaj 16 MHz në tabelën tuaj të bukës për të renë dhe shihni se si ndikon kjo në shkallën e ndezjes së LED. Tani duhet të jeni në gjendje të kaloni llogaritjen e saktë dhe të parashikoni saktësisht se si do të ndikojë në normën.

Hapi 9: Përfundim

Për ata prej jush të guximshëm që arritën deri këtu, Urime!

Unë e kuptoj se është mjaft e vështirë të ngadalësosh kur lexon dhe shikon më shumë se sa kërkon dhe eksperimenton, por shpresoj se keni mësuar gjërat e mëposhtme të rëndësishme:

  1. Si funksionon Memoria e Programit
  2. Si funksionon SRAM
  3. Si të kërkoni regjistra
  4. Si të shikoni udhëzimet dhe të dini se çfarë bëjnë ata
  5. Si të zbatoni ndërprerjet
  6. Si CP ekzekuton kodin, si funksionon SREG dhe çfarë ndodh gjatë ndërprerjeve
  7. Si të bëni sythe dhe kërcime dhe të kërcej përreth në kod
  8. Sa e rëndësishme është të lexosh fletën e të dhënave!
  9. Sapo të dini se si t'i bëni të gjitha këto për mikrokontrolluesin Atmega328p, do të jetë një shëtitje relative e tortës për të mësuar ndonjë kontrollues të ri që ju intereson.
  10. Si ta ndryshoni kohën e CPU -së në kohë reale dhe ta përdorni atë në rutinat e vonesës.

Tani që kemi shumë teori nga rruga, ne jemi në gjendje të shkruajmë kod më të mirë dhe të kontrollojmë gjëra më të ndërlikuara. Pra, tutoriali tjetër do të bëjmë pikërisht atë. Ne do të ndërtojmë një qark më të komplikuar, më interesant dhe do ta kontrollojmë atë në mënyra argëtuese.

Ushtrimi 7: "Thyej" kodin në mënyra të ndryshme dhe shiko se çfarë ndodh! Kuriozitet shkencor foshnjë! Dikush tjetër mund t'i lajë enët mirë? Ushtrimi 8: Mblidhni kodin duke përdorur opsionin "-l" për të krijuar një skedar liste. Dmth "avra -l blink.lst blink.asm" dhe hidhini një sy skedarit të listës. Kredi shtesë: Kodi i pa komentuar që dhashë në fillim dhe kodi i komentuar që diskutojmë më vonë ndryshojnë! Ekziston një linjë e kodit që është e ndryshme. Mund ta gjeni? Pse nuk ka rëndësi ai ndryshim?

Shpresoj se u argëtuat! Shihemi herën tjetër…

Recommended: