Taula de continguts:

Tutorial AVR Assembler 3: 9 passos
Tutorial AVR Assembler 3: 9 passos

Vídeo: Tutorial AVR Assembler 3: 9 passos

Vídeo: Tutorial AVR Assembler 3: 9 passos
Vídeo: AVR Ассемблер. Урок 3. Таймер. Мигалка на таймере. AVR Assembler. Lesson 3. Timer. 2024, Juliol
Anonim
Tutorial AVR Assembler 3
Tutorial AVR Assembler 3

Benvingut al tutorial número 3!

Abans de començar vull fer un punt filosòfic. No tingueu por d’experimentar amb els circuits i el codi que estem construint en aquests tutorials. Canvieu els cables, afegiu components nous, traieu components, canvieu línies de codi, afegiu línies noves, suprimiu línies i vegeu què passa. És molt difícil trencar res i, si ho fas, a qui li importa? Res que fem servir, inclòs el microcontrolador, és molt car i sempre és educatiu veure com poden fallar les coses. No només descobrireu què no heu de fer la propera vegada, sinó que, sobretot, sabreu per què no fer-ho. Si ets com jo, quan eres petit i vas aconseguir una joguina nova, no va passar molt de temps abans de tenir-lo a trossos per veure què feia que es marqués bé? De vegades, la joguina acabava danyada irreparablement, però no era gran cosa. Permetre que un nen explori la seva curiositat fins i tot fins a trencar joguines és el que el converteix en un científic o enginyer en lloc de rentavaixelles.

Avui anem a connectar un circuit molt senzill i després ens endinsarem una mica en la teoria. Ho sentim, però necessitem les eines. Prometo que compensarem això al tutorial 4, on farem una construcció de circuits més seriosa i el resultat serà força maco. Tot i això, la manera com heu de fer tots aquests tutorials és d’una manera molt lenta i contemplativa. Si només llaureu, creeu el circuit, copieu i enganxeu el codi i executeu-lo, segur que funcionarà, però no aprendreu res. Cal pensar en cada línia. Pausa. Experiment. Inventa. Si ho feu així, al final del 5è tutorial, deixareu de construir coses interessants i no necessiteu més tutories. En cas contrari, simplement mireu en lloc d'aprendre i crear.

En qualsevol cas, prou filosofia, comencem!

En aquest tutorial necessitareu:

  1. la vostra placa de prototipatge
  2. un LED
  3. connexió de cables
  4. una resistència d’uns 220 a 330 ohms
  5. El manual del conjunt d’instruccions: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. El full de dades: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. un oscil·lador de cristall diferent (opcional)

Aquí hi ha un enllaç a la col·lecció completa de tutorials:

Pas 1: construcció del circuit

Construint el circuit
Construint el circuit

El circuit d’aquest tutorial és extremadament senzill. Bàsicament escriurem el programa "parpellejar", de manera que tot el que necessitem és el següent.

Connecteu un LED a PD4, després a una resistència de 330 ohms i després a terra. és a dir, PD4 - LED - R (330) - GND

i això és tot!

Tanmateix, la teoria serà molt dura …

Pas 2: per què necessitem els comentaris i el fitxer M328Pdef.inc?

Crec que hauríem de començar mostrant per què són útils el fitxer d’inclusió i els comentaris. Cap d’ells no és realment necessari i podeu escriure, muntar i penjar codi de la mateixa manera sense ells i funcionarà perfectament (tot i que sense el fitxer d’inclusió és possible que rebeu queixes del muntador, però no hi ha errors)

Aquí teniu el codi que escriurem avui, tret que he eliminat els comentaris i el fitxer d’inclusió:

.aparell ATmega328P

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

força senzill oi? Haha. Si heu muntat i penjat aquest fitxer, provocareu que el LED parpellegi a un ritme d’1 parpelleig per segon, el parpelleig durarà 1/2 segon i la pausa entre parpelleigs durarà 1/2 segon.

Tanmateix, mirar aquest codi és difícilment aclaridor. Si escrivíssiu codi com aquest i volguéssiu modificar-lo o reutilitzar-lo en el futur, us costaria molt.

Posem, doncs, els comentaris i incloem el fitxer perquè puguem donar-hi sentit.

Pas 3: Blink.asm

Aquí teniu el codi que parlarem avui:

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

; escrit per: 1o_o7; data:; versió: 1.0; arxiu desat com: blink.asm; per a AVR: atmega328p; freqüència de rellotge: 16 MHz (opcional); *************************************; Funció del programa: ---------------------; compta els segons parpellejant un LED;; PD4 - LED - R (330 ohm) - GND;; --------------------------------------.nolist.include "./m328Pdef.inc".list; ==============; Declaracions:.def temp = r16.def overflow = r17.org 0x0000; memòria (PC) ubicació del gestor de restabliments rjmp Restabliment; jmp costa 2 cicles de CPU i RJMP només 1; per tant, tret que hàgiu de saltar més de 8k bytes; només necessiteu rjmp. Alguns microcontroladors, per tant, només; tenen rjmp i no jmp.org 0x0020; ubicació de memòria del controlador de desbordament Timer0 rjmp overflow_handler; aneu aquí si es produeix una interrupció de desbordament de timer0; ============ Restableix: ldi temp, 0b00000101 out TCCR0B, temp; configureu els bits del selector de rellotge CS00, CS01, CS02 a 101; això posa Timer Counter0, TCNT0 al mode FCPU / 1024; per tant, marca la freq de la CPU / 1024 ldi temp, 0b00000001 pts TIMSK0, temp; configureu el bit d’activació d’interrupció del desbordament del temporitzador (TOIE0); del registre de màscares d'interrupció del temporitzador (TIMSK0) sei; habilita les interrupcions globals - equivalent a "sbi SREG, I" clr temp out TCNT0, temp; inicialitzar el temporitzador / comptador a 0 sbi DDRD, 4; configureu PD4 a la sortida; ======================; Cos principal del programa: parpelleig: sbi PORTD, 4; encendre el LED en retard de trucada PD4; el retard serà de 1/2 segon cbi PORTD, 4; apagueu el LED en retard de trucada PD4; el retard serà de 1/2 segon parpelleig rjmp; retrocedeix al retard d’inici: desbordaments clr; estableix els desbordaments a 0 seg_count: desbordaments CPI, 30; compareu el nombre de desbordaments i 30 brn sec_count; branca per tornar a sec_count si no és igual ret; si s'han produït 30 desbordaments, torneu a parpellejar overflow_handler: inc desbordaments; afegiu 1 a la variable desbordaments cpi desbordaments, 61; comparar amb 61 brne PC + 2; Programa Comptador + 2 (salta la línia següent) si no és igual de desbordaments de clr; si es van produir 61 desbordaments, restableix el comptador a zero reti; tornar de la interrupció

Com podeu veure, ara els meus comentaris són una mica més breus. Un cop sabem què fan les ordres del conjunt d’instruccions, no cal que ho expliquem als comentaris. Només ens cal explicar què passa des del punt de vista del programa.

Anem a discutir què fa tot això peça a peça, però primer intentem obtenir una perspectiva global. El cos principal del programa funciona de la següent manera.

Primer establim el bit 4 de PORTD amb "sbi PORTD, 4", que envia un 1 a PD4 que posa el voltatge a 5V en aquest pin. S'encendrà el LED. Aleshores saltem a la subrutina "delay" que compta 1/2 segon (explicarem com ho fa més endavant). Després tornem a parpellejar i esborreu el bit 4 de PORTD, que estableix PD4 a 0V i, per tant, apaga el LED. A continuació, retardem 1/2 segon més i tornem a saltar al començament del parpelleig amb "rjmp parpellejar".

Heu d'executar aquest codi i veure que fa el que hauria de fer.

I aquí ho teniu! Això és tot el que fa físicament aquest codi. La mecànica interna del que fa el microcontrolador està una mica més implicada i per això estem fent aquest tutorial. Analitzem, doncs, cada secció al seu torn.

Pas 4: Directives de muntatge.org

Ja sabem què fan les directrius de muntadors.nolist,.list,.include i.def dels nostres tutorials anteriors, així que primer fem una ullada a les 4 línies de codi que vénen després:

.org 0x0000

jmp Restableix.org 0x0020 jmp overflow_handler

La declaració.org indica al muntador on està a "Memòria del programa" per posar la següent declaració. A mesura que s'executa el programa, el "Programa comptador" (abreviat com a PC) conté l'adreça de la línia actual que s'està executant. Així doncs, en aquest cas, quan el PC estigui a 0x0000, veurà l'ordre "jmp Reset" que resideix en aquesta ubicació de memòria. La raó per la qual volem posar jmp Reset en aquesta ubicació és perquè quan comença el programa o es reinicia el xip, el PC comença a executar codi en aquest punt. Per tant, com podem veure, li acabem de dir que "salti" immediatament a la secció anomenada "Restableix". Per què ho vam fer? Això significa que les dues darreres línies anteriors s’estan saltant. Per què?

Doncs aquí és on les coses es posen interessants. Ara haurà d’obrir un visor de pdf amb el full de dades ATmega328p complet que he assenyalat a la primera pàgina d’aquest tutorial (per això és l’ítem 4 de la secció "necessitareu"). Si la vostra pantalla és massa petita o si teniu massa finestres obertes (com és el cas de mi), podríeu fer el que faig i posar-lo en un Ereader o al vostre telèfon Android. L’utilitzaràs tot el temps si teniu previst escriure el codi de muntatge. El més interessant és que tots els microcontroladors s’organitzen de maneres molt similars i, per tant, un cop us hàgiu acostumat a llegir fulls de dades i codificar-los, serà gairebé trivial fer el mateix per a un microcontrolador diferent. Per tant, en realitat estem aprenent a utilitzar tots els microcontroladors en un sentit i no només l’atmega328p.

D'acord, aneu a la pàgina 18 del full de dades i mireu la figura 8-2.

Així es configura la memòria del programa al microcontrolador. Podeu veure que comença amb l'adreça 0x0000 i està separat en dues seccions; una secció de flaix d’aplicació i una secció de flaix d’arrencada. Si feu referència breument a la pàgina 277 de la taula 27-14, veureu que la secció de flaix de l’aplicació ocupa les ubicacions de 0x0000 a 0x37FF i la secció de flaix d’arrencada ocupa la resta de localitzacions de 0x3800 a 0x3FFF.

Exercici 1: quantes ubicacions hi ha a la memòria del programa? És a dir, converteix 3FFF en decimal i afegiu-ne 1 ja que comencem a comptar a 0. Com que cada ubicació de memòria té una amplada de 16 bits (o 2 bytes), quin és el nombre total de bytes de memòria? Ara convertiu-lo en kilobytes, recordant que hi ha 2 ^ 10 = 1024 bytes en un kilobyte. La secció de flash d’arrencada va de 0x3800 a 0x37FF, quants kilobytes és això? Quants quilobytes de memòria ens queden per utilitzar per emmagatzemar el nostre programa? En altres paraules, fins a quin punt pot ser el nostre programa? Finalment, quantes línies de codi podem tenir?

Molt bé, ara que ja sabem tot sobre l’organització de la memòria del programa flash, continuem amb la discussió de les declaracions.org. Veiem que la primera ubicació de memòria 0x0000 conté la nostra instrucció per anar a la nostra secció que hem etiquetat com a Restableix. Ara veiem què fa la declaració ".org 0x0020". Diu que volem que la instrucció de la següent línia es col·loqui a la ubicació de memòria 0x0020. La instrucció que hi hem posat és un salt a una secció del nostre codi que hem etiquetat com a "overflow_handler" … ara, què diable exigiríem que aquest salt es situés a la ubicació de memòria 0x0020? Per esbrinar-ho, passem a la pàgina 65 del full de dades i fem una ullada a la taula 12-6.

La taula 12-6 és una taula de "Restablir i interrompre els vectors" i mostra exactament cap a on anirà el PC quan rebi una "interrupció". Per exemple, si mireu el vector número 1. La "font" de la interrupció és "RESET", que es defineix com "Pin extern, restabliment d'engegada, restabliment marró i restabliment del sistema de vigilància", si hi ha aquestes coses passen al nostre microcontrolador, el PC començarà a executar el nostre programa a la ubicació de memòria del programa 0x0000. Què passa amb la nostra directiva.org? Bé, hem col·locat una ordre a la ubicació de memòria 0x0020 i si mireu cap avall la taula veureu que si es produeix un desbordament de temporitzador / comptador0 (provinent de TIMER0 OVF) executarà el que estigui a la ubicació 0x0020. Així, sempre que això passi, el PC saltarà al lloc que hem etiquetat com a "overflow_handler". Guai no? Veuràs en un minut per què hem fet això, però primer acabem aquest pas del tutorial amb un apart.

Si volem que el nostre codi sigui més ordenat i real, hauríem de substituir les 4 línies que comentem actualment per les següents (vegeu la pàgina 66):

.org 0x0000

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

De manera que, si es produeix una interrupció determinada, només "reti" que significa "tornar de la interrupció" i no passa res més. Però si mai "Habilitem" aquestes diverses interrupcions, no s'utilitzaran i podem posar codi de programa en aquests llocs. Al nostre programa actual "blink.asm" només anem a habilitar la interrupció de desbordament del temporitzador0 (i, per descomptat, la interrupció de restabliment que sempre està habilitada), de manera que no ens molestarem amb les altres.

Com podem "habilitar" la interrupció de desbordament del temporitzador0? … aquest és el tema del nostre següent pas en aquest tutorial.

Pas 5: temporitzador / comptador 0

Temporitzador / comptador 0
Temporitzador / comptador 0

Mireu la imatge anterior. Aquest és el procés de presa de decisions del "PC" quan alguna influència externa "interromp" el flux del nostre programa. El primer que fa quan rep un senyal de fora que s'ha produït una interrupció és comprovar si hem definit el bit "interrupt enable enable" per a aquest tipus d'interrupció. Si no ho hem fet, continuarà executant la nostra següent línia de codi. Si hem establert aquest bit d’activació d’interrupcions concret (de manera que hi hagi un 1 en aquesta ubicació de bit en lloc d’un 0), comprovarà si hem activat o no les "interrupcions globals", si no, tornarà a passar a la línia següent de codi i continuar. Si també hem activat les interrupcions globals, anirà a la ubicació de memòria del programa d’aquest tipus d’interrupció (tal com es mostra a la taula 12-6) i executarà qualsevol ordre que hi hàgim fet. Vegem, doncs, com hem implementat tot això al nostre codi.

La secció Restableix l’etiqueta del nostre codi comença amb les dues línies següents:

Restableix:

ldi temp, 0b00000101 out TCCR0B, temp

Com ja sabem, això carrega a la temperatura (és a dir, R16) el nombre immediatament següent, que és 0b00000101. Després escriu aquest número al registre anomenat TCCR0B mitjançant l'ordre "out". Què és aquest registre? Bé, anem a la pàgina 614 del full de dades. Es troba al mig d’una taula que resumeix tots els registres. A l’adreça 0x25 trobareu TCCR0B. (Ara ja sabeu d'on provenia la línia "out 0x25, r16" a la meva versió del codi sense comentaris). Veiem pel segment de codi anterior que hem definit el bit 0 i el segon bit i hem esborrat la resta. En mirar la taula, podeu veure que això significa que hem definit CS00 i CS02. Ara anem al capítol del full de dades anomenat "Temporitzador / comptador de 8 bits amb PWM". En particular, aneu a la pàgina 107 d’aquest capítol. Veureu la mateixa descripció del registre "Registre de control de temporitzador / comptador B" (TCCR0B) que acabem de veure a la taula de resum del registre (per tant, podríem haver vingut directament aquí, però volia que vegeu com s'utilitzen les taules de resum per a futures referències). El full de dades continua proporcionant una descripció de cadascun dels bits d’aquest registre i del que fan. Ometrem tot això per ara i passarem la pàgina a la taula 15-9. En aquesta taula es mostra la "Descripció del bit de selecció de rellotge". Ara busqueu aquesta taula fins que trobeu la línia que correspon als bits que acabem d’establir en aquest registre. La línia diu "clk / 1024 (de prescaler)". Això vol dir que volem que el temporitzador / comptador0 (TCNT0) marqui a una velocitat que és la freqüència de la CPU dividida per 1024. Com que tenim el nostre microcontrolador alimentat per un oscil·lador de cristall de 16 MHz significa que la velocitat que la nostra CPU executa les instruccions és: 16 milions d’instruccions per segon. Per tant, la taxa que marcarà el nostre comptador TCNT0 és de 16 milions / 1024 = 15625 vegades per segon (proveu-ho amb diferents bits de selecció de rellotge i vegeu què passa: recordeu la nostra filosofia?). Mantenim el número 15625 al fons de la nostra ment per més endavant i passem a les dues línies de codi següents:

temp ldi, 0b00000001

pts TIMSK0, temp

Això estableix el bit 0 del registre anomenat TIMSK0 i esborra tota la resta. Si doneu un cop d'ull a la pàgina 109 del full de dades, veureu que TIMSK0 significa "Registre de màscara d'interrupció de temporitzador / comptador 0" i el nostre codi ha definit el bit 0 que es denomina TOIE0 que significa "Activació d'interrupció de desbordament de temporitzador / comptador0". … Allà! Ara veieu de què es tracta. Ara tenim el "bit d'activació d'interrupció establert" com volíem des de la primera decisió de la nostra imatge a la part superior. Per tant, ara tot el que hem de fer és habilitar les "interrupcions globals" i el nostre programa podrà respondre a aquest tipus d'interrupcions. Activarem les interrupcions globals en breu, però abans de fer-ho potser us haureu confós alguna cosa.

Sempre que em veieu, utilitzeu una instrucció que no heu vist abans. El primer que heu de fer és passar a la pàgina 616 del full de dades. Aquest és el "Resum del conjunt d'instruccions". Ara cerqueu la instrucció "STS" que he utilitzat. Es diu que pren un número d'un registre R (hem utilitzat R16) i de la ubicació k "Store direct to SRAM" (en el nostre cas donat per TIMSK0). Llavors, per què hem hagut d’utilitzar "sts" que triga 2 cicles de rellotge (vegeu l'última columna de la taula) per emmagatzemar-los a TIMSK0 i només necessitem "out", que només necessita un cicle de rellotge, per emmagatzemar-los a TCCR0B abans? Per respondre a aquesta pregunta, hem de tornar a la nostra taula de resum del registre a la pàgina 614. Veu que el registre TCCR0B es troba a l’adreça 0x25, però també a (0x45), oi? Això vol dir que és un registre a SRAM, però també és un tipus de registre determinat anomenat "port" (o registre d'E / S). Si mireu la taula de resum d'instruccions al costat de l'ordre "out", veureu que pren valors dels "registres de treball" com R16 i els envia a un PORT. Per tant, podem utilitzar "out" quan escrivim a TCCR0B i estalviar-nos un cicle de rellotge. Però ara busqueu TIMSK0 a la taula de registres. Veureu que té l'adreça 0x6e. Això està fora de l’interval de ports (que són només les primeres ubicacions 0x3F de SRAM) i, per tant, heu de tornar a utilitzar l’ordre sts i fer dos cicles de rellotge de CPU per fer-ho. Llegiu la Nota 4 al final de la taula de resum de les instruccions a la pàgina 615 ara mateix. Tingueu en compte també que tots els nostres ports d’entrada i sortida, com PORTD, es troben a la part inferior de la taula. Per exemple, PD4 és el bit 4 a l'adreça 0x0b (ara veieu d'on provenien totes les coses de 0x0b al meu codi que no heu comentat!).. bé, pregunta ràpida: heu canviat les "puntades" a "fora" i veureu què passa? Recordeu la nostra filosofia! trenca-ho! no confiïs només en la meva paraula.

D'acord, abans de continuar, aneu a la pàgina 19 del full de dades durant un minut. Veureu una imatge de la memòria de dades (SRAM). Els primers 32 registres a SRAM (de 0x0000 a 0x001F) són els "registres de treball de propòsit general" de R0 a R31 que utilitzem tot el temps com a variables del nostre codi. Els següents 64 registres són els ports d'E / S fins a 0x005f (és a dir, dels que parlàvem que tenen aquestes adreces sense claudàtors al costat de la taula de registres, que podem utilitzar l'ordre "out" en lloc de "sts"). la següent secció de SRAM conté tots els altres registres de la taula resum fins a l'adreça 0x00FF i, finalment, la resta és SRAM intern. Ara, passem ràpidament a la pàgina 12 per un segon. Allà veieu una taula dels "registres de treball de propòsit general" que sempre fem servir com a variables. Veu la línia gruixuda entre els números R0 a R15 i després R16 a R31? Aquesta és la raó per la qual sempre fem servir R16 com la més petita i hi entraré una mica més en el següent tutorial on també necessitarem els tres registres d’adreces indirectes de 16 bits, X, Y i Z. No ho faré entrar-hi encara, ja que no ho necessitem ara i ens estem embadalint prou.

Torneu d'una pàgina a la pàgina 11 del full de dades. Veureu un diagrama del registre SREG a la part superior dreta? Veureu que el bit 7 d'aquest registre es diu "jo". Ara baixeu la pàgina i llegiu la descripció de Bit 7…. Visca! És el bit Global Interrupt Enable. Això és el que hem de configurar per passar la segona decisió del nostre diagrama anterior i permetre les interrupcions de desbordament de temporitzador / comptador al nostre programa. Per tant, la següent línia del nostre programa hauria de llegir:

sbi SREG, I

que defineix el bit anomenat "I" al registre SREG. No obstant això, en lloc d'això hem utilitzat la instrucció

sei

en canvi. Aquest bit s’estableix tan sovint en programes que acaben de fer una manera més senzilla de fer-ho.

Bé! Ara ja tenim a punt les interrupcions de desbordament perquè el nostre "jmp overflow_handler" s'executi sempre que es produeixi.

Abans de continuar, doneu una ullada ràpida al registre SREG (Status Register) perquè és molt important. Llegiu què representa cadascuna de les banderes. En particular, moltes de les instruccions que fem servir configuraran i revisaran aquestes marques constantment. Per exemple, més endavant utilitzarem l'ordre "CPI" que significa "comparar immediat". Feu un cop d'ull a la taula de resum d'instruccions per a aquesta instrucció i observeu quants indicadors estableix a la columna "indicadors". Totes aquestes marques són a SREG i el nostre codi les configurarà i les comprovarà constantment. Aviat en veureu exemples. Finalment, l'últim fragment d'aquesta secció de codi és:

clr temp

sortida TCNT0, temp sbi DDRD, 4

L’última línia aquí és força evident. Simplement estableix el 4t bit del registre de direcció de dades per a PortD fent que PD4 sigui SORTIDA.

El primer estableix la temperatura variable a zero i després el copia al registre TCNT0. TCNT0 és el nostre temporitzador / comptador0. Això el posa a zero. Tan bon punt el PC executi aquesta línia, el temporitzador 0 començarà a zero i comptarà a una velocitat de 15625 vegades cada segon. El problema és aquest: TCNT0 és un registre de "8 bits", oi? Quin és el nombre més gran que pot contenir un registre de 8 bits? Doncs 0b11111111 ho és. Aquest és el número 0xFF. Quin és 255. Llavors veieu què passa? El temporitzador augmenta 15625 vegades per segon i cada cop que arriba a 255 "desborda" i torna a 0 de nou. Al mateix temps que torna a zero, envia un senyal d'interrupció de desbordament del temporitzador. El PC ho aconsegueix i ja saps què fa ara? Sí. Va a la ubicació de memòria del programa 0x0020 i executa la instrucció que hi troba.

Genial! Si encara esteu amb mi, sou un superheroi incansable! Seguim endavant …

Pas 6: Gestor de desbordaments

Suposem, doncs, que el registre temporitzador / comptador0 acaba de desbordar-se. Ara sabem que el programa rep un senyal d’interrupció i executa 0x0020, que indica al PC Counter, PC que salti a l’etiqueta "overflow_handler", el següent és el codi que vam escriure després d’aquesta etiqueta:

handflow_handler:

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

El primer que fa és incrementar la variable "desbordaments" (que és el nostre nom per al registre de treball de propòsit general R17) i després "compara" el contingut dels desbordaments amb el número 61. La manera com funciona la instrucció cpi és que simplement resta els dos números i si el resultat és zero, marca la bandera Z al registre SREG (ja us he dit que veuríem aquest registre tot el temps). Si els dos números són iguals, la bandera Z serà un 1, si els dos números no són iguals, serà un 0.

La següent línia diu "brne PC + 2" que significa "branca si no és igual". Essencialment, comprova la bandera Z a SREG i si NO és un (és a dir, els dos números no són iguals, si fossin iguals, la marca zero s’establiria) el PC es ramifica a PC + 2, és a dir, salta el següent line i va directament a "reti" que torna de la interrupció al lloc que es trobava al codi quan va arribar la interrupció. Si la instrucció brne trobés un 1 al bit de bandera zero, no es ramificaria i, en canvi, continuaria fins a la línia següent, que es desbordaria clr, restablint-la a 0.

Quin és el resultat net de tot això?

Doncs veiem que cada vegada que es produeix un desbordament del temporitzador, aquest controlador augmenta el valor dels "desbordaments" en un. Per tant, la variable "desbordaments" compta el nombre de desbordaments a mesura que es produeixen. Sempre que el nombre arriba a 61 el restablim a zero.

Ara bé, per què ho faríem al món?

A veure. Recordem que la nostra velocitat de rellotge per a la nostra CPU és de 16 MHz i la vam "prescalificar" mitjançant TCCR0B perquè el temporitzador només compti a una velocitat de 15625 recomptes per segon, oi? I cada cop que el temporitzador arriba a un recompte de 255, es desborda. Per tant, això significa que desborda 15625/256 = 61,04 vegades per segon. Estem fent un seguiment del nombre de desbordaments amb la nostra variable "desbordaments" i estem comparant aquest nombre amb 61. Per tant, veiem que els "desbordaments" seran 61 una vegada cada segon. Per tant, el nostre gestor restablirà els "desbordaments" a zero un cop cada segon. Per tant, si simplement superviséssim la variable "desbordaments" i prenguéssim nota de cada vegada que es restableix a zero, comptaríem segon a segon en temps real (tingueu en compte que al següent tutorial mostrarem com obtenir una retard en mil·lisegons de la mateixa manera que funciona la rutina de "retard" Arduino).

Ara hem "gestionat" les interrupcions de desbordament del temporitzador. Assegureu-vos d’entendre com funciona això i, a continuació, passeu al següent pas on fem ús d’aquest fet.

Pas 7: Retard

Ara que hem vist que la nostra rutina "overflow_handler" del controlador d'interrupcions de desbordament del temporitzador establirà la variable "desbordaments" a zero un cop cada segon, podem utilitzar aquest fet per dissenyar una subrutina de "retard".

Mireu el següent codi a sota del nostre retard: etiqueta

retard:

clr desborda sec_count: CPI desborda, 30 brne sec_count ret

Anem a trucar a aquesta subrutina cada vegada que necessitem un retard en el nostre programa. La manera com funciona és que primer posa la variable "desbordaments" a zero. A continuació, entra en una àrea etiquetada com "sec_count" i compara els desbordaments amb 30, si no són iguals es ramifica a l'etiqueta sec_count i es compara una i altra vegada, etc. fins que finalment són iguals (recordeu que tot el temps això va al nostre controlador d'interrupcions del temporitzador continua augmentant els desbordaments de variables i, per tant, canvia cada vegada que anem per aquí. Quan els desbordaments finalment són iguals a 30, surt del bucle i torna a allà on anomenem delay: from. El resultat net és un retard de 1/2 segon

Exercici 2: canvieu la rutina overflow_handler pel següent:

handflow_handler:

inc desbordaments reti

i executeu el programa. Hi ha alguna cosa diferent? Per què o per què no?

Pas 8: parpelleja

Finalment, vegem la rutina de parpelleig:

parpellejar:

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

Primer activem PD4, després cridem la nostra subrutina de retard. Utilitzem rcall perquè, quan el PC arribi a una declaració "ret", torni a la línia següent a rcall. A continuació, els retards de la rutina de retard de 30 recomptes a la variable de desbordament com hem vist i són gairebé exactament 1/2 segon, després desactivem PD4, retardem 1/2 segon més i tornem al principi de nou.

El resultat net és un LED parpellejant.

Crec que ara acceptareu que "parpellejar" probablement no sigui el millor programa "hola món" en llenguatge assemblador.

Exercici 3: canvieu els diversos paràmetres del programa de manera que el LED parpellegi a velocitats diferents, com ara un segon o 4 vegades per segon, etc. Exercici 4: canvieu-lo perquè el LED estigui encès i apagat durant diferents quantitats de temps. Per exemple, activat durant 1/4 de segon i després apagat durant 2 segons o alguna cosa semblant. Exercici 5: canvieu el bit de selecció del rellotge TCCR0B a 100 i després continueu pujant per la taula. En quin moment no es pot distingir del nostre programa "hello.asm" del tutorial 1? Exercici 6 (opcional): si teniu un oscil·lador de cristall diferent, com ara un 4 MHz o un 13,5 MHz o el que sigui, canvieu el vostre oscil·lador de 16 MHz al tauler de revisió per veure la nova i veureu com afecta la velocitat de parpelleig del LED. Ara hauríeu de poder fer el càlcul precís i predir exactament com afectarà la taxa.

Pas 9: Conclusió

Per a aquells de vosaltres que heu arribat fins aquí, enhorabona!

M’adono que és molt difícil fer-ho quan estàs llegint i mirant cap amunt més del que estàs connectant i experimentant, però espero haver après les següents coses importants:

  1. Com funciona la memòria del programa
  2. Com funciona SRAM
  3. Com buscar registres
  4. Com buscar instruccions i saber què fan
  5. Com implementar interrupcions
  6. Com el CP executa el codi, com funciona el SREG i què passa durant les interrupcions
  7. Com fer bucles i salts i rebotar al codi
  8. Que important és llegir el full de dades!
  9. Com una vegada que sàpiga fer tot això per al microcontrolador Atmega328p, serà relativament senzill conèixer els nous controladors que us interessin.
  10. Com canviar el temps de la CPU en temps real i utilitzar-lo en rutines de retard.

Ara que tenim molta teoria fora del camí, podem escriure un codi millor i controlar coses més complicades. Per tant, el proper tutorial ho farem precisament. Construirem un circuit més complicat, més interessant i el controlarem de maneres divertides.

Exercici 7: "Trencar" el codi de diverses maneres i veure què passa. Curiositat científica, nadó! Algú més pot rentar els plats, oi? Exercici 8: Muntar el codi amb l'opció "-l" per generar un fitxer de llista. És a dir, "avra -l blink.lst blink.asm" i mireu el fitxer de llista. Crèdit addicional: el codi sense comentaris que he donat al principi i el codi comentat que comentem més endavant difereixen. Hi ha una línia de codi diferent. El trobes? Per què no importa aquesta diferència?

Espero que us ho hagueu divertit! Ens veiem la propera vegada …

Recomanat: