Inhoudsopgave:

AVR Assembler Tutorial 2: 4 stappen
AVR Assembler Tutorial 2: 4 stappen

Video: AVR Assembler Tutorial 2: 4 stappen

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

Deze tutorial is een vervolg op "AVR Assembler Tutorial 1"

Als je Tutorial 1 nog niet hebt doorlopen, moet je nu stoppen en die eerst doen.

In deze tutorial gaan we verder met onze studie van het programmeren in assembler van de atmega328p die in Arduino's wordt gebruikt.

Je zal nodig hebben:

  1. een breadboard Arduino of gewoon een normale Arduino zoals in Tutorial 1
  2. een LED
  3. een weerstand van 220 ohm
  4. een drukknop
  5. aansluitdraden voor het maken van de schakeling op je breadboard
  6. Handleiding handleiding: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Gegevensblad: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

De volledige verzameling van mijn tutorials is hier te vinden:

Stap 1: Het circuit bouwen

Het circuit bouwen
Het circuit bouwen

Eerst moet je het circuit bouwen dat we in deze tutorial zullen bestuderen.

Hier is de manier waarop het is aangesloten:

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

PD0 (digitale pin 0) - drukknop - GND

U kunt controleren of uw LED correct is georiënteerd door hem aan te sluiten op GND in plaats van op PB0. Als er niets gebeurt, keer dan de richting om en het lampje zou moeten gaan branden. Verbind het vervolgens opnieuw met PB0 en ga verder. De afbeelding laat zien hoe mijn breadboard arduino is aangesloten.

Stap 2: De montagecode schrijven

De montagecode schrijven
De montagecode schrijven

Schrijf de volgende code in een tekstbestand met de naam pushbutton.asm en compileer het met avra zoals je deed in Tutorial 1.

Merk op dat we in deze code veel opmerkingen hebben. Elke keer dat de assembler een puntkomma ziet, slaat hij de rest van de regel over en gaat hij door naar de volgende regel. Het is een goede programmeerpraktijk (vooral in assembler!) om veel commentaar op uw code te geven, zodat u, wanneer u er in de toekomst naar terugkeert, weet wat u aan het doen was. Ik ga in de eerste paar tutorials nogal wat opmerkingen maken, zodat we precies weten wat er aan de hand is en waarom. Later, als we een beetje beter worden in het coderen van assemblages, zal ik wat minder gedetailleerd commentaar geven.

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

; geschreven door: 1o_o7; datum: 23 okt 2014;***********************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; wijs werkregister r16 aan als temp rjmp Init; eerste regel uitgevoerd

In het:

ser temp; zet alle bits in temp op 1's. uit DDRB, temp; een bit instellen als 1 op de Data Direction I/O; register voor PortB, wat DDRB is, stelt dat in; pin als uitvoer, een 0 zou die pin als invoer instellen; dus hier zijn alle PortB-pinnen uitgangen (ingesteld op 1) ldi temp, 0b11111110; laad het `onmiddellijke' nummer in het tijdelijke register; als het gewoon ld was, dan het tweede argument; zou in plaats daarvan een geheugenlocatie moeten zijn DDRD, temp; mv temp naar DDRD, resultaat is dat PD0 wordt ingevoerd; en de rest zijn uitgangen clr temp; alle bits in temp zijn ingesteld op 0's out PortB, temp; stel alle bits (d.w.z. pinnen) in PortB in op 0V ldi temp, 0b00000001; laad direct nummer om uit te zenden PortD, temp; verplaats temp naar PortD. PD0 heeft een pull-up weerstand; (d.w.z. ingesteld op 5V) omdat er een 1 in dat bit zit; de rest is 0V sinds nullen.

Hoofd:

in temperatuur, PinD; PinD heeft de status van PortD, kopieer dit naar temp; als de knop is aangesloten op PD0 is dit; 0 wanneer de knop wordt ingedrukt, 1 anders sinds; PD0 heeft een pull-up-weerstand, deze is normaal gesproken op 5V uit PortB, temp; stuurt de nullen en 1's die hierboven zijn gelezen naar PortB; dit betekent dat we de LED willen aansluiten op PB0,; wanneer PD0 LAAG is, stelt het PB0 in op LAAG en draait; op de LED (aangezien de andere kant van de LED is; aangesloten op 5V en dit zal PB0 op 0V zetten, dus stroom zal vloeien) rjmp Main; keert terug naar het begin van Main

Merk op dat we deze keer niet alleen veel meer opmerkingen in onze code hebben, maar we hebben ook een koptekst die wat informatie geeft over wie het heeft geschreven en wanneer het is geschreven. De rest van de code is ook onderverdeeld in secties.

Nadat u de bovenstaande code hebt gecompileerd, moet u deze op de microcontroller laden en zien dat deze werkt. De LED moet aangaan terwijl u op de knop drukt en vervolgens weer uitgaan als u hem loslaat. Ik heb laten zien hoe het eruit ziet op de foto.

Stap 3: Line-by-line analyse van de code

Ik zal de regels overslaan die slechts opmerkingen zijn, omdat hun doel duidelijk is.

.nolist

.inclusief "m328Pdef.inc".lijst

Deze drie regels bevatten het bestand met de register- en bitdefinities voor de ATmega328P die we aan het programmeren zijn. Het.nolist commando vertelt de assembler om dit bestand niet op te nemen in het pushbutton.lst bestand dat het produceert wanneer je het assembleert. Het schakelt de aanbiedingsoptie uit. Na het opnemen van het bestand zetten we de listing-optie weer aan met het.list-commando. De reden dat we dit doen is omdat het bestand m328Pdef.inc vrij lang is en we het niet echt in het lijstbestand hoeven te zien. Onze assembler, avra, genereert niet automatisch een lijstbestand en als we er een zouden willen, zouden we het samenstellen met behulp van de volgende opdracht:

avra -l drukknop.lst drukknop.asm

Als u dit doet, wordt een bestand met de naam pushbutton.lst gegenereerd en als u dit bestand bekijkt, zult u zien dat het uw programmacode samen met extra informatie toont. Als je naar de extra informatie kijkt, zul je zien dat de regels beginnen met een C: gevolgd door het relatieve adres in hex van waar de code in het geheugen is geplaatst. In wezen begint het bij 000000 met de eerste opdracht en neemt vanaf daar toe met elke volgende opdracht. De tweede kolom na de relatieve plaats in het geheugen is de hexadecimale code voor het commando gevolgd door de hexadecimale code voor het argument van het commando. We zullen lijstbestanden verder bespreken in toekomstige tutorials.

.def temp = r16; wijs werkregister r16 aan als temp

In deze regel gebruiken we de assembler-richtlijn ".def" om de variabele "temp" te definiëren als gelijk aan het r16 "werkregister". We zullen register r16 gebruiken als degene die de nummers opslaat die we willen kopiëren naar verschillende poorten en registers (waar niet direct naar geschreven kan worden).

Oefening 1: Probeer een binair getal rechtstreeks naar een poort of speciaal register zoals DDRB te kopiëren en kijk wat er gebeurt als je de code probeert samen te stellen.

Een register bevat een byte (8 bits) aan informatie. In wezen is het meestal een verzameling SR-Latches, elk is een "bit" en bevat een 1 of een 0. We kunnen dit later in deze serie bespreken (en er zelfs een bouwen!) U vraagt zich misschien af wat een "werkregister" is en waarom we voor r16 hebben gekozen. We zullen dat in een toekomstige tutorial bespreken wanneer we in het moeras van de binnenkant van de chip duiken. Voor nu wil ik dat je begrijpt hoe je dingen kunt doen zoals het schrijven van code en het programmeren van fysieke hardware. Dan heb je vanuit die ervaring een referentiekader waardoor de geheugen- en registereigenschappen van de microcontroller beter te begrijpen zijn. Ik realiseer me dat de meeste inleidende leerboeken en discussies dit andersom doen, maar ik heb gemerkt dat het spelen van een videogame eerst een tijdje om een globaal perspectief te krijgen voordat je de handleiding leest, veel gemakkelijker is dan eerst de handleiding te lezen.

rjmp Init; eerste regel uitgevoerd

Deze regel is een "relatieve sprong" naar het label "Init" en is hier niet echt nodig omdat het volgende commando al in Init staat, maar we nemen het op voor toekomstig gebruik.

In het:

ser temp; zet alle bits in temp op 1's.

Na het Init label voeren we een "set register" commando uit. Dit stelt alle 8 bits in het register "temp" (waarvan u zich herinnert dat het r16 is) in op enen. Dus temp bevat nu 0b11111111.

uit DDRB, temp; een bit instellen als 1 in het Data Direction I/O-register

; voor PortB, wat DDRB is, stelt die pin in als uitvoer; een 0 zou die pin als invoer instellen; dus hier zijn alle PortB-pinnen uitgangen (ingesteld op 1)

Het register DDRB (Data Direction Register for PortB) vertelt welke pinnen op PortB (d.w.z. PB0 t/m PB7) als input en welke als output zijn aangewezen. Omdat we de pin PB0 hebben aangesloten op onze LED en de rest nergens op is aangesloten, zullen we alle bits op 1 zetten, wat betekent dat het allemaal uitgangen zijn.

ldi temp, 0b11111110; laad het `onmiddellijke' nummer in het tijdelijke register

; als het gewoon ld was, dan zou het tweede argument; moet een geheugenlocatie zijn

Deze regel laadt het binaire getal 0b11111110 in het tijdelijke register.

uit DDRD, temp; mv temp naar DDRD, resultaat is dat PD0 wordt ingevoerd en

; de rest zijn uitgangen

Nu stellen we het Data Direction Register voor PortD van temp in, aangezien temp nog steeds 0b11111110 bevat, zien we dat PD0 zal worden aangewezen als een invoerpin (aangezien er een 0 op de uiterst rechtse plek is) en de rest wordt aangewezen als uitvoer omdat er zijn 1 zit op die plekken.

clr temp; alle bits in temp zijn ingesteld op 0's

uit PoortB, temp; stel alle bits (d.w.z. pinnen) in PortB in op 0V

Eerst "wissen" we de registertemperatuur, wat betekent dat alle bits op nul worden gezet. Dan kopiëren we dat naar het PortB-register dat 0V instelt op al die pinnen. Een nul op een PortB-bit betekent dat de processor die pin op 0V houdt, een één op een bit zorgt ervoor dat die pin wordt ingesteld op 5V.

Oefening 2: Gebruik een multimeter om te controleren of alle pinnen op PortB eigenlijk nul zijn. Is er iets raars aan de hand met PB1? Enig idee waarom dat zou kunnen zijn? (vergelijkbaar met Oefening 4 hieronder, volg dan de code…) Oefening 3: Verwijder de bovenstaande twee regels uit je code. Loopt het programma nog goed? Waarom?

ldi-temp, 0b00000001; laad direct nummer naar temp

uit PoortD, temp; verplaats temp naar PortD. PD0 is op 5V (heeft een pullup-weerstand); aangezien het een 1 in dat bit heeft, is de rest 0V. Oefening 4: Verwijder de bovenstaande twee regels uit je code. Loopt het programma nog goed? Waarom? (Dit verschilt van Oefening 3 hierboven. Zie het pin-out diagram. Wat is de standaard DDRD-instelling voor PD0? (Zie pagina 90 van het gegevensblad

Eerst "laden we onmiddellijk" het nummer 0b00000001 naar temp. Het "onmiddellijke" deel is er omdat we een rechtstreeks nummer naar temp laden in plaats van een aanwijzer naar een geheugenlocatie met het nummer dat moet worden geladen. In dat geval zouden we gewoon "ld" gebruiken in plaats van "ldi". Vervolgens sturen we dit nummer naar PortD die PD0 instelt op 5V en de rest op 0V.

Nu hebben we de pinnen ingesteld als invoer of uitvoer en we hebben hun begintoestanden ingesteld als 0V of 5V (LAAG of HOOG) en dus gaan we nu ons programma "lus" in.

Hoofd: in temp, PinD; PinD heeft de status van PortD, kopieer dit naar temp

; als de knop is aangesloten op PD0 dan is dit; een 0 wanneer de knop wordt ingedrukt, 1 anders sinds; PD0 heeft een pull-up weerstand, deze is normaal gesproken 5V

Het register PinD bevat de huidige status van de PortD-pinnen. Als u bijvoorbeeld een 5V-draad op PD3 hebt aangesloten, dan bij de volgende klokcyclus (wat 16 miljoen keer per seconde gebeurt omdat we de microcontroller hebben aangesloten op een 16MHz-kloksignaal) de PinD3-bit (van de huidige status van PD3) zou een 1 worden in plaats van een 0. Dus in deze regel kopiëren we de huidige status van de pinnen naar temp.

uit PoortB, temp; stuurt de nullen en 1's die hierboven zijn gelezen naar PortB

; dit betekent dat we de LED aangesloten willen hebben op PB0, dus; wanneer PD0 LAAG is, wordt PB0 op LAAG gezet en wordt; op de LED (de andere kant van de LED is aangesloten; op 5V en dit zal PB0 op 0V zetten zodat er stroom vloeit)

Nu sturen we de status van de pinnen in PinD naar de PortB-uitgang. In feite betekent dit dat PD0 een 1 naar PortD0 stuurt, tenzij de knop wordt ingedrukt. In dat geval, aangezien de knop is verbonden met aarde, staat die pin op 0V en stuurt hij een 0 naar PortB0. Als je nu naar het schakelschema kijkt, betekent 0V op PB0 dat de LED oplicht, aangezien de andere kant 5V is. Als we niet op de knop drukken, zodat er een 1 naar PB0 wordt gestuurd, zou dat betekenen dat we 5V op PB0 hebben en ook 5V aan de andere kant van de LED en dus is er geen potentiaalverschil en loopt er geen stroom en dus de LED zal niet gloeien (in dit geval is het een LED die een diode is en dus stroomt de stroom maar in één richting, ongeacht maar wat dan ook).

rjmp Hoofd; keert terug naar Start

Deze relatieve sprong brengt ons terug naar ons Main:-label en we controleren PinD opnieuw enzovoort. Elke 16 miljoenste van een seconde controleren of de knop wordt ingedrukt en PB0 dienovereenkomstig instellen.

Oefening 5: Pas uw code aan zodat uw LED is aangesloten op PB3 in plaats van PB0 en kijk of het werkt. Oefening 6: Sluit uw LED aan op GND in plaats van 5V en pas uw code dienovereenkomstig aan.

Stap 4: Conclusie

In deze tutorial hebben we de assembleertaal voor de ATmega328p verder onderzocht en geleerd hoe je een LED aanstuurt met een drukknop. In het bijzonder hebben we de volgende commando's geleerd:

ser register stelt alle bits van een register in op 1's

clr register stelt alle bits van een register in op nullen

in register, i/o register kopieert het nummer van een i/o register naar een werkregister

In de volgende zelfstudie zullen we de structuur van de ATmega328p en de verschillende registers, bewerkingen en bronnen die daarin zijn opgenomen onderzoeken.

Voordat ik verder ga met deze tutorials, wacht ik af en zie ik de mate van interesse. Als er een aantal mensen zijn die het echt leuk vinden om programma's voor deze microprocessor in assembler te leren coderen, dan zal ik doorgaan en meer gecompliceerde circuits bouwen en robuustere code gebruiken.

Aanbevolen: