AVR Assembler Tutorial 2 (3 / 4 steg)
Steg 3: Rad-för-rad analys av koden
Jag kommer att hoppa över raderna som är bara kommentarer som deras syfte är självklart.
.nolist
.include ". / m328Pdef.inc"
.lista
Dessa tre linjer omfatta filen som innehåller de Register och lite definitionerna för de ATmega328P som vi programmering. Kommandot .nolist berättar assembler inte att inkludera denna fil i filen pushbutton.lst som produceras när du monterar den. Den inaktiverar alternativet lista. Efter inklusive fil aktivera vi alternativet lista igen med kommandot .lista. Anledningen till att vi gör detta är att filen m328Pdef.inc är ganska lång och vi egentligen inte behöver se den i filen list. Vår assembler, avra, automatiskt generera inte en lista fil och om vi vill ha en vi skulle montera användande den följande befalla:
Avra -l pushbutton.lst pushbutton.asm
Om du gör det kommer den att generera en fil som heter pushbutton.lst och om du undersöka filen hittar du att den visar din programkod tillsammans med extra information. Om man tittar på den extra informationen visas som rader börjar med en C: följt av relativa adress i hex där koden är placerat i minnet. I huvudsak det börjar vid 000000 med det första kommandot och ökar därifrån med varje efterföljande kommandon. Den andra kolumnen efter den relativa platsen i minnet är hex koden för kommandot följt av hex koden för argumentet för kommandot. Vi kommer att diskutera listan filer vidare i framtida tutorials.
.def temp = r16; utnämner temp arbetande register r16
I denna linje använda vi assembler direktiv "def" för att definiera variabeln "temp" som lika till r16 "arbetande register." Vi kommer använda registret r16 som den som lagrar de siffror som vi vill kopiera till olika hamnar och register (som inte kan skrivas till direkt).
Övning 1: Prova att kopiera ett binärt tal direkt till en hamn eller ett särskilt register som DDRB och se vad som händer när du försöker montera koden.
Ett register innehåller en byte (8 bitar) av information. I huvudsak är det oftast en samling av SR-lås var och en är "lite" och innehåller 1 eller 0. Vi kan diskutera detta (och även bygga en!) senare på i denna serie. Du kanske undrar vad är en "arbetande register" och varför vi valde r16. Vi kommer att diskutera som i en framtida handledning när vi dyker i gungfly av de interna delarna av chipet. För nu vill jag du ska förstå hur man gör saker som att skriva och programfiler fysiska maskinvara. Du har en referensram från den erfarenheten som gör minnet och registrera egenskaper av mikrokontroller lättare att förstå. Jag inser att de flesta inledande läroböcker och diskussioner göra detta tvärtom men jag har funnit att spela ett spel ett tag först och dicking runt att få ett globalt perspektiv och sedan läsa bruksanvisningen att bli bättre på det är mycket lättare än att läsa manualen först.
rjmp Init; första raden avrättades
Denna linje är en "relativ hoppa" på etiketten "Init" och är inte nödvändigt här sedan nästa kommando är redan i Init men vi inkludera den för framtida bruk.
Init:
ser härda; Ange alla bitar i temp till 1.
Efter Init etikett utföra vi ett kommando för "set register". Det här alternativet anger alla 8 bitar i registret "temp" (som ni minns är r16) 1. Så temp innehåller nu 0b11111111.
ut DDRB, temp; ställa in lite eftersom 1 på Data riktning I/O registrerar
; för PortB, som är DDRB, anger att pin som utdata
; en 0 skulle ange den pin som indata
; så här, är alla PortB stift utgångar (värdet 1)
Registret DDRB (Data riktning Register för PortB) berättar som stiften på PortB (dvs. PB0 genom PB7) betecknas som input och som designeras som utgång. Eftersom vi har stiftet PB0 ansluten till våra LED och resten inte ansluten till något kommer att vi ställa alla bitar till 1 vilket betyder att de är alla utgångar.
LDI temp, 0b11111110; Ladda för "omedelbar" numret temp registret
; om det vore bara ld så skulle det andra argumentet
; måste vara en minnesplats
Denna linje laddar den binära nummer 0b11111110 till temp registret.
ut DDRD, temp; MV temp till DDRD, resultatet är att PD0 är ingång och
; resten är utgångar
Nu satt vi Data riktning registrera för PortD från temp, eftersom temp fortfarande innehåller 0b11111110 ser vi att PD0 kommer att utses som en ingångsstift (eftersom det är en 0 på alldeles rätt plats) och resten är utsedda som resultat eftersom det finns 1 i dessa fläckar.
Färgtemp; alla bitarna i temp anges till 0 är
ut PortB, temp; som alla bitar (dvs stift) i PortB till 0V
Vi avmarkera"" registrera temp vilket innebär att alla bitar till noll. Sedan kopiera vi som till PortB registret som sätter 0V på alla dessa stift. En nolla på lite PortB innebär att processorn kommer att hålla det klämmer fast på 0V, en en på lite kommer att orsaka att PIN-koden ska vara 5V.
Övning 2: Använda en multimeter för att kontrollera om alla stiften på PortB är faktiskt noll. Är något konstigt händer med PB1? Någon aning om varför det kan vara? (liknande Övning 4 nedan sedan följa koden...)
Övning 3: Ta bort ovanstående två rader från din kod. Programmet fortfarande körs korrekt? Varför?
LDI temp, 0b00000001; Ladda omedelbara nummer för att härda
ut PortD, temp; flytta temp till PortD. PD0 är på 5V (har en pullup motstånd)
; eftersom det har är en 1 i det lite resten 0V.
Övning 4: Ta bort ovanstående två rader från din kod. Programmet fortfarande körs korrekt? Varför? (Detta är olika från Övning 3 ovan. Se stiftet ut diagrammet. Vad är DDRD standardinställningen för PD0? (Se sida 90 av datablad)
Först vi "Ladda omedelbar" nummer 0b00000001 att härda. Den "omedelbara" del är det eftersom vi läser in en rakt upp numret till temp-snarare än en pekare till en minnesplats som innehåller det nummer som ska läsas in. I så fall skulle vi helt enkelt använda "ld" istället för "ldi". Sedan skickar vi detta nummer till PortD som anger PD0 för 5V och resten till 0V.
Nu har vi satt stiften som in- eller utdata och vi har satt upp sina första stater som 0V eller 5V (låg eller hög) och så vi nu in vårt program "loop".
Main: i temp, PinD; PinD innehar tillståndet för PortD, kopiera detta för att härda
; Om knappen är ansluten till PD0 då detta kommer att
; 0 när knappen trycks, 1 annars sedan
; PD0 har en dra upp motstånd det är normalt på 5V
Registret PinD innehåller aktuella status PortD stift. Till exempel, om du har kopplat en 5V tråd till PD3, sedan på nästa klockan cykel (vilket händer 16 miljoner gånger per sekund eftersom vi har mikrokontroller ansluten till en 16MHz klocksignal) PinD3 lite (från det aktuella läget i PD3) skulle bli en 1 istället för 0. Så i denna linje kopiera vi det aktuella läget för stiften till temp.
ut PortB, temp; skickar 0 och 1 är läsa ovan till PortB
; Detta innebär att vi vill ha LED ansluten till PB0, så
; När PD0 är låg, kommer att det ställa in PB0 till låg och vända
; på LED (andra sidan av lysdioden är ansluten
; till 5V och detta ställer PB0 till 0V så strömmen flödar)
Nu skickar vi statligt av stiften i PinD till PortB utdata. Effektivt, innebär detta att PD0 kommer att sända en 1 till PortD0 om inte trycker på knappen. I så fall eftersom knappen är ansluten till marken det klämmer fast kommer att vara på 0V och en 0 skickas till PortB0. Nu, om du tittar på kopplingsschemat, 0V på PB0 betyder LED lyser sedan den andra sidan av det är på 5V. Om vi inte att trycka på knappen, så att en 1 skickas till PB0, som skulle innebära att vi har 5V på PB0 och även 5V på andra sidan av LED och så finns det ingen spänningsskillnad och ingen ström kommer att flyta och så lyser lysdioden inte (i detta fall är det en LED som är en diod och så aktuella bara flödar en riktning oavsett men oavsett).
rjmp huvudsakliga; loopar tillbaka till Start
Denna relativa hopp öglorna oss tillbaka till vår huvudsakliga: etikett och vi kontrollera PinD igen och så vidare. Kontrollera varje 16 miljondel av en sekund om knappen trycks och att PB0 därmed.
Övning 5: Ändra din kod så att din LED är ansluten till PB3 istället för PB0 och se att det fungerar.
Övning 6: Anslut din LED till GND istället för 5V och ändra din kod därefter.