Sommario:

Tutorial Assembler AVR 2: 4 passaggi
Tutorial Assembler AVR 2: 4 passaggi

Video: Tutorial Assembler AVR 2: 4 passaggi

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

Questo tutorial è una continuazione di "AVR Assembler Tutorial 1"

Se non hai seguito il Tutorial 1, dovresti fermarti ora e farlo prima.

In questo tutorial continueremo il nostro studio sulla programmazione in linguaggio assembly dell'atmega328p utilizzato in Arduino.

Avrai bisogno:

  1. una breadboard Arduino o solo un normale Arduino come nel Tutorial 1
  2. un LED
  3. un resistore da 220 ohm
  4. un pulsante
  5. cavi di collegamento per realizzare il circuito sulla breadboard
  6. Manuale del set di istruzioni: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Scheda tecnica: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

La raccolta completa dei miei tutorial può essere trovata qui:

Passaggio 1: costruire il circuito

Costruire il circuito
Costruire il circuito

Per prima cosa devi costruire il circuito che studieremo in questo tutorial.

Ecco come è collegato:

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

PD0 (pin 0 digitale) - pulsante - GND

Puoi verificare che il tuo LED sia orientato correttamente collegandolo a GND invece che a PB0. Se non succede nulla, invertire l'orientamento e la luce dovrebbe accendersi. Quindi ricollegalo a PB0 e continua. L'immagine mostra come è collegata la mia breadboard Arduino.

Passaggio 2: scrittura del codice assembly

Scrivere il codice assembly
Scrivere il codice assembly

Scrivi il seguente codice in un file di testo chiamato pushbutton.asm e compilalo con avra come hai fatto nel Tutorial 1.

Nota che in questo codice abbiamo molti commenti. Ogni volta che l'assemblatore vede un punto e virgola, salterà il resto della riga e passerà alla riga successiva. È buona pratica di programmazione (soprattutto in linguaggio assembly!) commentare pesantemente il codice in modo che quando tornerai ad usarlo in futuro saprai cosa stavi facendo. Commenterò spesso le cose nei primi tutorial in modo da sapere esattamente cosa sta succedendo e perché. In seguito, una volta che saremo diventati un po' più bravi nella codifica degli assembly, commenterò le cose in modo un po' meno dettagliato.

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

; scritto da: 1o_o7; data: 23 ottobre 2014;***********************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; designare il registro di lavoro r16 come temp rjmp Init; prima riga eseguita

Dentro:

temperatura di servizio; imposta tutti i bit in temp su 1. out DDRB, temp; impostando un bit a 1 su Data Direction I/O; register for PortB, che è DDRB, lo imposta; pin come output, uno 0 imposterebbe quel pin come input; quindi qui, tutti i pin PortB sono output (impostati su 1) ldi temp, 0b11111110; caricare il numero "immediato" nel registro temporaneo; se fosse solo ld allora il secondo argomento; dovrebbe essere una locazione di memoria invece di DDRD, temp; mv temp a DDRD, il risultato è che viene immesso PD0; e il resto sono output clr temp; tutti i bit in temp sono impostati a 0 out PortB, temp; imposta tutti i bit (cioè i pin) in PortB a 0V ldi temp, 0b00000001; carica il numero immediato su temp out PortD, temp; sposta la temperatura su PortD. PD0 ha una resistenza di pull-up; (cioè impostato su 5V) poiché ha un 1 in quel bit; il resto è 0V da 0.

Principale:

in temperatura, PinD; PinD mantiene lo stato di PortD, copialo in temp; se il pulsante è connesso a PD0 questo sarà; 0 quando si preme il pulsante, 1 altrimenti poiché; PD0 ha una resistenza di pull-up che normalmente è a 5V out PortB, temp; invia gli 0 e gli 1 letti sopra a PortB; questo significa che vogliamo che il LED sia collegato a PB0,; quando PD0 è LOW, imposta PB0 su LOW e ruota; sul LED (poiché l'altro lato del LED è; collegato a 5V e questo imposterà PB0 a 0V quindi; la corrente scorrerà) rjmp Main; torna all'inizio di Main

Nota che questa volta non solo abbiamo molti più commenti nel nostro codice, ma abbiamo anche una sezione di intestazione che fornisce alcune informazioni su chi l'ha scritto e quando è stato scritto. Anche il resto del codice è suddiviso in sezioni.

Dopo aver compilato il codice sopra, dovresti caricarlo sul microcontrollore e vedere che funziona. Il LED dovrebbe accendersi mentre si preme il pulsante e poi spegnersi di nuovo quando si rilascia. Ho mostrato come appare nella foto.

Passaggio 3: analisi riga per riga del codice

Salterò le righe che sono solo commenti poiché il loro scopo è evidente.

.nolist

.include "m328Pdef.inc".list

Queste tre righe includono il file contenente le definizioni di Registro e Bit per l'ATmega328P che stiamo programmando. Il comando.nolist dice all'assemblatore di non includere questo file nel file pushbutton.lst che produce quando lo assembli. Disattiva l'opzione di elenco. Dopo aver incluso il file, riattiviamo l'opzione di elenco con il comando.list. Il motivo per cui lo facciamo è perché il file m328Pdef.inc è piuttosto lungo e non è necessario vederlo nel file di elenco. Il nostro assembler, avra, non genera automaticamente un file di elenco e se ne desideriamo uno lo assembleremo utilizzando il seguente comando:

avra -l pulsante.lst pulsante.asm

Se lo fai, genererà un file chiamato pushbutton.lst e se esamini questo file scoprirai che mostra il codice del tuo programma insieme a informazioni extra. Se guardi le informazioni extra vedrai che le righe iniziano con una C: seguita dal relativo indirizzo in esadecimale di dove è posizionato il codice in memoria. Essenzialmente inizia a 000000 con il primo comando e aumenta da lì con ogni comando successivo. La seconda colonna dopo il relativo posto in memoria è il codice esadecimale per il comando seguito dal codice esadecimale per l'argomento del comando. Discuteremo ulteriormente i file di elenco nei tutorial futuri.

.def temp = r16; designare il registro di lavoro r16 come temp

In questa riga usiamo la direttiva assembler ".def" per definire la variabile "temp" come uguale al "registro di lavoro" r16. Useremo il registro r16 come quello che memorizza i numeri che vogliamo copiare su varie porte e registri (che non possono essere scritti direttamente).

Esercizio 1: Prova a copiare un numero binario direttamente in una porta o in un registro speciale come DDRB e guarda cosa succede quando provi ad assemblare il codice.

Un registro contiene un byte (8 bit) di informazioni. Essenzialmente è di solito una raccolta di SR-Latches, ognuno è un "bit" e contiene un 1 o uno 0. Potremmo discuterne (e persino costruirne uno!) Più avanti in questa serie. Forse ti starai chiedendo cos'è un "registro di lavoro" e perché abbiamo scelto r16. Ne discuteremo in un futuro tutorial quando ci immergeremo nel pantano degli interni del chip. Per ora voglio che tu capisca come fare cose come scrivere codice e programmare hardware fisico. Quindi avrai un quadro di riferimento da quell'esperienza che renderà più facile capire la memoria e le proprietà del registro del microcontrollore. Mi rendo conto che la maggior parte dei libri di testo introduttivi e delle discussioni fanno il contrario, ma ho scoperto che giocare a un videogioco per un po' prima di avere una prospettiva globale prima di leggere il manuale di istruzioni è molto più facile che leggere prima il manuale.

rjmp Init; prima riga eseguita

Questa riga è un "salto relativo" all'etichetta "Init" e non è realmente necessaria qui poiché il comando successivo è già in Init ma lo includiamo per un uso futuro.

Dentro:

temperatura di servizio; imposta tutti i bit in temp su 1.

Dopo l'etichetta Init eseguiamo un comando "set register". Questo imposta tutti gli 8 bit nel registro "temp" (che ricordi è r16) a 1. Quindi temp ora contiene 0b11111111.

out DDRB, temp; impostando un bit come 1 sul registro I/O Data Direction

; per PortB, che è DDRB, imposta quel pin come output; uno 0 imposterebbe quel pin come input; quindi qui, tutti i pin PortB sono output (impostati su 1)

Il registro DDRB (Data Direction Register for PortB) indica quali pin su PortB (cioè da PB0 a PB7) sono designati come input e quali sono designati come output. Poiché abbiamo il pin PB0 collegato al nostro LED e il resto non connesso a nulla, imposteremo tutti i bit a 1, il che significa che sono tutte uscite.

ldi temp, 0b11111110; caricare il numero "immediato" nel registro temporaneo

; se fosse solo ld, il secondo argomento sarebbe; deve essere una posizione di memoria

Questa riga carica il numero binario 0b11111110 nel registro temporaneo.

fuori DDRD, temp; mv temp a DDRD, il risultato è che PD0 viene immesso e

; il resto sono uscite

Ora impostiamo il registro di direzione dei dati per PortD da temp, poiché temp contiene ancora 0b11111110 vediamo che PD0 sarà designato come pin di input (poiché c'è uno 0 nel punto all'estrema destra) e il resto è designato come output poiché ci sono 1 è in quei punti.

clr temp; tutti i bit in temp sono impostati su 0

uscita PortaB, temp; imposta tutti i bit (cioè i pin) in PortB su 0V

Per prima cosa "cancelliamo" il registro temp, il che significa impostare tutti i bit a zero. Quindi lo copiamo nel registro PortB che imposta 0V su tutti quei pin. Uno zero su un bit PortB significa che il processore manterrà quel pin a 0V, uno su un bit farà sì che quel pin venga impostato su 5V.

Esercizio 2: Usa un multimetro per verificare se tutti i pin su PortB sono effettivamente zero. Sta succedendo qualcosa di strano con PB1? Qualche idea sul perché potrebbe essere? (simile all'Esercizio 4 qui sotto, quindi segui il codice…) Esercizio 3: Rimuovi le due righe precedenti dal tuo codice. Il programma funziona ancora correttamente? Come mai?

ldi temp, 0b00000001; carica il numero immediato in temp

out PortD, temp; sposta la temperatura su PortD. PD0 è a 5V (ha una resistenza di pullup); poiché ha un 1 in quel bit, il resto è 0V. Esercizio 4: Rimuovi le due righe precedenti dal tuo codice. Il programma funziona ancora correttamente? Come mai? (Questo è diverso dall'Esercizio 3 sopra. Vedi il diagramma di pin out. Qual è l'impostazione DDRD predefinita per PD0? (Vedi pagina 90 della scheda tecnica

Per prima cosa "carichiamo immediatamente" il numero 0b0000001 su temp. La parte "immediata" è lì poiché stiamo caricando un numero diretto su temp piuttosto che un puntatore a una posizione di memoria contenente il numero da caricare. In tal caso useremmo semplicemente "ld" invece di "ldi". Quindi inviamo questo numero a PortD che imposta PD0 su 5V e il resto su 0V.

Ora abbiamo impostato i pin come input o output e abbiamo impostato i loro stati iniziali come 0V o 5V (LOW o HIGH) e quindi ora entriamo nel nostro programma "loop".

Principale: in temp, PinD; PinD mantiene lo stato di PortD, copialo in temp

; se il pulsante è connesso a PD0 allora questo sarà; a 0 quando si preme il pulsante, 1 altrimenti poiché; PD0 ha una resistenza di pull up che normalmente è a 5V

Il registro PinD contiene lo stato attuale dei pin PortD. Ad esempio, se hai collegato un cavo da 5 V a PD3, quindi al successivo ciclo di clock (che accade 16 milioni di volte al secondo poiché abbiamo il microcontrollore collegato a un segnale di clock a 16 MHz) il bit PinD3 (dallo stato attuale di PD3) diventerebbe un 1 invece di uno 0. Quindi in questa riga copiamo lo stato attuale dei pin in temp.

uscita PortaB, temp; invia gli 0 e gli 1 letti sopra a PortB

; questo significa che vogliamo che il LED sia collegato a PB0, quindi; quando PD0 è LOW, imposterà PB0 su LOW e girerà; sul LED (l'altro lato del LED è collegato; a 5V e questo imposterà PB0 a 0V in modo che la corrente fluisca)

Ora inviamo lo stato dei pin in PinD all'uscita PortB. In effetti, ciò significa che PD0 invierà un 1 a PortD0 a meno che non venga premuto il pulsante. In tal caso, poiché il pulsante è collegato a massa, quel pin sarà a 0V e invierà uno 0 a PortB0. Ora, se guardi lo schema del circuito, 0V su PB0 significa che il LED si accenderà poiché l'altro lato è a 5V. Se non stiamo premendo il pulsante, in modo che venga inviato un 1 a PB0, ciò significherebbe che abbiamo 5V su PB0 e anche 5V sull'altro lato del LED e quindi non c'è differenza di potenziale e non scorrerà corrente e quindi il Il LED non si accenderà (in questo caso è un LED che è un diodo e quindi la corrente scorre solo in una direzione indipendentemente da qualsiasi cosa).

rjmp principale; torna a Start

Questo salto relativo ci riporta alla nostra etichetta Main: e controlliamo nuovamente PinD e così via. Controllare ogni 16 milionesimi di secondo se il pulsante viene premuto e impostare PB0 di conseguenza.

Esercizio 5: Modifica il codice in modo che il LED sia collegato a PB3 anziché a PB0 e verifica che funzioni. Esercizio 6: Collega il LED a GND anziché a 5 V e modifica il codice di conseguenza.

Passaggio 4: conclusione

In questo tutorial abbiamo studiato ulteriormente il linguaggio assembly per l'ATmega328p e abbiamo imparato a controllare un LED con un pulsante. In particolare abbiamo appreso i seguenti comandi:

ser register imposta tutti i bit di un registro su 1's

clr register imposta tutti i bit di un registro a 0

in register, i/o register copia il numero da un i/o register a un working register

Nel prossimo tutorial esamineremo la struttura dell'ATmega328p ei vari registri, operazioni e risorse in esso contenute.

Prima di continuare con questi tutorial, aspetterò e vedrò il livello di interesse. Se c'è un certo numero di persone a cui piace imparare a codificare programmi per questo microprocessore in linguaggio assembly, allora continuerò e costruirò circuiti più complicati e userò un codice più robusto.

Consigliato: