Innehållsförteckning:

AVR Assembler Tutorial 2: 4 Steg
AVR Assembler Tutorial 2: 4 Steg

Video: AVR Assembler Tutorial 2: 4 Steg

Video: AVR Assembler Tutorial 2: 4 Steg
Video: AVR Ассемблер. Урок 2. Порты. Мигалка. AVR Assembler. Lesson 2. Ports. Flasher. 2024, Juli
Anonim
AVR -monteringshandledning 2
AVR -monteringshandledning 2

Denna handledning är en fortsättning på "AVR Assembler Tutorial 1"

Om du inte har gått igenom självstudie 1 bör du sluta nu och göra det först.

I denna handledning fortsätter vi vår studie av monteringsspråksprogrammering av atmega328p som används i Arduino.

Du kommer behöva:

  1. en brödbräda Arduino eller bara en vanlig Arduino som i självstudie 1
  2. en LED
  3. ett 220 ohm motstånd
  4. en tryckknapp
  5. anslutningskablar för att göra kretsen på din brödbräda
  6. Instuktion Set Manual: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Datablad: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Den kompletta samlingen av mina självstudier hittar du här:

Steg 1: Bygg kretsen

Bygga kretsen
Bygga kretsen

Först måste du konstruera kretsen som vi kommer att studera i denna handledning.

Så här är det anslutet:

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

PD0 (digital stift 0) - tryckknapp - GND

Du kan kontrollera att din LED är rätt orienterad genom att ansluta den till GND istället för PB0. Om inget händer, vänd sedan orienteringen och lampan ska tändas. Anslut den sedan till PB0 och fortsätt. Bilden visar hur min brödbräda arduino är ansluten.

Steg 2: Skriva församlingskoden

Skriva församlingskoden
Skriva församlingskoden

Skriv följande kod i en textfil som heter pushbutton.asm och kompilera den med avra som du gjorde i självstudie 1.

Lägg märke till att vi har många kommentarer i den här koden. Varje gång montören ser ett semikolon hoppar den över resten av raden och går vidare till nästa rad. Det är bra programmeringspraxis (särskilt i monteringsspråk!) Att kraftigt kommentera din kod så att när du återkommer till den i framtiden vet du vad du gjorde. Jag kommer att kommentera saker ganska mycket i de första självstudierna så att vi vet exakt vad som händer och varför. Senare, när vi blivit lite bättre på att montera kodning kommer jag att kommentera saker lite mer detaljerat.

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

; skriven av: 1o_o7; datum: 23 oktober 2014; ************************************

.nolist

.inkludera "m328Pdef.inc".list.def temp = r16; utse arbetsregistret r16 som temp rjmp Init; första raden utförd

I det:

ser temp; ställ in alla bitar i temp till 1: or. ut DDRB, temp; ställa in lite som 1 på Data Direction I/O; registrera dig för PortB, som är DDRB, anger att; pin som output, en 0 skulle ställa in den som input; så här är alla PortB -stift utgångar (inställda på 1) ldi temp, 0b11111110; ladda det 'omedelbara' numret till tempregistret; om det bara var ld då det andra argumentet; måste vara en minnesplats istället för DDRD, temp; mv temp till DDRD, resultatet är att PD0 är ingång; och resten är utgångar clr temp; alla bitar i temp är inställda på 0: or ut PortB, temp; ställ in alla bitar (dvs stift) i PortB till 0V ldi temp, 0b00000001; ladda omedelbart nummer för att temp ut PortD, temp; flytta temp till PortD. PD0 har ett uppdragningsmotstånd; (dvs inställd på 5V) eftersom den har en 1 i den biten; resten är 0V sedan 0 -talet.

Huvud:

i temp, PinD; PinD håller tillståndet för PortD, kopiera detta till temp; om knappen är ansluten till PD0 blir detta; 0 när knappen trycks in, 1 annars sedan; PD0 har ett pull -up -motstånd, det är normalt vid 5V ut PortB, temp; skickar 0: orna och 1: orna ovan till PortB; detta betyder att vi vill att lysdioden ska vara ansluten till PB0,; när PD0 är LÅG ställer den PB0 på LÅG och vrider; på lysdioden (eftersom den andra sidan av lysdioden är; ansluten till 5V och detta kommer att ställa PB0 till 0V så; strömmen kommer att flöda) rjmp Main; går tillbaka till början av Main

Lägg märke till att den här gången har vi inte bara många fler kommentarer i vår kod, utan vi har också en rubriksektion som ger lite information om vem som skrev den och när den skrevs. Resten av koden är också uppdelad i sektioner.

När du har sammanställt ovanstående kod bör du ladda den på mikrokontrollern och se att den fungerar. Lysdioden ska tändas medan du trycker på knappen och sedan släckas igen när du släpper. Jag har visat hur det ser ut på bilden.

Steg 3: Rad-för-rad-analys av koden

Jag kommer att hoppa över de rader som bara är kommentarer eftersom deras syfte är självklart.

.nolist

. inkludera "m328Pdef.inc".lista

Dessa tre rader inkluderar filen som innehåller de register- och bitdefinitioner för ATmega328P som vi programmerar. Kommandot.nolist uppmanar montören att inte ta med den här filen i filen pushbutton.lst som den producerar när du monterar den. Det stänger av listningsalternativet. Efter att ha inkluderat filen aktiverar vi listningsalternativet igen med kommandot.list. Anledningen till att vi gör detta är att filen m328Pdef.inc är ganska lång och att vi inte behöver se den i listfilen. Vår assembler, avra, genererar inte automatiskt en listfil och om vi skulle vilja ha en skulle vi montera med följande kommando:

avra -l tryckknapp.lst tryckknapp.asm

Om du gör detta kommer det att generera en fil som heter pushbutton.lst och om du undersöker den här filen kommer du att upptäcka att den visar din programkod tillsammans med extra information. Om du tittar på den extra informationen ser du att raderna börjar med ett C: följt av den relativa adressen i hex där koden placeras i minnet. I huvudsak börjar det på 000000 med det första kommandot och ökar därifrån med varje efterföljande kommando. Den andra kolumnen efter den relativa platsen i minnet är hex -koden för kommandot följt av hex -koden för kommandoets argument. Vi kommer att diskutera listfiler ytterligare i framtida självstudier.

.def temp = r16; ange arbetsregister r16 som temp

I denna rad använder vi assembler -direktivet ".def" för att definiera variabeln "temp" som lika med r16 "arbetsregistret". Vi kommer att använda register r16 som det som lagrar de nummer som vi vill kopiera till olika portar och register (som inte kan skrivas till direkt).

Övning 1: Försök att kopiera ett binärt tal direkt till en port eller specialregister som DDRB och se vad som händer när du försöker montera koden.

Ett register innehåller en byte (8 bitar) information. I huvudsak är det vanligtvis en samling SR-spärrar var och en är en "bit" och innehåller en 1 eller en 0. Vi kan diskutera detta (och till och med bygga en!) Senare i denna serie. Du kanske undrar vad som är ett "fungerande register" och varför vi valde r16. Vi kommer att diskutera det i en framtida handledning när vi dyker ner i kärren i chipets inre. För närvarande vill jag att du ska förstå hur man gör saker som att skriva kod och programmera fysisk hårdvara. Då kommer du att ha en referensram från den erfarenheten som kommer att göra minnes- och registeregenskaperna för mikrokontrollern lättare att förstå. Jag inser att de flesta inledande läroböcker och diskussioner gör detta tvärtom men jag har funnit att det är mycket lättare att läsa ett tv -spel ett tag först för att få ett globalt perspektiv innan man läser bruksanvisningen än att läsa manualen först.

rjmp Init; första raden utförd

Denna rad är ett "relativt hopp" till etiketten "Init" och är egentligen inte nödvändigt här eftersom nästa kommando redan finns i Init men vi inkluderar det för framtida bruk.

I det:

ser temp; ställ in alla bitar i temp till 1: or.

Efter Init -etiketten kör vi ett "set register" -kommando. Detta sätter alla de 8 bitarna i registret "temp" (som du minns är r16) till 1: or. Så temp innehåller nu 0b11111111.

ut DDRB, temp; ställer in lite som 1 på Data Direction I/O -registret

; för PortB, som är DDRB, anger denna pin som utgång; en 0 skulle ställa in den stiftet som ingång; så här är alla PortB -stift utgångar (inställda på 1)

Registret DDRB (Data Direction Register for PortB) berättar vilka stift på PortB (dvs PB0 till PB7) som betecknas som ingång och vilka som betecknas som utgång. Eftersom vi har stiftet PB0 anslutet till vår LED och resten inte är anslutet till någonting kommer vi att ställa in alla bitar till 1 vilket betyder att de alla är utgångar.

ldi temp, 0b11111110; ladda det 'omedelbara' numret till tempregistret

; om det bara var ld så skulle det andra argumentet; måste vara en minnesplats

Denna rad laddar det binära numret 0b11111110 i tempregistret.

ut DDRD, temp; mv temp till DDRD, resultatet är att PD0 är input och

; resten är utgångar

Nu ställer vi in datariktningsregistret för PortD från temp, eftersom temp fortfarande innehåller 0b11111110 ser vi att PD0 kommer att betecknas som en ingångsstift (eftersom det finns en 0 längst till höger) och resten är betecknade som utgångar eftersom det finns 1: or på de platserna.

clr temp; alla bitar i temp är inställda på 0: or

ut PortB, temp; ställ in alla bitar (dvs stift) i PortB till 0V

Först "rensar" vi registertempen vilket innebär att alla bitar ställs till noll. Sedan kopierar vi det till PortB -registret som sätter 0V på alla dessa stift. En nolla på en PortB -bit betyder att processorn kommer att behålla den stiftet vid 0V, en en på en bit kommer att göra att stiftet sätts till 5V.

Övning 2: Använd en multimeter för att kontrollera om alla stiften på PortB faktiskt är noll. Är det något konstigt som händer med PB1? Någon aning om varför det kan vara det? (liknande övning 4 nedan och följ sedan koden …) Övning 3: Ta bort de två ovanstående raderna från koden. Kör programmet fortfarande korrekt? Varför?

ldi temp, 0b00000001; ladda omedelbart nummer till temp

ut PortD, temp; flytta temp till PortD. PD0 är vid 5V (har ett pullup -motstånd); eftersom den har en 1 i den biten är resten 0V. Övning 4: Ta bort ovanstående två rader från din kod. Kör programmet fortfarande korrekt? Varför? (Detta skiljer sig från övning 3 ovan. Se pin -out -diagrammet. Vad är standardinställningen för DDRD för PD0? (Se sidan 90 i databladet

Först "laddar vi omedelbart" talet 0b00000001 till temp. Den "omedelbara" delen är där eftersom vi laddar ett rakt upp nummer till temp snarare än en pekare till en minnesplats som innehåller numret som ska laddas. I så fall skulle vi helt enkelt använda "ld" snarare än "ldi". Sedan skickar vi detta nummer till PortD som sätter PD0 till 5V och resten till 0V.

Nu har vi ställt in stiften som input eller output och vi har ställt in deras initialtillstånd som antingen 0V eller 5V (LOW eller HIGH) och så går vi nu in i vårt program "loop".

Main: i temp, PinD; PinD håller tillståndet för PortD, kopiera detta till temp

; om knappen är ansluten till PD0 så blir detta; a 0 när knappen trycks in, 1 annars sedan; PD0 har ett pull -up -motstånd, det är normalt vid 5V

Registret PinD innehåller det aktuella tillståndet för PortD -stiften. Om du till exempel har anslutit en 5V -kabel till PD3, då vid nästa klockcykel (vilket sker 16 miljoner gånger per sekund eftersom vi har mikrokontrollern ansluten till en 16MHz klocksignal) PinD3 -biten (från det aktuella tillståndet för PD3) skulle bli en 1 istället för en 0. Så i den här raden kopierar vi det nuvarande tillståndet för stiften till temp.

ut PortB, temp; skickar 0: orna och 1: orna ovan till PortB

; det betyder att vi vill att lysdioden är ansluten till PB0, så; när PD0 är LÅG ställer det PB0 på LÅGT och vrider; på lysdioden (den andra sidan av lysdioden är ansluten; till 5V och detta kommer att ställa PB0 till 0V så att strömmen flödar)

Nu skickar vi tillståndet för stiften i PinD till PortB -utgången. Detta innebär att PD0 skickar en 1 till PortD0 om inte knappen trycks in. I så fall eftersom knappen är ansluten till marken kommer stiftet att vara vid 0V och det kommer att skicka ett 0 till PortB0. Om du tittar på kretsschemat betyder 0V på PB0 att lysdioden kommer att lysa eftersom den andra sidan är 5V. Om vi inte trycker på knappen, så att en 1 skickas till PB0, skulle det betyda att vi har 5V på PB0 och även 5V på andra sidan av lysdioden och så finns det ingen potentialskillnad och ingen ström kommer att flöda och så kommer Lysdioden lyser inte (i det här fallet är det en lysdiod som är en diod och så strömmen flyter bara en riktning oavsett men vad som helst).

rjmp Main; går tillbaka till Start

Detta relativa hopp slingar oss tillbaka till vår Main: -etikett och vi kontrollerar PinD igen och så vidare. Kontrollerar var 16 miljoner sekund av om knappen trycks in och ställer in PB0 i enlighet därmed.

Övning 5: Ändra din kod så att din lysdiod är ansluten till PB3 istället för PB0 och se att den fungerar. Övning 6: Anslut din lysdiod till GND istället för 5V och ändra din kod därefter.

Steg 4: Slutsats

I denna handledning har vi vidare undersökt monteringsspråket för ATmega328p och lärt oss hur man styr en LED med en tryckknapp. I synnerhet lärde vi oss följande kommandon:

ser -register sätter alla bitar i ett register till 1: or

clr -registret sätter alla bitar i ett register till 0: or

i register kopierar i/o -registret numret från ett i/o -register till ett arbetsregister

I nästa handledning kommer vi att undersöka strukturen för ATmega328p och de olika registren, operationerna och resurserna i den.

Innan jag fortsätter med dessa självstudier kommer jag att vänta och se hur stort intresset är. Om det finns ett antal människor som faktiskt tycker om att lära sig att koda program för denna mikroprocessor i monteringsspråk, kommer jag att fortsätta och konstruera mer komplicerade kretsar och använda mer robust kod.

Rekommenderad: