Innholdsfortegnelse:

AVR Assembler Tutorial 2: 4 Steps
AVR Assembler Tutorial 2: 4 Steps

Video: AVR Assembler Tutorial 2: 4 Steps

Video: AVR Assembler Tutorial 2: 4 Steps
Video: AVR Assembly Tutorial: Part 1 (Basic Commands) 2024, Juli
Anonim
AVR Assembler Tutorial 2
AVR Assembler Tutorial 2

Denne opplæringen er en fortsettelse av "AVR Assembler Tutorial 1"

Hvis du ikke har gått gjennom veiledning 1, bør du stoppe nå og gjøre det først.

I denne opplæringen vil vi fortsette vår studie av montering språk programmering av atmega328p som brukes i Arduino.

Du vil trenge:

  1. et brødbrett Arduino eller bare en vanlig Arduino som i opplæring 1
  2. en LED
  3. en 220 ohm motstand
  4. en trykknapp
  5. koble ledninger for å lage kretsen på brødbrettet
  6. Instruksjonshåndbok: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Datablad: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Den komplette samlingen av opplæringsprogrammene mine finner du her:

Trinn 1: Bygg kretsen

Bygg kretsen
Bygg kretsen

Først må du konstruere kretsen som vi skal studere i denne opplæringen.

Her er måten det er koblet til:

PB0 (digital pin 8) - LED - R (220 ohm) - 5V

PD0 (digital pin 0) - trykknapp - GND

Du kan kontrollere at lysdioden er riktig orientert ved å koble den til GND i stedet for PB0. Hvis ingenting skjer, snu retningen og lyset skal tennes. Koble den deretter til PB0 og fortsett. Bildet viser hvordan min breadboard -arduino er koblet til.

Trinn 2: Skrive forsamlingskoden

Skrive forsamlingskoden
Skrive forsamlingskoden

Skriv følgende kode i en tekstfil som heter pushbutton.asm og kompiler den med avra slik du gjorde i veiledning 1.

Legg merke til at vi har mange kommentarer i denne koden. Hver gang montøren ser et semikolon, hopper den over resten av linjen og går videre til neste linje. Det er god programmeringspraksis (spesielt på monteringsspråk!) Å kommentere koden din sterkt, slik at når du kommer tilbake til den i fremtiden, vet du hva du gjorde. Jeg kommer til å kommentere ting ganske mye i de første opplæringsprogrammene, slik at vi vet nøyaktig hva som skjer og hvorfor. Senere, når vi blir litt flinkere til å montere koding, vil jeg kommentere ting litt mindre detaljert.

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

; skrevet av: 1o_o7; dato: 23. oktober 2014; ***********************************

.nolist

. inkludere "m328Pdef.inc".list.def temp = r16; utpeke arbeidsregister r16 som temp rjmp Init; første linje utført

I det:

ser temp; sett alle bitene i temp til 1. ut DDRB, temp; sette litt som 1 på Data Direction I/O; registrer deg for PortB, som er DDRB, angir at; pin som utgang, ville en 0 angitt pin som input; så her er alle PortB -pinner utganger (satt til 1) ldi temp, 0b11111110; last det 'umiddelbare' nummeret til tempregisteret; hvis det bare var ld så det andre argumentet; måtte være et minne sted i stedet ut DDRD, temp; mv temp til DDRD, resultatet er at PD0 er input; og resten er utganger clr temp; alle biter i temp er satt til 0's out PortB, temp; sett alle bitene (dvs. pins) i PortB til 0V ldi temp, 0b00000001; last umiddelbart nummer for å vikle ut PortD, temp; flytte temp til PortD. PD0 har en opptrekksmotstand; (dvs. satt til 5V) siden den har en 1 i den biten; resten er 0V siden 0 -tallet.

Hoved:

i temp, PinD; PinD holder tilstanden til PortD, kopier dette til temp; hvis knappen er koblet til PD0 vil dette være; 0 når knappen trykkes, 1 ellers siden; PD0 har en pull -up -motstand, den er normalt ved 5V ut PortB, temp; sender 0 og 1 lest ovenfor til PortB; dette betyr at vi vil at LED -en skal være koblet til PB0,; når PD0 er LAV, setter den PB0 til LAV og svinger; på lysdioden (siden den andre siden av lysdioden er; koblet til 5V og dette vil sette PB0 til 0V så; strømmen vil flyte) rjmp Main; går tilbake til starten av Main

Legg merke til at denne gangen har vi ikke bare mange flere kommentarer i koden vår, men vi har også en overskriftsseksjon som gir litt informasjon om hvem som skrev den og når den ble skrevet. Resten av koden er også delt inn i seksjoner.

Etter at du har samlet koden ovenfor, bør du laste den inn på mikrokontrolleren og se at den fungerer. Lysdioden skal slås på mens du trykker på knappen og deretter slå seg av igjen når du slipper. Jeg har vist hvordan det ser ut på bildet.

Trinn 3: Linje-for-linje analyse av koden

Jeg vil hoppe over linjene som bare er kommentarer ettersom formålet deres er selvsagt.

.nolist

. inkludere "m328Pdef.inc".list

Disse tre linjene inkluderer filen som inneholder Register- og Bit -definisjonene for ATmega328P som vi programmerer. Kommandoen.nolist forteller montøren om ikke å inkludere denne filen i pushbutton.lst -filen som den produserer når du monterer den. Det slår av oppføringsalternativet. Etter at vi har tatt med filen, slår vi på alternativet for liste igjen med kommandoen.list. Grunnen til at vi gjør dette er fordi m328Pdef.inc -filen er ganske lang, og vi egentlig ikke trenger å se den i listefilen. Vår montør, avra, genererer ikke automatisk en listefil, og hvis vi ønsker en, ville vi sette sammen ved hjelp av følgende kommando:

avra -l trykknapp.lst trykknapp.asm

Hvis du gjør dette vil den generere en fil som heter pushbutton.lst, og hvis du undersøker denne filen vil du finne at den viser programkoden din sammen med ekstra informasjon. Hvis du ser på den ekstra informasjonen, vil du se at linjene begynner med en C: etterfulgt av den relative adressen i hex for hvor koden er plassert i minnet. I hovedsak begynner den på 000000 med den første kommandoen og øker derfra med hver påfølgende kommando. Den andre kolonnen etter det relative stedet i minnet er hex -koden for kommandoen etterfulgt av hex -koden for argumentet til kommandoen. Vi vil diskutere listefiler videre i fremtidige opplæringsprogrammer.

.def temp = r16; angi arbeidsregister r16 som temp

I denne linjen bruker vi assembler -direktivet ".def" til å definere variabelen "temp" som lik r16 "arbeidsregisteret". Vi vil bruke register r16 som det som lagrer tallene vi vil kopiere til forskjellige porter og registre (som ikke kan skrives direkte til).

Oppgave 1: Prøv å kopiere et binært tall direkte til en port eller spesialregister som DDRB og se hva som skjer når du prøver å sette sammen koden.

Et register inneholder en byte (8 bits) med informasjon. I hovedsak er det vanligvis en samling SR-låser som hver er en "bit" og inneholder en 1 eller en 0. Vi kan diskutere dette (og til og med bygge en!) Senere i denne serien. Du lurer kanskje på hva som er et "arbeidsregister" og hvorfor vi valgte r16. Vi vil diskutere det i en fremtidig opplæring når vi dykker ned i myra på innsiden av brikken. For nå vil jeg at du skal forstå hvordan du gjør ting som å skrive kode og programmere fysisk maskinvare. Deretter vil du ha en referanseramme fra den erfaringen som vil gjøre minnet og registeregenskapene til mikrokontrolleren lettere å forstå. Jeg innser at de fleste innledende lærebøker og diskusjoner gjør dette omvendt, men jeg har funnet ut at det å spille et videospill en stund først for å få et globalt perspektiv før du leser bruksanvisningen er mye enklere enn å lese manualen først.

rjmp Init; første linje utført

Denne linjen er et "relativt hopp" til etiketten "Init" og er egentlig ikke nødvendig her siden den neste kommandoen allerede er i Init, men vi inkluderer den for fremtidig bruk.

I det:

ser temp; sett alle bitene i temp til 1 -tallet.

Etter Init -etiketten utfører vi en "set register" -kommando. Dette setter alle de 8 bitene i registeret "temp" (som du husker er r16) til 1 -tallet. Så temp inneholder nå 0b11111111.

ut DDRB, temp; sette litt som 1 på Data Direction I/O -registeret

; for PortB, som er DDRB, angir denne pinnen som utgang; en 0 ville angi den pinnen som input; så her er alle PortB -pinner utganger (satt til 1)

Registeret DDRB (Data Direction Register for PortB) forteller hvilke pinner på PortB (dvs. PB0 til og med PB7) som er angitt som input og hvilke som er angitt som output. Siden vi har pin PB0 koblet til LED -en vår og resten ikke er koblet til noe, vil vi sette alle bitene til 1, noe som betyr at de alle er utganger.

ldi temp, 0b11111110; last det 'umiddelbare' nummeret til vikarregisteret

; hvis det bare var ld så ville det andre argumentet; må være et minnested

Denne linjen laster det binære tallet 0b11111110 inn i temp -registeret.

ut DDRD, temp; mv temp til DDRD, er resultatet at PD0 er input og

; resten er utganger

Nå setter vi Data Direction Register for PortD fra temp, siden temp fortsatt inneholder 0b11111110 ser vi at PD0 vil bli angitt som en inngangspinne (siden det er en 0 helt til høyre) og resten er angitt som utganger siden det er 1 er på disse stedene.

clr temp; alle biter i temp er satt til 0s

ut PortB, temp; sett alle bitene (dvs. pins) i PortB til 0V

Først "sletter" vi registertemp, som betyr å sette alle bitene til null. Deretter kopierer vi det til PortB -registeret som setter 0V på alle disse pinnene. En null på en PortB -bit betyr at prosessoren vil beholde den pinnen på 0V, en en på en bit vil føre til at den pinnen settes til 5V.

Øvelse 2: Bruk et multimeter for å sjekke om alle pinnene på PortB faktisk er null. Skjer det noe rart med PB1? Noen anelse om hvorfor det kan være det? (ligner på oppgave 4 nedenfor, følg deretter koden …) Oppgave 3: Fjern de to linjene ovenfor fra koden. Kjører programmet fremdeles riktig? Hvorfor?

ldi temp, 0b00000001; last umiddelbart nummer til temp

ut PortD, temp; flytte temp til PortD. PD0 er på 5V (har en pullup -motstand); siden den har en 1 i den biten, er resten 0V. Oppgave 4: Fjern de to linjene ovenfor fra koden. Kjører programmet fremdeles riktig? Hvorfor? (Dette er forskjellig fra oppgave 3 ovenfor. Se pin -out -diagrammet. Hva er standard DDRD -innstilling for PD0? (Se side 90 i databladet

Først "laster vi umiddelbart" tallet 0b00000001 til temp. Den "umiddelbare" delen er der siden vi laster et rett opp nummer til temp i stedet for en peker til et minnested som inneholder nummeret som skal lastes inn. I så fall ville vi ganske enkelt bruke "ld" i stedet for "ldi". Så sender vi dette nummeret til PortD som setter PD0 til 5V og resten til 0V.

Nå har vi satt pinnene som inngang eller utgang, og vi har satt opp de opprinnelige tilstandene som enten 0V eller 5V (LAV eller HØY), og så går vi inn i programmet "loop".

Hoved: i temp, PinD; PinD holder tilstanden til PortD, kopier dette til temp

; hvis knappen er koblet til PD0 så blir dette; a 0 når knappen trykkes, 1 ellers siden; PD0 har en pull -up -motstand, den er vanligvis på 5V

Registeret PinD inneholder den nåværende tilstanden til PortD -pinnene. For eksempel, hvis du koblet en 5V ledning til PD3, så ved neste klokkesyklus (som skjer 16 millioner ganger per sekund siden vi har mikrokontrolleren koblet til et 16MHz klokkesignal) PinD3 -biten (fra den nåværende tilstanden til PD3) ville bli en 1 i stedet for en 0. Så i denne linjen kopierer vi den nåværende tilstanden til pinnene til temp.

ut PortB, temp; sender 0 og 1 lest ovenfor til PortB

; dette betyr at vi vil at LED -en skal være koblet til PB0, så; når PD0 er LAV, vil den sette PB0 til LAV og slå; på lysdioden (den andre siden av lysdioden er tilkoblet; til 5V og dette vil sette PB0 til 0V så strømmen strømmer)

Nå sender vi tilstanden til pinnene i PinD til PortB -utgangen. Dette betyr faktisk at PD0 sender en 1 til PortD0 med mindre knappen trykkes. I så fall siden knappen er koblet til bakken, vil pinnen være på 0V og den vil sende en 0 til PortB0. Hvis du ser på kretsdiagrammet, betyr 0V på PB0 at LED -en vil lyse siden den andre siden av den er på 5V. Hvis vi ikke trykker på knappen, slik at en 1 sendes til PB0, vil det bety at vi har 5V på PB0 og også 5V på den andre siden av lysdioden, så det er ingen potensiell forskjell og ingen strøm vil strømme og så vil LED vil ikke lyse (i dette tilfellet er det en LED som er en diode, og strømmen flyter bare en retning uansett, uansett).

rjmp Main; går tilbake til Start

Dette relative hoppet sløyfer oss tilbake til vår Main: -etikett, og vi sjekker PinD igjen og så videre. Kontrollerer hver 16. milliondel av et sekund om knappen trykkes og stiller PB0 deretter.

Øvelse 5: Endre koden slik at LED -en din er koblet til PB3 i stedet for PB0, og se at den fungerer. Øvelse 6: Koble LED -en til GND i stedet for 5V og endre koden deretter.

Trinn 4: Konklusjon

I denne opplæringen har vi undersøkt monteringsspråket for ATmega328p ytterligere og lært hvordan du kontrollerer en LED med en trykknapp. Spesielt lærte vi følgende kommandoer:

ser register setter alle bitene i et register til 1 -tallet

clr -registeret setter alle bitene i et register til 0 -tallet

i register, kopierer i/o -registeret nummeret fra et i/o -register til et arbeidsregister

I den neste opplæringen vil vi undersøke strukturen til ATmega328p og de forskjellige registre, operasjoner og ressurser som finnes i den.

Før jeg fortsetter med disse opplæringene, skal jeg vente og se interessen. Hvis det er et antall mennesker som faktisk liker å lære å kode programmer for denne mikroprosessoren i samlingsspråk, vil jeg fortsette og konstruere mer kompliserte kretser og bruke mer robust kode.

Anbefalt: