; **************************************************************************************** ; ; File: AVR_BLINK.ASM * ; Data: 14.Dic.2005 * ; Version: 1.0 * ; IDE: AVR-Studio 4.0 * ; * ; Autore: Giuseppe Franco - giuseppe.franco@chilab.polito.it * ; Riferimenti: Politecnico di Torino - Laboratorio Materiali e Microsistemi * ; ; **************************************************************************************** ; Questo programma serve ad imparare come utilizzare le porte di I/O ; del microcontrollore, in lettura ed in scrittura, in particolare: ; ; --> quali operazioni effettuare prima dell'inizio di un programma. ; --> come configurare le porte per il loro utilizzo in ingresso ed in uscita. ; --> come accedere ai registri interni del microcontrollore. ; --> come accedere ad una porta di uscita (accendere due led). ; --> come accedere ad una porta in ingresso (lettura di un deviatore). ; --> come generare ritardi per mezzo delle istruzioni disponibili. ; --> l'utilizzo di alcune direttive assembler. ; ; **************************************************************************************** .NOLIST .INCLUDE "atmega8.inc" .LIST ; la direttiva .NOLIST and .LIST esclude/include dal listato il contenuto dei file ; compreso tra queste due direttive ; (In particolare il file 'atmega8.inc' non sarà incluso nel file AVR_BLINK.LST ; generato durante la compilazione). ; La direttiva .INCLUDE serve per inserire un file nella posizione in cui la direttiva ; appare. In genere viene utilizzata per includere parti del sorgente relative ; alla definizione di costanti, macro, etc.. ; Un utilizzo, in genere, è quello di inserire le definizioni dei riferimenti ; ai registri ed ai bit di un particolare microcontrollore. ; In questo caso è stato incluso il file 'atmega8.inc' relativo al microcontrollore ; ATmega8L utilizzato per eseguire questo programma. ; Possono esistere inclusioni nidificate. ; Definizione dei registri utilizzati .DEF mp = R16 .DEF n1 = R17 .DEF n2 = R18 .DEF n3 = R19 ; La direttiva .DEF rinomina uno dei 32 registri di utilizzo generale con ; un nome facile da ricordare durante la scrittura e la rilettura del ; codice sorgente. ; Il compilatore effettua una semplice sostituzione di ciascuna occorrenza ; che trova nel listato durante la compilazione. ; Un altro vantaggio non trascurabile è rappresentato dal fatto che quando ; si vuole cambiare un registro, è sufficiente ridefinire il nome all'inizio ; del sorgente, senza dovere modificare tutti nomi i attraverso l'intero listato. rjmp main ; Quando l'AVR viene avviato, egli comincia l'esecuzione del codice dalla ; locazione all'indirizzo 0x0000. In generale questo indirizzo rappresenta ; l'inizio del codice in seguito ad un evento di reset, provocato dal ; power-on, da un reset esterno proveniente dal pin, o dal timer del watchdog ; che raggiunge in valore di zero. ; Se non specificato, l'assemblatore produce il codice in modo che la prima ; istruzione si allocata all'indirizzo 0x0000, per cui è necessario che ; tale istruzione corrisponda all'inizio del programma, oppure in generale ; che sia un salto alla routine principale del programma. ; Vedremo in seguito l'utilità di inserire come prima istruzione un salto ; alla routine principale. main: ; In questo punto inizia la routine principale del programma, che deve ; essere un 'loop' chiuso, o terminare con un 'loop' infinito. ; Il listato assembler è costituito, in generale da una prima etichetta ; o 'label' seguita da ':' e seguita dal codice 'mnemonico' dell'istruzione ; da eseguire. Tale etichetta definisce un nome relativo ad una posizione ; di programma (indirizzo) per tutte le istruzioni che necessitano di ; un riferimento ad una particolare zona di programma, ad esempio le ; istruzioni di salto o chiamata a procedura. ldi mp,0x02 out SPH,mp ldi mp,0x5f out SPL,mp ; E' ***** F O N D A M E N T A L E ***** che una delle prime istruzioni provvedano ; ad impostare il valore dello stack pointer !!!!! ; in ogni caso ciò deve ; avvenire almeno prima di una chiamata effettuata attraverso una 'call', ; prima di abilitare un'interrupt, o prima di utilizzare istruzioni che ; intervengano a modificarne il contenuto durante il programma. ; In genere lo stack viene allocato all'indirizzo corrispondente alla parte ; alta della RAM disponibile, poiché lo stack pointer, quando utilizzato viene ; decrementato e successivamente incrementato dopo l'utilizzo. ldi mp,0b00000001 ; La prima operazione da fare per utilizzare una porta di I/O è quella di definire ; quali pin sono utilizzati come uscite e quali come ingressi. ; I pin che sono usati come uscite, nel nostro caso per pilotare un led, devono ; avere il corrispondente bit nel registro direzione dati ad '1'. ; I rimanenti in ingresso devono avere i bit a '0'. ; Il comando ldi effettua un'operazione con indirizzamento immediato, cioè il secondo ; operando è rappresentato da una costante: in questo caso il registro viene caricato ; con la costante binaria indicata. ; Le costanti sono precedute da un suffisso che ne rappresenta il tipo di codifica: ; 0b.... se numero binario, 0x... o $... se esadecimale, senza suffisso se decimale. out DDRC,mp ; L'istruzione out serve per scrivere un valore in un registro di I/O, in questo ; caso il valore di R16 nel registro direzione dati. loop: ldi mp,0x00 out PORTC,mp ; Questa istruzione scrive il valore di zero nella porta C, il led resta spento. rcall delay ; Questa esegue una chiamata ad una routine che genera un ritardo 'software'. ; In particolare l'istruzione 'rcall' a differenza di 'call' effettua un salto ; ad un indirizzo Relativo alla posizione corrente all'interno di un intervallo ; compreso tra +/-2K, dato che sono disponibili 10 bit di salto sugli indirizzi. ; Il vantaggio è che in questo modo si esegue un salto 'vicino' utilizzando ; un'unico codice operativo. Nel caso di un salto 'lungo' la destinazione è ; un indirizzo assoluto in tutto il range indirizzabile, ma richiede una word ; di codice aggiuntiva per memorizzare la destinazione del salto. ldi mp,0x01 out PORTC,mp ; Questa istruzione scrive il valore di uno nella porta C, il led resta acceso. rcall delay rjmp loop ; Questo loop viene eseguito all'infinito e genera un lampeggio del led collegato ; sulla porta C. ; Questa routine genera un ritardo eseguendo due 'loop' annidati, ciascuno dei quali ; decrementa un contatore su 8-bit. In questo modo vengono eseguiti 9*256*256 istruzioni ; di decremento e di salto condizionato. ; Conoscendo la frequenza di clock del microprocessore, i cicli macchina di ciascuna ; istruzione, è possibile calcolare il tempo totale che impiega la routine ad uscire. ; Nel caso particolare di un clock di 8MHz il ritardo è di circa 200 ms. delay: ldi n3,0x9 delay_loop3: ldi n2,0xff delay_loop2: ldi n1,0xff delay_loop1: dec n1 brne delay_loop1 dec n2 brne delay_loop2 dec n3 brne delay_loop3 ret