Wednesday, March 28, 2012

Linguaggio assembly - parte 1


Linguaggio assembly - parte I


Introduzione



Il PIC 16F84 ha un set di appena 35 istruzioni. Ogni istruzione e' una parola a 14 bit ed e' composta da un opcode che specifica l'istruzione stessa e da uno o due operandi (per alcune questi sono facoltativi o del tutto assenti) che specificano ulteriormente l'operazione da eseguire.
Ogni istruzione viene eseguita in un ciclo macchina (4 colpi di clock) eccetto per le istruzioni di salto (ma solo quando il salto e' effettuato) e per le istruzioni che modificano il program counter; in questi due casi l'istruzione richiede 2 cicli macchina (8 colpi di clock).
Le istruzioni sono variegate, ma possono essere raggruppate in tre categorie principali: le istruzioni di salto (condizionato o non), quelle aritmetiche/logiche e di movimentazione dei dati.


Concetti generali



Molte istruzioni sono composte dallo mnemonico (il nome dell'istruzione stessa) e da uno o due parametri (a volte nessuno). I parametri possono essere di quattro tipi: parametro F, parametro D, B e K.
  • F - Questo parametro rappresenta la locazione di memoria di un registro (SFR o GPR) della memoria dati.
  • D - E' un singolo bit ed indica se il risultato dell'operazione andra' nel registro indicato da F (D=1) oppure nel registro di lavoro W (D=0). Se D viene omesso si ricade sempre nel caso D=1.
  • B - E' un parametro a 3 bit (valori tra 0 e 7 inclusi) ed indica la posizione del bit prescelto all'interno di un registro puntato da F. Ad esempio, se B=2 indica il terzo bit meno significativo del registro indicato da F (il primo e' il bit numero zero, il secondo il numero uno ed il terzo il numero due). Alcune istruzioni necessitano di tale parametro per sapere quale bit deve essere controllato/modificato all'interno di un dato registro.
  • K - Rappresenta un valore letterale, un byte, cioe' un valore tra 0 e 255. Alcune istruzioni hanno bisogno di un letterale come parametro per effettuare operazioni aritmetiche o logiche.
Queste lettere vengono usate nelle descrizioni sintattiche delle varie istruzioni assembly.
Eventuali commenti al codice sono preceduti dal punto e virgola e vengono inseriti a destra della istruzione commentata.
Come si vedra' anche in seguito, in cima al listato del programma assembly e' possibile definire delle etichette da associare alle locazioni RAM del PIC. In questo modo ci si puo' riferire piu' facilmente a tali locazioni specificandone il nome scelto anziche' l'indirizzo in esadecimale. Per ora e' sufficiente sapere che scrivendo la seguente riga in cima al listato

ILMIOREGISTRO EQU 0x25

e' possibile riferire la locazione 0x25 tramite il nome "ILMIOREGISTRO", esattamente come accade per le variabili nei linguaggi di programmazione di alto livello.



Operazioni di base



Set e clear dei bit (BSF e BCF)

Sono le due istruzioni BSF e BCF.
Vengono utilizzate per impostare a 0 o 1 i singoli bit dei registri.
BSF setta a uno il bit in posizione B del registro F, mentre BCF setta a zero il bit in posizione B del registro F.
Sintassi delle istruzioni:

BSF F B

BCF F B
Esempio:Settare ad uno il sesto bit meno significativo del registro alla locazione 0x13

BSF 0x13,5
NOTA: Il sesto bit e' il numero cinque, perche' il primo bit e' il numero zero!

Esempio:
Portare a zero il primo e l'ultimo bit del registro FSR

BCF 0x04,0
BCF 0x04,7
oppure, utilizzando le etichette:

FSR EQU 0x04
BCF FSR,0
BCF FSR,7
Esempio:
Portare a zero il contenuto del registro in posizione 0x19

BCF 0x19,0
BCF 0x19,1BCF 0x19,2BCF 0x19,3
BCF 0x19,4BCF 0x19,5BCF 0x19,6BCF 0x19,7

NOTA: ovviamente l'azzeramento di un registro si puo' fare in modi piu' efficienti e veloci.




Selezione del banco RAM (banco 0, banco 1)

Per ora abbiamo toccato solo registri che non richiedevano la specifica di uno dei due banchi (il registro FSR e i registri GPR sono mappati in entrambi i banchi). Se pero', ad esempio, occorre modificare il registro TRIS_A e' necessario specificare il banco 1.
Il banco RAM desiderato (0 o 1) viene selezionato impostando il sesto bit del registro STATUS. Se il bit vale zero e' abilitato il banco 0, se il bit vale uno e' abilitato il banco 1.
All'accensione o al reset del PIC il banco 0 e' quello di default.


Esempio:selezionare il banco 1 della RAM

BSF 0x03,5

Esempio:selezionare il banco 0 della RAM

BCF 0x03,5
Esempio:settare ad uno il terzo bit del registro TRIS_A supponendo che attualmente sia selezionato il banco 0.
STATUS EQU 0x03
TRIS_A EQU 0x05

BSF STATUS,5
BSF TRIS_A,3

NOTA:
 Chiaramente l'accesso al registro STATUS (0x03) non richiede la selezione preventiva del banco in quanto e' mappato in entrambi.

  


Spostare i dati (MOVF, MOVWF e MOVLW)

Sono le tre istruzioni MOVLW, MOVF e MOVWF. Grazie a queste tre istruzioni e' possibile copiare un byte da un registro ram (GPR o SFR) al registro W e viceversa, oppure caricare W con un valore letterale compreso tra 0 e 255.
MOVLW si usa tutte le volte in cui occorre inizializzare il registro W con un dato valore.
Sintassi delle istruzioni:

MOVLW K
MOVLW inizializza il registro W con il valore K. K e' un valore compreso tra 0..255 inclusi (0x00..0xFF in esadecimale).
MOVWF F
MOVLW 
muove il contenuto del registro W alla locazione di memoria F. Il registro W non viene modificato.
MOVF F,D
MOVLW 
muove il contenuto del registro F nel registro W se D=0. Se invece D=1 il contenuto di F viene "copiato" su se stesso senza toccare W. L'istruzione modifica automaticamente anche un bit del registroSTATUS, piu' precisamente il terzo bit, detto bit Z o ZERO_FLAG. Tale bit viene settato ad uno se e solo se il registro F (al momento dell'esecuzione dell'istruzione MOVF) vale zero, altrimenti viene impostato a zero. Ecco quindi che ha un senso anche copiare un registro su se stesso (con l'opzione D=1) perche' in questo caso l'unico effetto dell'istruzione e' la modifica opportuna del bit ZERO_FLAG, bit che viene controllato dal PIC per saltare o meno durante i salti condizionati.

Esempio:Caricare il registro W con il valore letterale 0x34 (52 in decimale)

MOVLW 0x34
Esempio:Muovere il contenuto del registro W alla locazione di memoria 0x41 (in questo caso un registro GPR)

MOVWF 0x41
Esempio:
Muovere il contenuto del registro in posizione 0x32 nel registro W

MOVF 0x32,0

Esempio:
Copiare il contenuto della locazione di memoria 0x0F nella locazione 0x10.

MOVF  0x0F,0
MOVWF 0x10
NOTA: In questi casi, siccome le locazioni 0x34,0x32,0x0F e 0x10 sono relative a registri GPR non e' mai necessario specificare il banco RAM (0 o 1).

Esempio:
Copiare il contenuto del registro TRIS_A nel registro W

BSF  0x03,5 
         ;selezione del banco RAM 1
MOVF 0x05,0
NOTA: In questo caso e' necessario prima specificare il banco RAM per accedere a TRIS_A

Esempio:
Copiare il contenuto del registro OPTION_REG in PORT_A

BSF   0x03,5         
;selezione del banco RAM 1
MOVF  0x01,0
BCF   0x03,5         
;selezione del banco RAM 0
MOVWF 0x05



Azzerare i registri (CLRF e CLRW)

Vengono utilizzate per azzerare i contenuti dei registri. 
Sintassi delle istruzioni:

CLRF  F
Azzera il contenuto del registro puntato da F. Viene automaticamente settata a 1 la ZERO_FLAG del registro di stato.
CLRW
Azzera il contentuo del registro di lavoro W. Viene automaticamente settata a 1 la ZERO_FLAG del registro di stato.

Esempio:Azzerare il contenuto di PORT_A

CLRF 0x05
Esempio:Azzerare il contenuto di TRIS_B
BSF  0x03,5         ;selezione del banco RAM 1
CLRF 0x05
Esempio:Azzerare il contenuto del registro W

CLRW



No operation (NOP)

Questa istruzione non produce alcun risultato. La sua "esecuzione" comporta solamente l'incremento del program counter alla prossima istruzione. L'istruzione di NOP viene utilizzata per effettuare dei ritardi nell'esecuzione dei programmi. Supponendo che l'oscillatore esterno sia di 4MHz, tutte le istruzioni non di salto (come NOP) verranno eseguite in 1us: se quindi e' necessario ritardare di 10us l'esecuzione in un punto di un programma, sara' sufficiente inserire in quel punto 10 NOP consecutivi. Ovviamente la risoluzione del tempo di ritardo sara' pari ad 1us. Ritardi piu' grandi possono essere effettuati tramite NOP all'interno di cicli come si vedra' piu' avanti.
L'istruzione NOP si rivela utilissima nei casi in cui si deve generare un segnale con tempistiche ben precise, ad esempio una comunicazione seriale RS232, oppure frequenze sonore.
Sintassi della istruzione:

NOP

Esempio:Effettuare un ritardo di 3us supponendo un oscillatore da 8MHz.
NOP
NOP
NOP
NOP
NOP
NOP
NOTA: Con un oscillatore da 8MHz ogni istruzione viene eseguta in 0,5us. Quindi per un ritardo di 3us occorrono 6 NOP.




Salto incondizionato (GOTO)

Questa istruzione permette di saltare incondizionatamente alla linea di programma desiderata, qualunque essa sia. L'istruzione viene eseguita in due cicli macchina (8 colpi di clock).
Sintassi della istruzione:

GOTO addr
Salta ad eseguire l'istruzione posizionata all'indirizzo "addr". "addr" e' l'indirizzo di memoria di programma (memoria FLASH) alla quale si vuole saltare; percio' "addr" deve essere un valore compreso tra 0 e 1023.
Spesso e' scomodo scrivere direttamente la locazione di memoria in forma numerica, percio' molti ambienti di sviluppo, come MPLAB, permettono di specificare delle label (da NON confondere con le etichette della direttiva EQU vista in precedenza!). La label e' semplicemente una stringa di testo arbitraria preceduta da ":" e la si posiziona all'interno del codice, tra una istruzione ed un altra. Quando si vuole saltare all'istruzione immediatamente successiva alla label, la si richiama nel goto scrivendo al posto di "addr" il nome dato alla label. Gli esempi successivi chiariscono ulteriormente il concetto:
Esempio:Ripetere il caricamento del registro W con il valore 0x30 all'infinito.
:loop              ;label di nome "loop"
MOVLW 0x30         ;caricamento di W con il valore 0x30
GOTO  loop         ;salto alla istruzione dopo "loop"
Esempio:Saltare tre istruzioni

BSF   0x03,5        
MOVF  0x01,0

GOTO salto         ;salto
BCF   0x03,5
CLRW
MOVLW 0x10

:salto
BCF   0x03,5       ;prima istruzione dopo il salto     
MOVWF 0x05





Salti condizionati (BTFSC, BTFSS, DECFSZ e INCFSZ)

Le quattro istruzioni di salto del microcontrollore sono BTFSC,BTFSS, DECFSZ e INCFSZ. Anche se i nomi di queste istruzioni possono apparire complicati, il loro funzionamento e' estremamente semplice.
Quello che differenzia tra loro le istruzioni e' la politica di salto ed alcuni altri effetti collaterali specifici, ma in linea di massima si comportano in questo modo: se la condizione di salto e' FALSA si esegue l'istruzione successiva, altrimenti, se la condizione di salto e' VERA, si esegue quella immediatamente dopo a quella successiva. La figura spiega meglio il concetto:
  

In questo caso e' stata scelta BTFSC, ma poteva tranquillamente essere un'altra delle quattro. Se la condizione di salto (specifica per ognuna delle istruzioni) e' FALSA, allora si prosegue normalmente con l'esecuzione del programma, cioe' eseguendo l'istruzione 1, poi l'istruzione 2 e cosi' via come mostrato nella figura. Se la condizione e' VERA allora NON si esegue l'istruzione 1 ma si continua con l'esecuzione del programma dall'istruzione 2 in poi. Molto spesso l'istruzione 1 e' un salto incondizionato (GOTO); in questo modo e' possibile redirigere l'esecuzione del programma ad un altro punto.
Le istruzioni di salto richiedono 2 cicli macchina quando la condizione e' VERA ed 1 solo ciclo macchina quando questa e' FALSA.

Sintassi delle istruzioni:


BTFSC F,B
Se il B-esimo bit del registro F e' settato a uno (condizione FALSA) viene eseguita l'istruzione immediatamente successiva; se vale zero (condizione VERA) l'istruzione immediatamente successiva viene saltata e si passa a quella dopo.
BTFSS F,B
Se il B-esimo bit del registro F e' settato a zero (condizione FALSA) viene eseguita l'istruzione immediatamente successiva; se vale uno (condizione VERA) l'istruzione immediatamente successiva viene saltata e si passa a quella dopo. Questa istruzione di salto e' simile alla precedente ma il controllo sul bit B-esimo e' invertito.
DECFSZ F,D
Viene decrementato di una unita' il contenuto del registro F. Il risultato viene posto in W se D=0 (lasciando inalterato F), viene posto in F se D=1. Se inoltre tale risultato e' zero si salta l'istruzione successiva (condizione VERA), altrimenti (condizione FALSA) si prosegue normalmente.
NOTA: Il decremento e' effettuato PRIMA del controllo della condizione!
INCFSZ F,D
Viene incrementato di una unita' il contenuto del registro F. Il risultato viene posto in W se D=0 (lasciando inalterato F), viene posto in F se D=1. Se inoltre tale risultato e' zero (condzione VERA) si salta l'istruzione successiva, altrimenti (condizione FALSA) si prosegue normalmente.NOTA: L'incremento e' effettuato PRIMA del controllo!

Esempio:Se il primo bit del registro 0x20 e' 1 caricare W con il valore 0x00, altrimenti caricare W con il valore 0xFF.
REG EQU 0x20

BTFSC REG,0          ;controlla se il primo bit del registro 0x20 e' 1
GOTO  yes             ;se vale 1 vai a "yes"
MOVLW 0xFF            ;se vale 0 carica W con 0xFF e vai a "end"
GOTO  end
:yes      
MOVLW 0x00            ;setta W con 0x00
:end
Esempio:Scrivere nel registro 0x30 tutti i valori in successione da 0 a 255. Infine azzerare W.

MOVLW  0x00            ;carico W con 0x00
MOVWF  0x2F            ;scrivo W nel registro di appoggio 0x2F
:loop
MOVLW  0x2F,0          ;carico W con il contenuto di 0x2F
MOVWF  0x30            ;scrivo il contenuto di W nel registro 0x30

INCFSZ 0x2F,1          ;incremento il registro 0x2F e controllo se vale zero
GOTO loop              ;non e' zero, vai a "loop"

CLRW                   ;si e' zero, resetta W. Programma terminato.



Cicli di delay

Abbiamo gia' visto come effettuare un ritardo tramite l'uso ripetuto dell'istruzione NOP. La tecnica non permette di generare grandi ritardi perche' occorrebbe inserire un gran numero di NOP in sequenza, occupando tutta la memoria di programma.
Conviene invece scrivere un ciclo che itera per un determinato numero di volte, decrementando un registro GPR adibito a contatore; l'istruzione di decremento e' incorporata in quella di salto, quindi il sistema e' estremamente semplice da implementare. Piu' difficile e' stabilire il valore iniziale del contatore per ottere uno specifico tempo di ritardo: occorre infatti calcolare accuratamente tutte le durate di esecuzione delle istruzioni.
Un tipico ciclo di delay e' quello riportato qui sotto:
Ciclo di delay:Considerando un oscillatore a 4MHz si crea un ritardo di 500us (mezzo millisecondo)
COUNT EQU 0x30

:delay
MOVLW  0xA7               ;carico W con il valore 167
MOVWF  COUNT              ;lo memorizzo nel registro COUNT
:loop
DECFSZ COUNT,1            ;decremento di uno, se non e' zero vai a "loop"
goto loop
Il loop principale prevede una istruzione DECFSZ ed un GOTO. DECFSZ richiede 1us quando NON salta (cioe' nel caso in cui il registro non sia zero) e 2us quando salta (e quindi termina la routine di delay).
Considerando N la nostra incognita (il valore di inzializzazione per count), DECFSZ richiede un tempo pari a:

tempo_DEFSZ = (N-1)+2 us

(N-1) volte richiede 1us (quando non salta) e all'ultimo giro richiede 2us (salta).
L'istruzione GOTO viene eseguita N-1 volte e quindi richiede 2(N-1) us.
In definitiva si ha:

tempo_tot = (N-1)+2+2(N-1) us

semplificando:

tempo_tot = 3(N-1)+2 us

semplificando ancora:

tempo_tot = 3N-1 us

risolvendo rispetto a N si ottiene:

N = (tempo_tot+1)/3

se "tempo_tot" e' appunto 500 come da richiesta, si ottiene N = 167 (0xA7 in esadecimale)

Per avere ritardi maggiori occorre annidare piu' cicli uno dentro l'altro utilizzando quindi tanti registri contatori quanti sono i cicli annidati, ma il calcolo dei valori di inizializzazione si complica ancora di piu' ed e' necessario risolvere sistemi non lineari molto piu' complessi.
Inoltre, spesso e volentieri le soluzioni prevedono valori non interi per cui o si sceglie di approssimare oppure si inseriscono NOP finali esterni ai cicli per compensare gli errori di approssimazione.
Per maggiori informazioni rimando a questo tutorial

Registri SFR


Alcuni registri SFR

I registri SFR sono in tutto 22. In questa sezione vengono trattati solo quelli che non sono specifici di una determinata funzione, cioe' i seguenti:
  • STATUS
  • OPTION_REG
  • PCL
  • PCLATH
  • INTCON

Essendo utilizzati in maniera trasversale non possono essere inseriti in una particolare sezione del tutorial.
Tutti gli altri sono trattati nelle rispettive sezioni di appartenenza. Ad esempio, i registri PORT_A,PORT_B,TRIS_A e TRIS_B vengono trattati alla sezione sull'utilizzo delle porte di I/O perche' questa e' la loro area di competenza.


    Registro STATUS



Locazione di memoria: 0x03
Banco RAM: mappato in entrambi

Descrizione dei bit:
bit CBit di Carry (riporto). Indica se una istruzione aritmetica ha prodotto un overflow oltre il bit piu' significativo (l'ottavo bit).
1 = overflow
0 = nessun overflow 
bit DCSimile al bit C, indica se c'e' stato un overflow oltre il quarto bit.
1 = overflow oltre il quarto bit
0 = overflow oltre il quarto bit
bit ZAlcune istruzioni modificano automaticamente questo bit quando il risultato delle stesse e' zero:
0 - il risultato non e' zero
1 - il risultato e' stato zero
bit #PDInsieme a #TO specifica che cosa ha causato il reset precedente
bit #TOInsieme a #PD specifica che cosa ha causato il reset precedente
bit RP0Usato per la selezione del banco RAM:
0 = banco 0
1 = banco 1
bit RP1Non implementato
bit IRPNon implementato

Valore di default al power-on-reset: 00011xxx
Valore di default per gli altri reset (#MCLR e Watch dog timer reset): 000qquuu

Legenda:
x = aleatorio
q = dipendente da altri fattori
u = rimane come era prima
- = non implementato



    Registro OPTION_REG



Locazione di memoria: 0x01
Banco RAM: banco 1

Descrizione dei bit:
bit PS0Bit di scelta del rapporto di divisione del prescaler
bit PS1Bit di scelta del rapporto di divisione del prescaler
bit PS2Bit di scelta del rapporto di divisione del prescaler
bit PSABit di assegnazione del prescaler
1 - al Watch-Dog
0 - al TIMER0
bit T0SEModalita' di incremento del TIMER0 quando preleva il segnale dal piedino RA4/T0CKl
1 - falling-edge
0 - rising-edge
bit T0CSSeleziona la modalita' di funzionamento del TIMER0
1 - modo contatore
0 - modo temporizzatore
bit INTDEGDecide la modalita' di interrupt per il piedino RB0/INT
1 - rising edge
0 - falling edge
bit #RBPUAbilita le resistenze interne di pull-up delle linee della porta B (per quelle linee che sono settate in modalita' output)
0 - resistenze abilitate
1 - resistenze disabilitate

Valore di default al power-on-reset: 11111111
Valore di default per gli altri reset (#MCLR e Watch dog timer reset): 11111111
Legenda:
x = aleatorio
q = dipendente da altri fattori
u = rimane come era prima
- = non implementato



    Registro PCL



Locazione di memoria: 0x02
Banco RAM: mappato in entrambi

Descrizione dei bit:
Il registro mappa gli 8 bit meno significativi del program counter. E' possibile leggere e scrivere tale registro. 
Valore di default al power-on-reset: 00000000
Valore di default per gli altri reset (#MCLR e Watch dog timer reset): 00000000
Legenda:
x = aleatorio
q = dipendente da altri fattori
u = rimane come era prima
- = non implementato



    Registro PCLATH



Locazione di memoria: 0x0a
Banco RAM: mappato in entrambi

Descrizione dei bit:
I due bit meno significativi sono messi in corrispondenza dei due bit piu' significativi del program counter. Gli altri bit di PCLATH non sono implementati. E' possibile sia leggere che scrivere. I due bit meno significativi di PCLATH vengono scritti nei due piu' significativi del program counter solo quando una qualche istruzione modifica il registro PCL.

Valore di default al power-on-reset: ---00000
Valore di default per gli altri reset (#MCLR e Watch dog timer reset): ---00000

Legenda:
x = aleatorio
q = dipendente da altri fattori
u = rimane come era prima
- = non implementato



    Registro INTCON



Locazione di memoria: 0x0B
Banco RAM: mappato in entrambi


Descrizione dei bit:

bit RBIFRilevatore di interrupt dai piedini RB4..RB7
1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt
bit INTFRilevatore di interrupt dal piedino INT
1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt
bit T0IFRilevatore di interrupt dal timer TMR0
1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt
bit RBIEBit di abilitazione dell'interrupt dai piedini RB4..RB7
1 - abilitato
0 - disabilitato
bit INTEBit di abilitazione dell'interrupt dal piedino INT
1 - abilitato
0 - disabilitato
bit T0IEBit di abilitazione dell'interrupt dal timer TMR0
1 - abilitato
0 - disabilitato
bit EEIEBit di abilitazione dell'interrupt per completamento della scrittura nella eeprom
1 - abilitato
0 - disabilitato
bit GIEInterruttore generale degli interrupt
0 - Tutti gli interrupt disabilitati
1 - Interrupt potenzialmente attivi (dipende poi dallo stato dei bit di attivazione specifici di ogni interrupt)

Valore di default al power-on-reset: 0000000x
Valore di default per gli altri reset (#MCLR e Watch dog timer reset): 0000000u

Memoria RAM/FLASH/EEPROM



    Memoria RAM/FLASH/EEPROM






La memoria dati (memoria RAM)



La memoria RAM del PIC 16F84 ha una dimensione di 90 byte, 22 byte per i SFR (Special Function Register) e i restanti 68 byte per i 68 GPR (General Purpose Register). Ogni registro occupa sempre ed esattamente un byte.
Gli SFR sono registri che controllano il funzionamento e le periferiche del microcontrollore (ad esempio, e' qui che sono mappate le porte A e B): tali registri non possono quindi essere utilizzati come memoria RAM per i programmi utente ma semplicemente fungono da pannello di controllo per l'intero microcontrollore.
I GPR sono invece 68 registri liberi a disposizione dell'utente.
Le aree di memoria colorate in azzurro non sono implementate; qualsiasi lettura in queste locazioni ritorna sempre con valore zero, mentre le scritture non hanno effetto.




La memoria RAM e' suddivisa in due banchi distinti (banco 0 e banco 1). Per poter accedere ad un registro e' quindi necessario specificare sia l'indirizzo di memoria che il banco. Ad esempio, all'indirizzo 01h e banco 0 abbiamo TMR0, mentre all'indirizzo 06h e banco 1 abbiamo TRIS B.
Alcuni registri sono "copiati" in entrambi i banchi; cio' significa che per accede a quel registro e' sufficiente specificarne l'indirizzo di memoria, non importa quale banco sia abilitato in quel momento. Ad esempio, per accedere al registro PCL possiamo portarci all'indirizzo 02h e banco 0, ma anche all'indirizzo 02h e banco 1. I registri di questo tipo sono: Indirect addr., PCL, STATUS, FSR, PCLATH, INTCON e tutti e 68 i registri GPR.




I registri SFR


Come appare poco evidente dalla composizione della memoria RAM, i registri SFR sono solo 16 (sembrerebbero 22 scartando le 2 sole aree in azzurro, ma ovviamente quelli mappati in ambedue i banchi contano sempre per uno solo, quindi sono effettivamente 16!). Eccoli elencati e suddivisi in gruppi:
Registri SFR di controllo del PIC

  • INDF
  • TMR0
  • OPTION_REG
  • PCL
  • STATUS
  • FSR
  • PCLATH
  • INTCON
Registri SFR per le porte di I/O
  • PORT_A
  • TRIS_A
  • PORT_B
  • TRIS_B
Registri SFR per l'utilizzo della memoria EEPROM interna
  • EEDATA
  • EECON1
  • EEADR
  • EECON2
Tutti i registri verranno analizzati nel corso del tutorial e nelle rispettive sezioni. Ad esempio, i registri per l'utilizzo delle memoria EEPROM sono approfonditi nella sezione "Utilizzo della memoria EEPROM". I registri piu' trasversali, cioe' che riguardano e toccano piu' argomenti diversi, vengono approfonditi all'occorrenza.



I registri GPR



I registri GPR, come detto in precedenza, sono tutti quelli che vanno dall'indirizzo di memoria 0Ch a 4Fh inclusi. Sono 68 ed ognuno di essi e' mappato in entrambi i banchi RAM.





La memoria di programma (memoria FLASH)



I programmi inseriti nel PIC vengono registrati in questa memoria. La memoria contiene esattamente 1024 locazioni (da 0000h fino a 03FFh) ed ogni locazione puo' contenere una sola istruzione (le istruzioni del PIC 16F84 sono a 14 bit, quindi ogni locazione memorizza esattamente 14bit). Per questo motivo un programma deve necessariamente essere costituito da un massimo di 1024 istruzioni.
La memoria istruzioni non e' accessibile da programma, cioe' non e' possibile indirizzarne lo spazio di memoria come per la memoria RAM; a livello utente (livello di programmazione) e' possibile dimenticarsi completamente dell'esistenza di questa memoria.




Una volta acceso (o resettato), il microcontrollore inizia ad eseguire il programma a partire dalla locazione 0000h, detta anche "vettore di reset", per poi proseguire con le istruzioni successive.
Esiste pero' una locazione speciale, cioe' l'indirizzo 0004h, che assolve ad un compito speciale; ogni qualvolta si verifica un interrupt (secondo le modalita' descritte che si vedranno in seguito) il PIC salta immediatamente a questa locazione.
In questo modo, se si desidera creare una routine di gestione delle interruzioni occorre farla cominciare da questa precisa locazione (0004h) in modo tale che essa verra' eseguita non appena si verifichera' un interrupt. Ovviamente prima della locazione 0004h occorrera' inserire un salto incondizionato per poter "oltrepassare" la routine di gestione delle interruzioni: di solito il salto e' inserito proprio in prima posizione, cioe' in 0000h, cosiche' il microcontrollore, una volta accesso o resettato, passera' immediatamente ad eseguire il salto e quindi si posizionera' alla locazione opportuna scelta dal programmatore.
In figura si mostra graficamente il concetto di salto al vettore di interrupt:


Senza approfondire troppo, l'ultima istruzione della routine di gestione delle interruzioni dovra' essere un RETFIE per poter ritornare ad eseguire il programma da dove era stato interrotto, ma tutto questo verra' approfondito alla sezione relativa agli interrupt.
Ovviamente se non e' prevista alcuna gestione di interrupt e quindi non c'e' alcuna routine di servizio delle interruzioni tutto il discorso puo' essere anche trascurato.


Il program counter


Il microcontrollore tiene traccia dello stato di avanzamento del programma grazie ad uno specifico puntatore, il program counter. Nel caso del 16F84, il program counter e' un puntatore a 13 bit che indica l'istruzione corrente da eseguire (solo i primi 10 bit dell program counter vengono utilizzati, per un totale di 2^10 = 1024 locazioni indirizzabili). Al reset del pic il program counter vale 0 (prima istruzione in cima alla memoria di programma), successivamente, per ogni istruzione eseguita, esso viene incrementato di uno. Le istruzioni di salto (condizionato o no) modificano esplicitamente il program counter quando sono eseguite, imponendo un salto opportuno nel normale flusso di esecuzione del programma.
Gli 8 bit meno significativi del program counter sono accessibili in lettura e scrittura dal registro PCL, quindi da tale registro e' possibile sia modificare gli 8 bit meno significativi del program counter che leggerne lo stato. Il registro PCL viene costantemente aggiornato e rispecchia sempre lo stato dei 8 bit meno significativi del program counter. Per i restanti 2 bit del program counter il discorso e' differente: tali bit sono nascosti e non sono visibili all'utente, ma possono essere modificati andando a cambiare i due bit meno significativi del registro PCLATH. PCLATH non viene mai aggiornato e quindi il suo stato non corrisponde a quello dei 2 bit piu' significativi del program counter.
La figura mostra come sono relazionati i registri PCL e PCLATH con il program counter:



In definitiva, durante il funzionamento normale del microcontrollore (esecuzione delle istruzioni, salti condizionati, salti incondizionati, chiamate a funzione etc..) il program counter viene aggiornato correttamente e non occorre preoccuparsi del suo stato. Occorre invece fare attenzione solo nel caso in cui una istruzione di programma modifichi esplicitamente il registro PCL: ogni volta che una qualche istruzione effettua una scrittura nel registro PCL, il contenuto di tale registro viene posto nei 8 bit meno significativi del program counter come detto in precedenza, ma anche i due bit meno significativi del registro PCLATH vengono copiati nei due bit piu' significativi del program counter (bit 8 e 9).
Cio' significa che se una istruzione tenta di incrementare forzatamente il registro PCL di 10 unita' (al fine di effettuare un salto in avanti nel programma di 10 istruzioni senza usare apposite istruzioni di salto) puo' farlo, ma occorre ricordarsi che i due bit meno significativi del registro PCLATH vengono automaticamente copiati nei due bit piu' significativi del program counter. Quindi, ogni volta che ci si appresta a scrivere in PCL, occorre far in modo che lo stato dei bit 0 e 1 di PCLATH corrisponda a quello dei bit 8 e 9 del program counter, pena un salto ad una locazione non desiderata. 





La EEPROM



La EEPROM interna e' una memoria secondaria ad accesso relativamente lento (20ms per un cliclo di lettura o scrittura) di 64 bytes. La EEPROM puo' essere scritta e letta da programma attraverso gli appositi registri SFR ed il suo contenuto permane anche a microcontrollore spento; in effetti la durata massima di ritenzione dei dati puo' arrivare fino a 40 anni.
La EEPROM verra' approfondita alla sezione "EEPROM interna".