Analizziamo ora linea per linea il contenuto del nostro
source LED.ASM. Per chi dispone di una stampante è
utile effettuare una stampa del source per poter meglio seguire la nostra descrizione.
Altrimenti è preferibile visualizzare il source in una finestra separata in modo da seguire simultaneamente il
source e la relativa spiegazione.
Partiamo dalla prima linea di codice:
PROCESSOR 16F84
PROCESSOR è una
direttiva del compilatore assembler che consente di definire per quale microprocessore è
stato scritto il nostro source. Le direttive non sono delle istruzioni mnemoniche che il
compilatore traduce nel rispettivo opcode, ma delle semplici indicazioni rivolte al
compilatore per determinarne il funzionamento durante la compilazione. In questo caso
informiamo il compilatore che le istruzioni che abbiamo inserito nel nostro source sono
relative ad un PIC16F84.
RADIX DEC
La direttiva RADIX
serve ad informare il compilatore che i numeri riportati senza notazione, sono da intendersi come numeri decimali. Ovvero
se intendiamo specificare, ad esempio il numero esadecimale 10 (16 decimale) non possiamo
scrivere solamente 10 perché verrebbe interpretato come 10 decimale, ma 10h oppure 0x10
oppure H'10'.
INCLUDE "P16F84.INC"
Ecco un'altra direttiva. Questa volta indichiamo al compilatore la
nostra intenzione di includere nel source un secondo file denominato P16F84.INC. Il
compilatore si limiterà a sostituire la linea contenente la direttiva INCLUDE con il
contenuto del file indicato e ad effettuare quindi la compilazione come se fosse anch'esso
parte del nostro source.
LED EQU 0
Ancora direttive ! Ma quando arrivano le istruzioni ? Ancora un po di
pazienza.
La direttiva EQU è molto importante in quanto ci
consente di definire delle costanti simboliche all'interno del nostro source. In
particolare la parola LED da questo punto in poi del source sarà
equivalente al valore 0. Lo scopo principale dell'esistenza della direttiva EQU è quindi
rendere i source più leggibili e consentire di cambiare i valori costanti in un unico
punto del source.
E' importante notare che la parola LED non identifica una variabile ma
semplicemente un nome simbolico valido durante la compilazione. Non sarà quindi possibile
inserire instruzioni tipo LED = 3 all'interno del source in quanto l'assegnazione dinamica
di un valore ad una variabile è un'operazione che richiede l'intervento della CPU del PIC
e che quindi deve essere espressa con istruzioni e non con direttive.
Le direttive hanno senso solo durante la compilazione del
source quindi un PIC non potrà mai eseguire una direttiva.
Vediamo ora la linea seguente:
ORG 0CH
Anche ORG è una direttiva e ci consente di definire
l'indirizzo da cui vogliamo che il compilatore inizi ad allocare i dati o le istruzioni
seguenti. In questo caso stiamo per definire un'area dati all'interno del PIC ovvero
un'area in cui memorizzare variabili e contatori durante l'esecuzione del nostro
programma. Quest'area coincide con l'area RAM del PIC definita dalla Microchip come area
dei FILE REGISTER.
I file register altro non sono che locazioni RAM disponibili per
l'utente a partire dall'indirizzo 0CH. Questo indirizzo di inizio è fisso e non può
essere cambiato in quanto le locazioni precedenti sono occupate da altri registri
specializzati per uso interno.
Count RES 2
In questa linea incontriamo una label: Count e una
direttiva: RES.
La direttiva RES indica al compilatore che intendiamo riservare un certo
numero di byte o meglio di file register all'interno dell'area dati; in questo caso 2
byte. La label Count, dove Count è un nome scelto da noi, è un marcatore che nel resto
del source assumerà il valore dell'indirizzo in cui è stato inserito. Dato che
precedentemente avevamo definito l'indirizzo di partenza a 0CH con la direttiva ORG, Count
varrà 0CH. Se ad esempio inseriamo una label anche alla linea successiva essa varrà 0CH
+ 2 (due sono i byte che abbiamo riservato) ovvero 0EH. I nomi delle label possono essere
qualsiasi ad eccezione delle parole riservate al compilatore quali sono le istruzioni
mnemoniche e le direttive).
Una label si distingue da una costante simbolica perchè il suo valore
viene calcolato in fase di compilazione e non assegnato da noi staticamente.
ORG 00H
Questa seconda direttiva ORG fà riferimento ad un indirizzo in area
programma (nella EEPROM) anzichè in area dati. Da questo punto in poi andremo infatti ad
inserire le istruzioni mnemoniche che il compilatore dovrà convertire negli opportuni
opcode per il PIC.
Il primo opcode eseguito dal PIC dopo il reset è quello memorizzato
nella locazione 0, da qui il valore 00H inserito nella ORG.
bsf STATUS,RP0
Ecco finalmente la prima istruzione mnemonica completa di parametri. I
PIC hanno una CPU interna di tipo RISC per cui ogni istruzione occupa una
sola locazione di memoria, opcode e parametri inclusi. In questo caso l'istruzione
mnemonica bsf sta per BIT SET FILE
REGISTER ovvero metti a uno (condizione logica alta) uno dei bit contenuti nella locazione
di ram specificata.
Il parametro STATUS viene definito nel file P16F84.INC tramite una direttiva EQU. Il valore
assegnato in questo file è 03H e corrisponde ad un file register (ovvero una locazione
ram nell'area dati) riservato.
Anche il parametro RP0 viene definito nel file P16F84.INC con valore 05H
e corrisponde al numero del bit che si vuole mettere a uno. Ogni file register è lungo 8
bit e la numerazione di ciascuno parte da 0 (bit meno significativo) fino ad arrivare a 7
(bit più significativo)
Questa istruzione in pratica mette a 1 il quinto bit del file register
STATUS. Questa operazione è necessaria, come vedremo nelle lezioni successive, per
accedere ai file register TRISA e TRISB come vedremo ora.
movlw 00011111B
Questa istruzione sta a significare: MOVE LITERAL
TO W REGISTER ovvero muovi un valore costante nell'accumulatore. Come
avremo modo di vedere più avanti, l'accumulatore è un particolare registro utilizzato
dalla CPU in tutte quelle situazioni in cui vengono effettuate operazioni tra due valori
oppure in operazioni di spostamento tra locazioni di memoria. In pratica è un registro di
appoggio utilizzato dalla CPU per memorizzare temporaneamente un byte ogni volta che se ne
presenta la necessità.
Il valore costante da memorizzare nell'accumulatore è 00011111B
ovvero un valore binario a 8 bit dove il bit più a
destra rappresenta il bit 0 o bit meno significativo.
Nell'istruzione successiva:
movwf TRISA
il valore 00011111 viene memorizzato nel registro TRISA (come per il
registro STATUS anche TRISA è definito tramite una direttiva EQU) la cui funzione è
quella di definire il funzionamento di ogni linea di I/O della porta A. In particolare
ogni bit ad uno del registro TRISA determina un ingresso sulla rispettiva linea della
porta A mentre ogni 0 determina un'uscita.
Nella seguente tabella viene riportata la configurazione che assumeranno
i pin del PIC dopo l'esecuzione di questa istruzione:
N.bit registro
TRISB |
Linea porta A |
N.Pin |
Valore |
Stato |
0 |
RA0 |
17 |
1 |
Ingresso |
1 |
RA1 |
18 |
1 |
Ingresso |
2 |
RA2 |
1 |
1 |
Ingresso |
3 |
RA3 |
2 |
1 |
Ingresso |
4 |
RA4 |
3 |
1 |
Ingresso |
5 |
- |
- |
0 |
- |
6 |
- |
- |
0 |
- |
7 |
- |
- |
0 |
- |
Come è possibile vedere i bit 5, 6 e 7 non corrispondono
a nessuna linea di I/O e quindi il loro valore non ha alcuna influenza.
Le due istruzioni successive svolgono le stesse funzioni per la porta B
del PIC:
movlw B'11111110'
movwf TRISB
in questo caso la definizione delle linee sarà la seguente:
N.bit registro
TRISB |
Linea porta B |
N.Pin |
Valore |
Stato |
0 |
RB0 |
6 |
0 |
Uscita |
1 |
RB1 |
7 |
1 |
Ingresso |
2 |
RB2 |
8 |
1 |
Ingresso |
3 |
RB3 |
9 |
1 |
Ingresso |
4 |
RB4 |
10 |
1 |
Ingresso |
5 |
RB5 |
11 |
1 |
Ingresso |
6 |
RB6 |
12 |
1 |
Ingresso |
7 |
RB7 |
13 |
1 |
Ingresso |
Notate come il valore 0 nel bit 0 del registro TRISB
determini la configurazione in uscita della rispettiva linea del PIC. Nella nostra
applicazione infatti questa linea viene utilizzata per pilotare il LED da far lampeggiare.
Abbiamo visto che l'istruzione movwf TRISB trasferisce
il valore contenuto nell'accumulatore (inizializzato opportunamente con l'istruzione movlw
11111110B) nel registro TRISB. Il significato di movwf è infatti MOVE
W TO FILE REGISTER.
bcf STATUS,RP0
Questa istruzione è simile alla bsf vista in
precedenza, con la sola differenza che azzera il bit anzichè metterlo a uno. La sigla un
questo caso è BIT CLEAR FILE REGISTER.
Dal punto di vista funzionale questa istruzione è stata inserita per
consentire l'accesso ai registri interni del banco 0 anzichè ai registri interni del
banco 1 di cui fanno parte TRISA e TRISB. Una descrizione più dettagliata verrà data
più avanti in questo corso.
bsf PORTB,LED
Con questa istruzione viene effettuata la prima operazione che ha
qualche riscontro all'esterno del PIC. In particolare viene acceso il led collegato alla
linea RB0. PORTB è una costante definita in P16F84.INC e consente di
referenziare il file register corrispondente alle linee di I/O della porta B mentre LED
è il numero della linea da mettere a 1. Se ben ricordate, all'inizio del source la
costante LED è stata definita pari a 0, quindi la linea interessata sarà RB0.
MainLoop
Questa linea contiene una label ovvero un riferimento
simbolico ad un indirizzo di memoria. Il valore della label, come detto in precedenza,
viene calcolato in fase di compilazione in base al numero di istruzioni, alle direttive
ORG e alle altre istruzione che in qualche modo allocano spazio nella memoria del PIC. In
questo caso, se contiamo le istruzioni inserite a partire dall'ultima direttiva ORG
possiamo calcolare il valore che verrà assegnato a MainLoop ovvero 07H.
In realtà il valore che assumono le label non ha molta importanza in
quanto il loro scopo è proprio quello di evitare di dover conoscere la posizione precisa
degli opcode nella memoria del PIC permettendo comunque di referenziare una determinata
locazione di memoria.
In questo caso la label MainLoop viene utilizzata come punto di ingresso
di un ciclo (dall'inglese Loop) di accensione e spegnimento del led, ovvero una parte di
codice che verrà ripetuta ciclicamente all'infinito. Incontreremo più avanti un
riferimento a questa label.
call Delay
Questa istruzione dermina una chiamata (dall'inglese call) ad
una subroutine che inizia in corrispondenza della label Delay.
Le subroutine sono delle parti di programma specializzare ad effettuare
una funzione specifica. Ogni qualvolta è necessaria quella funzione è sufficiente
richiamarla con una sola istruzione, anzichè ripetere ogni volta tutte le istruzioni
necessarie ad effettuarla. In questo caso la subroutine inserisce un ritardo pari al tempo
di accensione e spegnimento del led.
Le istruzioni che compongono la subroutine Delay
sono inserite più avanti in questo stesso source.
btfsc PORTB,LED
Il significato di questa istruzione è BIT TEST
FLAG, SKIP IF CLEAR ovvero controlla lo
stato di un bit all'interno di un registro e salta l'istruzione successiva se il valore di
tale bit è zero. Il bit da controllare corrisponde alla linea di uscita cui è collegato
il diodo led, tramite questo test potremo determinare quindi se il led è acceso o spento
e quindi agire di conseguenza, ovvero se il led è gia acceso lo spegneremo, se il led è
spento lo accenderemo.
goto SetToZero
Questa istruzione è un salto incondizionato
(dall'inglese GO TO, vai a)alla label SetToZero dove troveremo le
istruzioni per spegnere il led. Questa istruzione verrà saltata dall'istruzione
successiva se il led è gia spento.
bsf PORTB,LED
goto MainLoop
Queste due istruzioni semplicemente accendono
il led e rimandano il programma all'ingresso del ciclo di lampeggiamento.
SetToZero
bcf PORTB,LED
goto MainLoop
Queste due istruzioni semplicemente spengono
il led e rimandano il programma all'ingresso del ciclo di lampeggiamento.
La subroutine Delay
Come descritto in precedenza questa subroutine inserisce
un ritardo di circa un secondo e può essere chiamata più volte nel source tramite
l'istruzione call Delay.
Vediamo come funziona:
Delay
clrf Count
clrf Count+1
DelayLoop
decfsz Count,1
goto DelayLoop
decfsz Count+1,1
goto DelayLoop
retlw 0
END
Delay e DelayLoop sono due label. Delay
identifica l'indirizzo di inizio della subroutine e viene utilizzato per le chiamate dal
corpo principale del programma. DelayLoop viene chiamato internamente
dalla subrountine e serve come punto di ingresso per il ciclo (dall'inglese loop) di
ritardo.
In pratica il ritardo viene ottenuto eseguendo migliaia di istruzioni
che non fanno nulla !
Questo tipo di ritardo si chiama ritardo software o ritardo a programma.
E' il tipo di ritardo più semplice da implementare e può essere utilizzato quando non è
richiesto che il PIC esegua altri compiti mentre esegue il ritardo.
Le istruzioni:
clrf Count
clrf Count+1
CLEAR FILE REGISTER azzerano le due
locazioni di ram riservate precedentemente con l'istruzione:
Count RES 2
Queste due locazioni sono adiacenti a partire dall'indirizzo
referenziato dalla label Count.
decfsz Count,1
L'istruzione significa DECREMENT FILE
REGISTER, SKIP IF ZERO ovvero decrementa il contenuto di
un registro (in questo caso Count e salta l'istruzione successiva se il valore raggiunto
è zero). Se il valore raggiunto non è zero viene eseguita l'istruzione successiva:
goto DelayLoop
Che rimanda rimanda l'esecuzione all'inizio del ciclo di
ritardo. Una volta raggiunto lo zero con il contatore Count vengono eseguite le
istruzioni:
decfsz Count+1,1
goto DelayLoop
Che decremetano il registro seguente fino a che anche
questo raggiunge lo zero. Il registro Count+1 in particolare verrà decrementato di uno
ogni 256 decrementi di Count.
Quando anche Count+1 avrà raggiunto lo zero
l'istruzione:
return
il cui significato è RETURN FROM
SUBROUTINE determinerà l'uscita dalla routine di ritardo ed il proseguimento
dell'esecuzione dall'istruzione successiva la call Delay.
Per finire END è una direttiva che
indica al compilatore la fine del source assembler.
Nel passo successivo compileremo il source LED_1.ASM e
programmeremo il PIC con il codice generato in uscita dal compilatore assembler. |