Indholdsfortegnelse:

AVR Assembler Tutorial 2: 4 trin
AVR Assembler Tutorial 2: 4 trin

Video: AVR Assembler Tutorial 2: 4 trin

Video: AVR Assembler Tutorial 2: 4 trin
Video: AVR Assembly Tutorial: Part 1 (Basic Commands) 2024, Juli
Anonim
Vejledning til AVR -samler 2
Vejledning til AVR -samler 2

Denne vejledning er en fortsættelse af "AVR Assembler Tutorial 1"

Hvis du ikke har gennemgået vejledning 1, skal du stoppe nu og gøre det først.

I denne vejledning vil vi fortsætte vores undersøgelse af samlingssprogsprogrammering af atmega328p, der bruges i Arduino's.

Du får brug for:

  1. et brødbræt Arduino eller bare en normal Arduino som i selvstudie 1
  2. en LED
  3. en 220 ohm modstand
  4. en trykknap
  5. tilslutning af ledninger til at lave kredsløbet på dit brødbræt
  6. Instruktionssætmanual: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Datablad: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Den komplette samling af mine selvstudier findes her:

Trin 1: Opbygning af kredsløbet

Bygger kredsløbet
Bygger kredsløbet

Først skal du konstruere det kredsløb, som vi vil studere i denne vejledning.

Sådan er det forbundet:

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

PD0 (digital pin 0) - trykknap - GND

Du kan kontrollere, at din LED er korrekt orienteret ved at tilslutte den til GND i stedet for PB0. Hvis der ikke sker noget, skal du vende orienteringen, og lyset skal tændes. Tilslut det derefter til PB0 igen, og fortsæt. Billedet viser, hvordan min brødbræt -arduino er forbundet.

Trin 2: Skrivning af forsamlingskoden

Skrivning af forsamlingskoden
Skrivning af forsamlingskoden

Skriv følgende kode i en tekstfil kaldet pushbutton.asm og kompilér den med avra, som du gjorde i selvstudie 1.

Bemærk, at vi i denne kode har masser af kommentarer. Hver gang samleren ser et semikolon, springer den resten af linjen over og går videre til den næste linje. Det er god programmeringsskik (især i montagesprog!) At stærkt kommentere din kode, så når du vender tilbage til den i fremtiden, ved du, hvad du lavede. Jeg vil kommentere tingene ret meget i de første par tutorials, så vi ved præcis, hvad der foregår, og hvorfor. Senere, når vi er blevet lidt bedre til at samle kodning, vil jeg kommentere tingene lidt mindre detaljeret.

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

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

.nolist

. inkludere "m328Pdef.inc".list.def temp = r16; udpege arbejdsregister r16 som temp rjmp Init; første linje udført

I det:

ser temp; indstil alle bits i temp til 1'er. ud DDRB, temp; indstilling lidt som 1 på dataretning I/O; registrer dig for PortB, som er DDRB, angiver det; pin som output, ville en 0 sætte denne pin som input; så her er alle PortB pins udgange (indstillet til 1) ldi temp, 0b11111110; indlæse det 'umiddelbare' nummer i midlertidigt register; hvis det bare var ld så det andet argument; skulle være et hukommelsessted i stedet for DDRD, temp; mv temp til DDRD, resultatet er, at PD0 er input; og resten er output clr temp; alle bits i temp er sat til 0'er ud PortB, temp; indstil alle bits (dvs. pins) i PortB til 0V ldi temp, 0b00000001; indlæse øjeblikkeligt nummer for at vikle ud PortD, temp; flytte temp til PortD. PD0 har en pull up -modstand; (dvs. sat til 5V), da den har en 1 i den bit; resten er 0V siden 0'er.

Vigtigste:

i temp, PinD; PinD holder tilstanden PortD, kopier dette til temp; hvis knappen er forbundet til PD0, vil dette være; 0 når der trykkes på knappen, 1 ellers siden; PD0 har en pull up -modstand, den er normalt ved 5V ud PortB, temp; sender 0'erne og 1'erne læst ovenfor til PortB; det betyder, at vi vil have LED'en tilsluttet PB0,; når PD0 er LAV, sætter den PB0 til LAV og drejer; på LED'en (da den anden side af LED'en er; forbundet til 5V, og dette vil indstille PB0 til 0V, så strøm vil flyde) rjmp Main; sløjfer tilbage til starten af Main

Bemærk, at vi denne gang ikke kun har mange flere kommentarer i vores kode, men vi har også et overskriftsafsnit, der giver nogle oplysninger om, hvem der skrev det, og hvornår det blev skrevet. Resten af koden er også opdelt i sektioner.

Når du har kompileret ovenstående kode, skal du indlæse den på mikrokontrolleren og se, at den virker. Lysdioden skal tænde, mens du trykker på knappen og derefter slukke igen, når du slipper. Jeg har vist, hvordan det ser ud på billedet.

Trin 3: Line-by-line analyse af koden

Jeg vil springe over de linjer, der blot er kommentarer, da deres formål er indlysende.

.nolist

. inkludere "m328Pdef.inc".list

Disse tre linjer inkluderer den fil, der indeholder de Register- og Bit -definitioner for ATmega328P, som vi programmerer. Kommandoen.nolist fortæller montøren ikke at inkludere denne fil i filen pushbutton.lst, som den producerer, når du samler den. Det deaktiverer noteringsmuligheden. Efter at have inkluderet filen tænder vi for listeindstillingen igen med kommandoen.list. Grunden til at vi gør dette er, at filen m328Pdef.inc er ret lang, og vi ikke rigtig behøver at se den i listefilen. Vores assembler, avra, genererer ikke automatisk en listefil, og hvis vi gerne vil have en, ville vi samle ved hjælp af følgende kommando:

avra -l trykknap.lst trykknap.asm

Hvis du gør dette, genererer den en fil kaldet pushbutton.lst, og hvis du undersøger denne fil, vil du opdage, at den viser din programkode sammen med ekstra information. Hvis du ser på de ekstra oplysninger, vil du se, at linjerne begynder med et C: efterfulgt af den relative adresse i hex, hvor koden er placeret i hukommelsen. I det væsentlige begynder det på 000000 med den første kommando og stiger derfra med hver efterfølgende kommando. Den anden kolonne efter den relative plads i hukommelsen er hex -koden for kommandoen efterfulgt af hex -koden for kommandoens argument. Vi vil diskutere listefiler yderligere i fremtidige selvstudier.

.def temp = r16; udpege arbejdsregister r16 som temp

I denne linje bruger vi assembler -direktivet ".def" til at definere variablen "temp" som lig med r16 "arbejdsregisteret". Vi vil bruge register r16 som det, der gemmer de numre, vi vil kopiere til forskellige porte og registre (som ikke kan skrives direkte til).

Øvelse 1: Prøv at kopiere et binært tal direkte til en port eller et specielt register som DDRB og se, hvad der sker, når du prøver at samle koden.

Et register indeholder en byte (8 bit) information. I det væsentlige er det normalt en samling af SR-låse. Hver enkelt er en "bit" og indeholder en 1 eller en 0. Vi kan diskutere dette (og endda bygge en!) Senere i denne serie. Du undrer dig måske over, hvad der er et "arbejdsregister", og hvorfor vi valgte r16. Vi vil diskutere det i en fremtidig vejledning, når vi dykker ned i sumpen af chipens indvendige dele. For nu vil jeg have, at du forstår, hvordan du gør ting som at skrive kode og programmere fysisk hardware. Derefter vil du have en referenceramme fra den oplevelse, som vil gøre hukommelsen og registrere egenskaberne for mikrokontrolleren lettere at forstå. Jeg er klar over, at de fleste indledende lærebøger og diskussioner gør dette omvendt, men jeg har fundet ud af, at det er meget lettere at spille et videospil et stykke tid først for at få et globalt perspektiv, før man læser brugsanvisningen, end at læse vejledningen først.

rjmp Init; første linje udført

Denne linje er et "relativt spring" til etiketten "Init" og er ikke rigtig nødvendigt her, da den næste kommando allerede er i Init, men vi inkluderer den til fremtidig brug.

I det:

ser temp; indstil alle bits i temp til 1'er.

Efter Init -etiketten udfører vi en kommando "sæt register". Dette sætter alle de 8 bit i registret "temp" (som du husker er r16) til 1'er. Så temp indeholder nu 0b11111111.

ud DDRB, temp; indstilling lidt som 1 på Data Direction I/O -registret

; for PortB, som er DDRB, angiver denne pin som output; en 0 ville sætte denne pin som input; så her er alle PortB pins udgange (indstillet til 1)

Registret DDRB (Data Direction Register for PortB) fortæller, hvilke ben på PortB (dvs. PB0 til PB7), der er angivet som input, og hvilke der er betegnet som output. Da vi har stiften PB0 tilsluttet vores LED og resten ikke er forbundet til noget, sætter vi alle bitene til 1, hvilket betyder, at de alle er output.

ldi temp, 0b11111110; indlæse det 'umiddelbare' nummer i vikarregistret

; hvis det bare var ld så ville det andet argument; skal være et hukommelsessted

Denne linje indlæser det binære tal 0b11111110 i tempregistret.

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

; resten er output

Nu indstiller vi dataretningsregistret for PortD fra temp, da temp stadig indeholder 0b11111110 ser vi, at PD0 vil blive betegnet som en input -pin (da der er en 0 yderst til højre) og resten er betegnet som output, da der er 1 er på de steder.

clr temp; alle bits i temp er sat til 0'er

ud PortB, temp; indstil alle bits (dvs. pins) i PortB til 0V

Først "rydder" vi registertemp, hvilket betyder, at alle bits er nul. Derefter kopierer vi det til PortB -registret, der sætter 0V på alle disse ben. Et nul på en PortB -bit betyder, at processoren vil beholde denne pin på 0V, en en på en bit vil få den pin til at blive indstillet til 5V.

Øvelse 2: Brug et multimeter til at kontrollere, om alle benene på PortB faktisk er nul. Er der noget underligt i gang med PB1? En ide om hvorfor det kan være? (ligner øvelse 4 herunder, følg derefter koden …) Øvelse 3: Fjern de to ovenstående linjer fra din kode. Kører programmet stadig korrekt? Hvorfor?

ldi temp, 0b00000001; indlæse øjeblikkeligt nummer til temp

ud PortD, temp; flytte temp til PortD. PD0 er ved 5V (har en pullup -modstand); da den har en 1 i den bit er resten 0V. Øvelse 4: Fjern ovenstående to linjer fra din kode. Kører programmet stadig korrekt? Hvorfor? (Dette er forskelligt fra øvelse 3 ovenfor. Se pin -out -diagrammet. Hvad er standardindstillingen for DDRD for PD0? (Se side 90 i databladet

Først "indlæser vi øjeblikkeligt" tallet 0b00000001 til temp. Den "umiddelbare" del er der, da vi indlæser et lige op nummer til temp i stedet for en markør til et hukommelsessted, der indeholder det nummer, der skal indlæses. I så fald ville vi simpelthen bruge "ld" frem for "ldi". Derefter sender vi dette nummer til PortD, som sætter PD0 til 5V og resten til 0V.

Nu har vi indstillet stifterne som input eller output, og vi har konfigureret deres oprindelige tilstande som enten 0V eller 5V (LOW eller HIGH), og så går vi nu ind i vores program "loop".

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

; hvis knappen er forbundet til PD0, vil dette være; a 0 når der trykkes på knappen, 1 ellers siden; PD0 har en pull up -modstand, den er normalt ved 5V

Registeret PinD indeholder den aktuelle tilstand for PortD -benene. For eksempel, hvis du tilsluttede en 5V -ledning til PD3, så ved den næste urcyklus (hvilket sker 16 millioner gange i sekundet, da vi har mikrokontrolleren tilsluttet et 16MHz -klokkesignal) PinD3 -bit (fra den aktuelle tilstand af PD3) ville blive en 1 i stedet for en 0. Så i denne linje kopierer vi den aktuelle tilstand af stifterne til temp.

ud PortB, temp; sender 0'erne og 1'erne læst ovenfor til PortB

; det betyder, at vi vil have LED'en tilsluttet PB0, så; når PD0 er LAV, sætter den PB0 til LAV og drejer; på LED'en (den anden side af LED'en er tilsluttet; til 5V, og dette vil indstille PB0 til 0V, så strømmen strømmer)

Nu sender vi tilstanden af benene i PinD til PortB -udgangen. Dette betyder effektivt, at PD0 sender en 1 til PortD0, medmindre der trykkes på knappen. I så fald, da knappen er forbundet til jorden, vil denne pin være på 0V, og den sender en 0 til PortB0. Nu, hvis du ser på kredsløbsdiagrammet, betyder 0V på PB0, at LED'en lyser, da den anden side af den er på 5V. Hvis vi ikke trykker på knappen, så der sendes en 1 til PB0, ville det betyde, at vi har 5V på PB0 og også 5V på den anden side af LED'en, så der er ingen potentialeforskel, og der vil ikke strømme strøm, og så vil LED lyser ikke (i dette tilfælde er det en LED, der er en diode, og strømmen flyder derfor kun i én retning, uanset hvad).

rjmp Main; går tilbage til Start

Dette relative hop sløjfer os tilbage til vores Main: label, og vi tjekker PinD igen og så videre. Kontrollerer hver 16. milliondel af et sekund, om der trykkes på knappen og indstiller PB0 i overensstemmelse hermed.

Øvelse 5: Rediger din kode, så din LED er forbundet til PB3 i stedet for PB0, og se, at den fungerer. Øvelse 6: Tilslut din LED til GND i stedet for 5V, og modificer din kode i overensstemmelse hermed.

Trin 4: Konklusion

I denne vejledning har vi yderligere undersøgt samlingssproget for ATmega328p og lært at styre en LED med en trykknap. Især lærte vi følgende kommandoer:

ser register sætter alle bitene i et register til 1'er

clr -register sætter alle bitene i et register til 0'er

i register kopierer i/o register nummeret fra et i/o register til et arbejdsregister

I den næste vejledning vil vi undersøge strukturen af ATmega328p og de forskellige registre, operationer og ressourcer, der er indeholdt deri.

Inden jeg fortsætter med disse selvstudier, vil jeg vente og se interesseniveauet. Hvis der er et antal mennesker, der rent faktisk nyder at lære at kode programmer til denne mikroprocessor i samlingssprog, vil jeg fortsætte og konstruere mere komplicerede kredsløb og bruge mere robust kode.

Anbefalede: