Sei sulla pagina 1di 70

Guida pratica al PIC 16F84

Indice generale

Introduzione alla guida

Il PIC 16F84

o Introduzione
o Caratteristiche
o Piedinatura
o Il piedino #MCLR
o Oscillatore esterno

XT

RC

LP e HS

o Struttura interna del PIC

Memoria RAM/FLASH/EEPROM

o La memoria dati (memoria RAM)

I registri SFR

I registri GPR

o La memoria istruzioni (memoria


FLASH)

Il program counter

o La EEPROM

Alcuni registri SFR

o STATUS
o OPTION_REG
o PCL
o PCLATH
o INTCON

Linguaggio assembly - parte I

o Introduzione
o Concetti generali
o Operazioni di base

Set e clear dei bit (BSF e


BCF)

Selezione del banco RAM

Spostare i dati (MOVF,


MOVWF e MOVLW)

Azzerare i registri (CLRF e


CLRW)

No operation (NOP)

Salto
(GOTO)

Salti condizionati (BTFSC,


BTFSS, DECFSZ e INCFSZ)

Cicli di delay

incondizionato

Utilizzare le porte di I/O

o Le porte di I/O
o Caratteristiche elettriche

Linee porta A

Linee porta B

o Lettura e scrittura
o Esempi software
o Esempi di collegamento

Utilizzare
ouput

i piedini come

Utilizzare
input

i piedini come

Linguaggio assembly - parte II

o Operazioni logiche

AND logico
ANDLW)

(ANDWF

OR
logico
IORLW)

(IORWF

XOR logico
XORLW)

(XORWF

NOT logico (COMF)

o Operazioni aritmetiche

Addizione
ADDLW)

(ADDWF

Sottrazione
SUBLW)

Decremento (DECF)

Incremento (INCF)

(SUBWF

o Operazioni di modifica dei registri

Rotazione (RRF e RLF)

Nibble-swap (SWAPF)

o Chiamata a funzione

Chiamata
(CALL)

funzione

Ritorno semplice (RETURN)

Ritorno
con
(RETLW)

parametro

Programmare il PIC

o Fasi di sviluppo
o Costruzione del programmatore

Funzionamento e schema
elettrico

Configurazione di ICPROG

o L'ambiente MPLAB

Creazione del progetto e


del programma

Compilazione

o Debug del codice


o Programmazione del pic

Esempio pratico: led lampeggiante

o Il programma
o Circuito di prova

Il timer interno

Sistema di interrupt

o Interrupt dal piedino RB0/INT


o Interrupt da RB4..RB7
o Interrupt per completamento della
fase di scrittura della EEPROM

o Interrupt dal timer TIMER0

La memoria EEPROM

o Lettura
o Scrittura

Modalita' sleep

Indirizzamento indiretto

Il Watch-Dog

Il PIC 16F84
Introduzione

Il 16F84 un microcontrollore RISC a 8 bit della Microchip


estremamente versatile e semplice da utilizzare. E' possibile
controllare led, rele', motori e attuatori in genere oppure
controllare lo stato di sensori esterni. E' molto utilizzato
anche come manipolatore e generatore/ricevitore di segnali
(radiocomandi, lettore di schede magnetiche e/o simcard
etc...). Prima di poter utilizzare il PIC occorre programmarlo;
cio' richiede l'inserimento del programma compilato anella
memoria interna. Il programma invece viene realizzato e
compilato con l'ausilio di un normale computer casalingo.
Una volta programmato ed acceso, il PIC inizia ad eseguire il
programma e, se previsto, a comunicare opportunamente
con il mondo esterno tramite i suoi 13 piedini di
ingresso/uscita. Il microcontrollore anche molto economico
(un singolo pezzo pu venire a costare intorno ai 7 euro,
mentre se presi in stock si arriva anche a 50 centesimi l'uno)
il che lo rende adatto anche per l'elettronica amatoriale.
In foto si mostra un PIC 16F84 nel classico package DIL a 18
piedini.

Caratteristiche

Architettura Harvard: il PIC possiede


separate per i dati e le istruzioni.

Memoria FLASH per le istruzioni da 1 K (1024


istruzioni) programmabile con l'ausilio di un computer
esterno. Questa memoria supporta fino ad un
massimo di 1000 cicli di cancellazione/scrittura.

Memoria RAM dei dati da 90 bytes, dei quali 22


utilizzati per i registri interni di sistema (SFR =
Special Function Registers) e i restanti 68 come
registri utente liberi (GPR = General Purpose
Register)

Frequenza di clock massima: 10MHz (20MHz per la


versione 16F84A)

Un set di appena 35 istruzioni. Grazie al sistema di


pipelining ogni istruzione viene eseguita in un singolo
ciclo macchina (4 colpi di clock) eccetto le istruzioni
di salto che richiedono 2 cicli macchina (8 colpi di
clock). Ad esempio, con un oscillatore esterno da
4MHz richiesto 1 microsecondo per l'esecuzione di
una istruzione non di salto e 2 microsecondi per
quelle di salto (condizionato o non).

Memoria EEPROM interna da 64 bytes. Questa


memoria pu essere programmata via software dal
PIC stesso e sopporta fino ad un massimo di 1
milione di cicli di cancellazione/scrittura. Tale
memoria non pu essere utilizzata come RAM
aggiuntiva per via della sua lentezza (20 ms in
scrittura) e l'accesso seriale (non e' mappata nello
spazio degli indirizzi direttamente raggiungibili dalla
CPU e viene letta/scritta solo tramite l'ausiliio di
alcuni dei registri speciali SFR)

Stack a 8 livelli (Lo stack implementato in una


memoria a parte non visibile da programma e quindi
non occupa lo spazio della RAM)

memorie

Timer interno a 8 bit

Watch-dog interno

2 porte di I/O (porta A a 5 bit e porta B a 8 bit) per


un totale di 13 piedini di ingresso/uscita. Ogni linea
pu essere programmata indipendentemente dalle
altre come linea di ingresso o linea di uscita. Possono
sembrare poche ma va sempre ricordato che il
microcontrollore alloggiato in un DIL a soli 18
piedini!

4 diverse possibilit di interrupt (esterno tramite


piedino apposito, del timer, esterno dovuto al cambio
di stato dei piedini di I/O, interno di completamento
della fase di scrittura della EEPROM)

Opzione di protezione del codice. Settando questa


opzione viene impedito ogni tentativo di lettura della
memoria FLASH d programma.

Tensione di alimentazione da 2 a 6 volt. Tensione di


programmazione da 12 a 14 volt.

Piedinatura

Come detto prima il PIC possiede 18 piedini. 13 di questi


sono adibiti all'I/O in logica TTL: questo significa che i valori
booleani sono rappresentati dalle tensioni 0V e +5V
rispettivamente per lo 0 e 1 logico. I rimanenti piedini
vengono impiegati per il reset, l'alimentazione e per
l'oscillatore esterno che fornisce il clock al PIC stesso. Come
si vede in figura alcuni piedini hanno una duplice funzione
(ad esempio il piedino numero 3 porta la dicitura RA4 e
T0CKl) anche se di default vale la prima (il piedino numero 3
e' di default RA4). Le funzioni secondarie verranno spiegate
piu' avanti.

Ecco l'elenco dei piedini e delle loro funzioni:

RA0, RA1, RA2, RA3, RA4 - Sono i 5 piedini della


porta A (tale porta e' a 5 bit). I piedini possono
essere configurati indipendentemente come linee di
ingresso o di uscita. Quando un piedino e' impostato
come linea di uscita, presentera' una tensione di 0V o
5V a seconda che il PIC abbia associato uno 0 o 1
logico al piedino stesso, tramite apposite istruzioni.
Quando e' impostato come ingresso, il PIC potra'
ricevere un valore booleano leggendo il livello di
tensione fornito al piedino in questione. Se il livello e'
maggiore di 2V verra' letto un 1 logico, se inferiore a
0.8V verra' letto lo 0 logico. Applicando una tensione
compresa tra 0.8 e 2V il risultato e' impredicibile.

RB0, RB1, RB2, RB3, RB4, RB5, RB6, RB7 - Sono gli
8 piedini della porta B. Valgono le considerazioni viste
per quelli della porta A.

GND - Riferimento di massa

VDD - Tensione di alimentazione.


compresa tra 2 e 6V. Di norma e' 5V.

OSC1 e OSC2 - A questi piedini deve essere collegato


l'oscillatore che fornisce il clock al sistema. Per alcuni
tipi di oscillatori sono necessari tutti e due i piedini,
per altri serve solo OSC1 e quindi OSC2 puo' essere
lasciato scollegato.

#MCLR - Piedino di reset, detto anche MasterCLeaR.


L'attivazione del piedino produce un reset del PIC,
cioe' il sistema riparte da capo con l'esecuzione del
programma interno, come quando viene appena
acceso. Questo piedino e' l'unico ad essere attivo
basso, cio' significa che solo portandolo a massa si
provoca il reset del PIC; durante il funzionamento
normale il piedino deve essere mantenuto a livello
logico alto tramite apposita resistenza limitatrice di
corrente.

Deve

essere

Il piedino #MCLR

Per funzionare correttamente il PIC ha bisogno di tenere alto


il livello logico del piedino di reset (#MCLR). Ogni volta che
si desidera resettare il PIC occorrera' invece portare a massa
tale
piedino.
Ecco un semplice circuito di pull-up standard da interfacciare
al piedino #MCLR del microcontrollore: solo due resistenze
ed un interruttore.

Durante il funzionamento normale, cioe' ad interruttore


aperto, il piedino #MCLR e' in stato logico alto (circa VDD
Volt). Premendo l'interruttore si porta momentaneamente a
massa il piedino, scatenando il reset del PIC. La resistenza
da 100 ohm limita la corrente nel piedino stesso quando il
pulsante
e'
premuto.
Come si vedra' anche negli esempi questo circuito e'
praticamente sempre presente.

Oscillatore esterno

L'oscillatore esterno fornisce il clock al microcontrollore,


permettendone cosi' il corretto funzionamento. Il PIC 16F84
accetta clock con frequenza fino a 10MHz (il modello 16F84A
fino a 20MHz). In ogni caso il PIC impiega 4 colpi di clock
per istruzione (8 nel caso di istruzioni di salto).
Esistono 4 tipi di oscillatori: XT,RC,LS e HS

XT

Utilizza un cristallo di quarzo da collegare ad entrambi i

piedini OSC1/CLKIN e OSC2. Le frequenze supportate


dipendono dal quarzo stesso nel range di valori da 100KHz e
20MHz. Questo tipo di oscillatore e' estremamente preciso
ed affidabile e quindi quello piu' utilizzato.

RC

E' un'oscillatore molto semplice, composto da una


restistenza ed un condensatore, da collegare al piedino
OSC1/CLKIN. Il piedino OSC2 puo' essere lasciato
scollegato, oppure puo' essere utilizzato per prelevare un
segnale ad onda quadra di frequenza pari alla quarta parte
di quella di clock, cioe' la frequenza di esecuzione delle
istruzioni (una istruzione richiede 4 colpi di clock). Il
consumo di corrente e' ridotto al minimo ma l'oscillatore e'
poco preciso e tende a variare la frequenza in funzione della
temperatura e tensione di alimentazione. Si raggiungono
basse frequenze (da 80Hz fino a 625KHz): ad esempio, con
una resistenza da 10K e un condensatore da 220pF si
raggiungono
gli
80KHz.
Per programmi che richiedono una esecuzione lenta conviene
installare un oscillatore XT e poi aggiungere istruzioni di
ritardo, piuttosto che abbassare la frequenza del PIC con un
oscillatore RC.

LP e HS

Utilizzano un risonatore ceramico rispettivamente per basse


ed alte frequenze di clock. Tutti e due i tipi sono abbastanza
precisi, il primo tende a consumare poco mentre il secondo
molto di piu', per via delle elevate frequenze raggiunte.

Durante la fase di programmazione del PIC andra' specificato


quale dei 4 tipi di oscillatore e' stato utilizzato. In questo
modo i piedini 15 e 16 del PIC (OSC1/CLKIN e
OSC2/CLKOUT) si configureranno in modo opportuno per
ricevere il segnale da quello specifico tipo di oscillatore.

Struttura interna

In figura viene riportata a grandi linee la struttura interna


del PIC. In alto a sinistra e' la memoria delle istruzioni da
1024 posizioni. In alto al centro la RAM principale, chiamata
anche RAM File Register da 68 byte (cioe' 68 registri). A
destra di questa e' la memoria EEPROM da 64 byte.
La memoria delle istruzioni viene indirizzata da uno specifico
puntatore detto Program Counter. Tale registro permette di
recuperare in sequenza le istruzioni del programma e di
portarle nell'Instruction register. Sebbene bastino 10 bit per
indirizzare la memoria istruzioni (2^10 = 1024) il Program
Counter e' a 13 bit perche' il modello 16F84 (come il
16F84A) fa parte di una famiglia di PIC piu' estesa che
contempla anche memorie di dimensioni maggiori: i
progettisti tendono a ridurre le differenze tra le varie
versioni dei PIC per minimizzare i costi di produzione.
L'istruzione viene poi inserita nell'Instruction register (di 14
bit) e poi decodificata ed eseguita. Il Program Counter viene
incrementato di uno (oppure di una altra quantita' se e'
previsto
un
salto).
Il microcontrollore possiede un solo registro esterno alla
memoria RAM, il registro W (Work). Questo registro entra in
gioco durante le operazioni booleane, aritmetiche o di
movimentazione dei dati. Ad esempio, per copiare un byte
da una posizione di memoria ad un altra occorrono due
istruzioni macchina: la prima legge la posizione di memoria
desiderata e ne trasferisce il contenuto (il byte) nel registro
W, la seconda trasferisce il contenuto di W nella posizione di
memoria
di
destinazione.
Le operazioni booleane o aritmetiche prevedono sempre e
solo due operandi, il primo e' W, il secondo e' un locazione di
memoria RAM (cioe' un registro del File Register); si puo'
scegliere se piazzare il risultato dell'operazione in W (e
quindi sovrascrivere il primo operando), o nella stessa
locazione di memoria (sovrascrivendo il secondo operando).
Le porte di I/O (porta A e porta B) sono mappate in RAM,
cio' significa che si puo' leggere e scrivere nelle porte
semplicemente leggendo e scrivendo nelle relative locazioni
in RAM. Ad esempio, una scrittura di un byte nella locazione
di memoria collegata alla porta B causa la configurazione
opportuna dei segnali di tensione nei piedini della porta B
(RB0,RB1,RB2 ... RB7), viceversa una lettura traduce in un
byte la configurazione attuale delle tensioni applicate sui
piedini di tale porta. Ovviamente un piedino di I/O non puo'
essere simultaneamente di input e di putput: occorre

decidere all'inizio la "direzione" dei piedini per tutte e 13 le


linee e cio' si risolve ancora una volta scrivendo in
determinate
posizioni
di
memoria
RAM.
Piu' avanti si chiariranno tutti questi meccanismi con esempi
in assembler.

Torna all'indice

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".

Torna all'indice

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
Banco RAM: mappato in entrambi

Descrizione dei bit:

memoria:

0x03

bit C

bit DC

Bit 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
Simile 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 Z

Alcune 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 #PD

Insieme a #TO specifica che cosa ha causato il


reset precedente

bit #TO

Insieme a #PD specifica che cosa ha causato il


reset precedente

bit RP0

Usato per la selezione del banco RAM:


0 = banco 0
1 = banco 1

bit RP1

Non implementato

bit IRP

Non 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
Banco RAM: banco 1

di

memoria:

Descrizione dei bit:


bit PS0

Bit di scelta del rapporto di divisione del

0x01

prescaler
bit PS1

Bit di scelta del rapporto di divisione del


prescaler

bit PS2

Bit di scelta del rapporto di divisione del


prescaler

bit PSA

Bit
di
assegnazione
1
al
0 - al TIMER0

del

prescaler
Watch-Dog

bit T0SE

Modalita' di incremento del TIMER0 quando


preleva il segnale dal piedino RA4/T0CKl
1
falling-edge
0 - rising-edge

bit T0CS

Seleziona la modalita' di funzionamento del


TIMER0
1
modo
contatore
0 - modo temporizzatore

Decide la modalita' di interrupt per il piedino


RB0/INT
bit INTDEG
1
rising
edge
0 - falling edge
Abilita le resistenze interne di pull-up delle linee
della porta B (per quelle linee che sono settate
bit #RBPU 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
Banco RAM: mappato in entrambi

memoria:

Descrizione dei bit:


Il registro mappa gli 8 bit meno significativi del program counter. E'

0x02

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
Banco RAM: mappato in entrambi

memoria:

0x0a

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
=
q
=
dipendente
u
=
rimane
- = non implementato

da
come

altri
era

aleatorio
fattori
prima

Registro INTCON
Locazione
di
Banco RAM: mappato in entrambi

memoria:

0x0B

Descrizione dei bit:

bit RBIF

Rilevatore di interrupt dai piedini RB4..RB7


1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt

bit INTF

Rilevatore di interrupt dal piedino INT


1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt

bit T0IF

Rilevatore di interrupt dal timer TMR0


1 - C'e' appena stato un interrupt
0 - Non c'e' stato nessun interrupt

bit RBIE

Bit di abilitazione dell'interrupt dai piedini


RB4..RB7
1 - abilitato
0 - disabilitato

bit INTE

Bit di abilitazione dell'interrupt dal piedino INT


1 - abilitato
0 - disabilitato

bit T0IE

Bit di abilitazione dell'interrupt dal timer TMR0


1 - abilitato
0 - disabilitato

bit EEIE

Bit di abilitazione dell'interrupt per


completamento della scrittura nella eeprom
1 - abilitato
0 - disabilitato

bit GIE

Interruttore 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

Torna all'indice

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

BSF
BCF

delle

F
F

istruzioni:

B
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
BCF
BCF
BCF
BCF
BCF
BCF
BCF

0x19,0
0x19,1
0x19,2
0x19,3
0x19,4
0x19,5
0x19,6
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

MOVLW inizializza il registro W con il valore K. K e' un valore compreso tra


0..255
inclusi
(0x00..0xFF
in
esadecimale).

MOVWF

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 registro STATUS,
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
MOVF 0x05,0

;selezione del banco RAM 1

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
MOVF 0x01,0
BCF 0x03,5
MOVWF 0x05

;selezione del banco RAM 1


;selezione del banco RAM 0

Azzerare i registri (CLRF e CLRW)

Vengono

utilizzate

per

azzerare

Sintassi

delle

CLRF

contenuti

dei

registri.
istruzioni:

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
CLRF 0x05

;selezione del banco RAM 1

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
BCF 0x03,5
CLRW
MOVLW 0x10
:salto
BCF 0x03,5
MOVWF 0x05

;salto

;prima istruzione dopo il salto

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

BTFSC

delle

istruzioni:

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
GOTO yes
MOVLW 0xFF
GOTO end
:yes
MOVLW 0x00
:end

;controlla se il primo bit del registro 0x20 e' 1


;se vale 1 vai a "yes"
;se vale 0 carica W con 0xFF e vai a "end"
;setta W con 0x00

Esempio:
Scrivere nel registro 0x30 tutti i valori in successione da 0 a 255. Infine
azzerare W.
MOVLW 0x00
MOVWF 0x2F
:loop
MOVLW 0x2F,0
MOVWF 0x30
INCFSZ 0x2F,1
GOTO loop
CLRW

;carico W con 0x00


;scrivo W nel registro di appoggio 0x2F
;carico W con il contenuto di 0x2F
;scrivo il contenuto di W nel registro 0x30
;incremento il registro 0x2F e controllo se vale zero
;non e' zero, vai a "loop"
;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
MOVWF COUNT
:loop
DECFSZ COUNT,1
goto loop

;carico W con il valore 167


;lo memorizzo nel registro COUNT
;decremento di uno, se non e' zero vai a "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
risolvendo
N

=
rispetto

3N-1
a

us
si

ottiene:

(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

Torna all'indice

Utilizzare le porte di I/O


Le porte di I/O (PORT A e PORT
B)
Il PIC dispone di 13 linee di I/O, programmabili individualmente come linee
di ingresso o uscita. Questi pin sono mappati direttamente nei registri
PORT_A
e
PORT_B.
PORT_B controlla i pin RB0,RB1,RB2,RB3,RB4,RB5,RB6 e RB7.
PORT_A controlla i pin RA0,RA1,RA2,RA3 e RA4. I tre bit piu' significativi del
registro non sono usati (si ricorda che la porta A possiede solo 5 linee)
In figura si mostrano i collegamenti logici* tra porte e pin di I/O esterni:

* ovviamente la struttura interna


reale di "collegamento" e' molto
piu' complessa

I due registri PORT_A e PORT_B hanno associati i relativi registri di


direzione, cioe' TRIS_A e TRIS_B. Questi ultimi due registri sono posizionati
nel banco 1 della RAM in corrispondenza dei primi. Ogni bit dei registri
TRIS_A e TRIS_B specifica la direzione della relativa linea di I/O. Se il bit
vale 1, la corrispondente linea e' di input, se vale 0 e' di output.
Ovviamente anche il registro TRIS_A sara' di soli 5 bit; i 3 bit piu'
significativi
non
sono
utilizzati.
Di default (quindi anche all'accensione del pic) tutti i bit del registro TRIS_A
e di TRIS_B sono impostati a 1; cio' significa che tutte le linee RA0...RA3 e
RB0..RB7
sono
di
input.
Per modificare la direzione di una linea di I/O e' sufficiente settare
opportunamente il bit corrispondente nel registro TRIS_A o TRIS_B, ad
esempio utilizzando le istruzioni di set e clear dei bit oppure scrivendo
direttamente il byte opportuno con MOVWF.

Caratteristiche elettriche

Linee porta A
Le linee della porta A come detto prima sono 5, associate ai registri PORT_A
e TRIS_A. I pin RA0,RA1,RA2 e RA3, quando configurati come output, sono
di tipo push-pull, ovverosia possono essere utilizzati sia come "sorgenti" di
corrente (drain mode) che come "pozzi" (sink mode), in altre parole sono in
grado sia di assorbire corrente dal positivo dell'alimentazione (25mA max),
sia
di
fornirla
verso
massa
(20mA
max).
In figura si mostrano i collegamenti interni per i pin RA0,RA1,RA2 e RA3:

Il pin RA4 e' un caso a parte perche' e' di tipo a collettore aperto (open
drain); cio significa che e' possibile utilizzarlo esclusivamente come pozzo di
corrente: il suo comportamento equivalente e' come quello di un
interruttore che si chiude verso massa. La figura mostra i collegamenti
interni relativi alla linea RA4:

I pin della porta A impostati come ingressi sono automaticamente


configurati come linee in alta impedenza (flottanti); se una di tali linee non
viene utilizzata occorre necessariamente collegarla al positivo (VCC) tramite
apposita resistenza di pull-up (ad esempio da 10Kohm).
Linee porta B
La porta B e' composta da 8 linee, RB0,RB1,RB2,RB3,RB4,RB5,RB6 e RB7.
Quando una linea B e' impostata come output, valgono le stesse
considerazioni viste per le linee RA0,RA1,RA2 e RA3 (modalita' sink/drain e
stessi
assorbimenti
massimi).
In modalita' input le linee B possiedono ognuna una resistenza di pull-up
interna, non e' quindi necessario inserirla esternamente come accade per le
linee A. Tali resistenze interne possono essere abilitate settando a zero il bit
#RBPU del registro OPTION_REG (il registro e' collocato nel banco RAM 1,
alla locazione 0x01). Di default le resistenze sono disabiltate.

Lettura e scrittura
La lettura e scrittura delle porte di I/O e' estremamente semplice. Una volta
che si siano scelte le direzioni delle linee tramite l'imposizione dei bit dei
registri TRIS_A e TRIS_B sara' sufficiente settare opportunamente i bit dei
registri di porta (ad esempio con le istruzioni MOV,BCF,BSF etc..) per
ottenere le tensioni ai capi dei pin corrispondenti. Ovviamente ha senso
imporre lo stato dei bit solo per le linee dichiarate come output. Per
acquisire lo stato delle linee di input e' sufficiente andare a leggere lo stato

dei relativi bit di porta, con le normali tecniche di lettura dei registri messe
a
disposizione (istruzioni
della
famiglia
MOV).
Una linea di I/O impostata come output presentera' all'esterno (cioe' sul
relativo pin) una tensione di +5volt se il bit corrispondente nel registro di
porta (PORT_A o PORT_B) e' settato ad 1; 0volt nel caso sia settato a 0.
Una linea di I/O impostata come input invece e' l'esatto contrario: se la
tensione ai capi del relativo pin e' di circa 5volt, allora il PIC imposta ad 1 il
bit corrispondente nel registro di porta. Se la tensione e' nulla, lo setta a 0.

Esempi software
Esempio:
Inizializzare la porta A in modo che RA0 e RA1 siano di output e RA2,RA3 e
RA4 di input.
STATUS EQU 0x03
PORT_A EQU 0x05
TRIS_A EQU 0x05
BCF STATUS,5
CLRF PORT_A
BSF STATUS,5
MOVLW 0x1C
MOVWF TRIS_A

;selezione del banco RAM 0


;azzeramento di PORT_A
;selezione del banco RAM 1
;valore di inizializzazione
;scrittura del valore in TRIS_A

NOTA: 0x1C e' il valore di inizializzazione corretto perche' in binario vale


11100. Scrivere tale parola in TRIS_A corrisponde a dichiarare le prime due
linee come output (RA0 e RA1) e le restanti tre (RA2 RA3 RA4) come input.
Esempio:
Supponendo che tutte le linee della porta B siano gia' settate come input,
leggere tale porta e salvare il risultato alla locazione 0x20 (un registro
GPR).
STATUS EQU 0x03
BCF STATUS,5
MOVF 0x06,0
MOVWF 0x20

;selezione del banco RAM 0


;caricamento del contenuto di PORT_B in W
;scrive il contenuto di W alla locazione 0x20

Esempio:
Supponendo che tutte le linee della porta B siano gia' settate come output,
scrivere il contenuto del registro 0x20 su tale porta.
STATUS EQU 0x03
PORT_B EQU 0x06
BCF STATUS,5
MOVF 0x20,0
MOVWF PORT_B

;selezione del banco RAM 0


;caricamento del contenuto del registro 0x20 in W
;scrive il contenuto di W in PORT_B

Esempi di collegamento
Utilizzare i piedini come output
Ecco alcuni esempi di collegamenti al pin RB0 configurato come output.
In figura si riporta un collegamento di un led alla linea RB0 del
microcontrollore:

Pilotare un led con RB0 - source mode

La resistenza da 330 ohm limita la corrente nel ramo a circa 15mA. Quando
sulla linea RB0 viene imposto un 1 il piedino si porta a VCC Volt ed inizia a
fluire una corrente VERSO massa e quindi il led si accende. Quando viene
imposto 0 il piedino si porta a massa e la corrente smette di fluire; il led si
spenge.
Il circuito appena descritto utilizza il piedino RB0 come sorgente (source) di
corrente.
Il prossimo schema mostra come utilizzare il piedono RB0 come pozzo
(sink) di corrente:

Pioltare un led con RB0 - sink mode


Quando sulla linea RB0 viene imposto un 1 il piedino si porta alla tensione
VCC (esattamente come prima) ma il questa volta il led rimane spento
perche non fluisce corrente. Quando viene imposto uno 0, il piedino si porta
a massa ed inizia a scorrere una corrente VERSO di questo, accendendo il
led.

I led sono dispositivi a bassa potenza e possono essere alimentati


direttamente dalle linee di I/O del microcontrollore. Altri dispositivi invece
richiedono molta piu' potenza e si rendono necessari appositi stadi driver. Il
prossimo schema mostra come azionare una lampada a 220 Volt tramite la
linea RB0 del microcontrollore:

Attivazione di una lampada a 220 Volt tramite la linea RB0


Quando sul bit RB0 viene posto uno 0, il transistor BC547 rimane interdetto,
non scorre alcuna corrente lungo il ramo del rele che quindi non scatta e la
lampada
rimane
spenta.
Quando sul bit RB0 viene posto un 1, il piedino RB0 si porta a VCC Volt ed
inizia a scorrere una corrente verso massa, attraverso la base e l'emettitore
del BC547; tale corrente e' di piccola entita' (limitata dalla resistenza da
10K) ma sufficiente a portare in conduzione il transistor, che quindi
"diventa" un cortocircuito. Inizia a fluire corrente lungo il ramo del rele' che
quindi si attiva e chiude il circuito al quale e' collegata la lampada.

Utilizzare i piedini come input


Per le linee B con resistenze interne di pull-up attivate e' sufficiente tenere
scollegata la linea per avere un 1 logico, portarla a massa per ottenere uno
0
logico.
Nello schema seguente si setta un bit sulla linea RB0 tramite un semplice
interruttore:

Schema per impostare lo stato della linea RB0 (resistenze interne di pullup attivate!)

Se le resistenze interne di pull-up sono disattivate e' necessario presentare


una tensione di +5 volt per avere un 1 logico o portare a massa per
ottenere unno 0 logico, come nello schema seguente:

Schema per impostare lo stato della linea RB0 (resistenze interne di pull-up
disattivate!)
Lo schema precedente e' obbligatorio per gli input delle linee A, per le quali
non sono previste resistenze di pull-up interne attivabili via software come
accade per le B.

Torna all'indice

Linguaggio assembly - parte II


Operazioni logiche
AND logico (ANDWF e ANDLW)
Per l'operazione logica di AND sono messe a disposizione le due istruzioni
ANDWF
e
ANDLW.
Sintassi

ANDWF
ANDLW

delle

istruzioni:

F,D
K

ANDLW permette di effettuare l'AND tra il registro di lavoro W ed un valore


letterale K (un byte); il risultato viene posto di nuovo in W ed inoltre se

questo (dopo l'esecuzione dell'istruzione) vale zero, viene settato ad 1 il bit


Z
del
registro
STATUS,
altrimenti
viene
settato
a
0.
ANDWF permette invece di effettuare l'AND tra il registro di lavoro W ed un
registro F qualsiasi;. Anche in questo caso viene modificato il bit Z del
registro STATUS, esattamente come sopra.
Esempio:
AND logico tra W ed il valore 0xAA.
ANDLW 0xAA,1
Esempio:
AND logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
ANDWF 0x34,0

OR logico (IORWF e IORLW)


Per l'operazione logica di OR sono messe a disposizione le due istruzioni
IORWF
e
IORLW.
Sintassi

delle

istruzioni:

IORWF
IORLW

F,D
K

Funzionano esattamente come le istruzioni di AND, ma in questo caso si


effettua l'OR.
Esempio:
OR logico tra W ed il valore 0xAA
IORLW 0xAA
Esempio:
OR logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
IORWF 0x34,0

XOR logico (XORWF e XORLW)


Per l'operazione logica di XOR sono messe a disposizione le due istruzioni
XORWF
e
XORLW.
Sintassi

delle

istruzioni:

XORWF
XORLW

F,D
K

Funzionano esattamente come le istruzioni di AND e OR, ma in questo caso


si effettua l'operazione di XOR.
Esempio:
XOR logico tra W ed il valore 0xAA
XORLW 0xAA
Esempio:
XOR logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
XORWF 0x34,0

NOT logico (COMF)


L'operazione

di

NOT

si

Sintassi

effettua

con

l'istruzione

della

COMF

COMF

istruzione:

F,D

L'istruzione inverte tutti i bit del registro puntato da F. Se D=0 il risultato


invertito viene posto in W (senza modificare F), altrimenti viene scritto in F.
Come le altre istruzioni logiche, anche questa setta il bit Z del registro
STATUS in funzione del risultato dell'operazione.
Esempio:
Invertire i bit del registro alla locazione 0x30 e porre il risultato in W senza
modificare il primo.
COMF 0x30,0

Operazioni aritmetiche
Addizione (ADDWF e ADDLW)
Per l'operazione di somma sono messe a disposizione le due istruzioni
ADDWF
e
ADDLW.

Sintassi

delle

istruzioni:

ADDWF
ADDLW

F,D
K

ANDLW effettua la somma tra un byte K ed il contenuto del registro di


lavoro W. Il risultato viene posto di nuovo in W. L'istruzione inoltre modifica
opportunamente
i
bit
Z,C
e
DC
del
registro
STATUS.
ANDWF e' simile ed effettua la somma tra il contenuto del registro puntato
da F e quello del registro di lavoro W. Il risultato viene posto in W se D=0,
altrimenti viene posto in F. Anche in questo caso vengono impostate
opportunamente
i
bit
Z,C
e
DC
del
registro
STATUS.
Esempio:
Sommare il contenuto di W con 12
ADDLW 0x12
Esempio:
Sommare il contenuto del registro alla locazione 0x0E con W e porre il
risultato nel primo.
ADDWF 0x0E,1

Sottrazione (SUBWF e SUBLW)


Per la sottrazione sono messe a disposizione le due istruzioni SUBWF e
SUBLW.
Sintassi

delle

istruzioni:

SUBWF
SUBLW

F,D
K

Funzionano esattamente come le relative operazioni di somma; l'unica


differenza
e'
ovviamente
l'operazione effettuata.

Decremento (DECF)
Operazione
Sintassi

DECF

di

decremento
dela

unitario.
istruzione:

F,D

Decrementa di una unita' il contenuto del registro puntato da F. Il risultato


viene posto in W se D=0, altrimenti viene posto in F. L'istruzione imposta ad

1 il bit Z del registro STATUS se il risultato dell'operazione di decremento e'


zero, altrimenti lo imposta a 0.
Esempio:
Decrementare di una unita' il registro 0x32 e porre il risultato in W
DECF 0x32,0

Incremento (INCF)
Operazione
Sintassi

di

decremento.
dela

istruzione:

INCF

F,D

Decrementa di una unita' il contenuto del registro puntato da F. Il risultato


viene posto in W se D=0, altrimenti viene posto in F. L'istruzione imposta ad
1 il bit Z del registro STATUS se il risultato dell'operazione di decremento e'
zero, altrimenti lo imposta a 0.
Esempio:
Decrementare di una unita' il registro 0x32 e porre il risultato in W
DECF 0x32,0

Operazioni di modifica dei registri


Rotazione (RRF e RLF)
Il microcontrollore possiede due istruzioni di rotazione: RRF e RLF. La
rotazione consiste nel "traslare di un posto" tutti i bit all'interno di un dato
registro. La rotazione puo' quindi essere destra (RRF) o sinistra (RLF) a
seconda che la traslazione avvenga appunto verso destra o verso sinistra.
La rotazione ovviamente e' ciclica, cioe' il bit che esce rientra "dall'altra
parte", ma non subito...prima "passa attraverso" il bit C del registro
STATUS. Un esempio chiarira' le idee. Supponiamo che il contenuto di un
dato registro F sia il seguente (sopra ai bit del registro poniamo i rispettivi
indici):
76543210
00000001
Affianchiamo alla sinistra del registro il bit C (bit carry) del registro STATUS.

All'inizio tale bit potra' trovarsi in uno stato qualsiasi (dipendera' da quello
che e' successo prima nel programma, il suo stato non e' noto) quindi,
anziche' scrivere 0 oppure 1, scriveremo una 'x'.
C 76543210
x 00000001
Ora, effettuando in sequeza tante istruzioni RLF (Rotate Left) ecco come
cambia il contenuto del nostro registro e del bit C:

RLF
RLF
RLF
RLF
RLF
RLF
RLF
RLF
RLF
RLF

C 76543210
x 00000001
0 00000010
0 00000100
0 00001000
0 00010000
0 00100000
0 01000000
0 10000000
1 00000000
0 00000001
0 00000010

e cosi' via...
Analogamente, ecco cosa succede con RRF (Rotate Right) partendo sempre
dal medesimo contenuto di registro:
C 76543210
x 00000001
RRF 1 00000000
RRF 0 10000000
RRF 0 01000000
RRF 0 00100000
RRF 0 00010000
RRF 0 00001000
RRF 0 00000100
RRF 0 00000010
RRF 0 00000001
RRF 1 00000000
RRF 0 10000000
RRF 0 01000000
RRF 0 00100000
RRF 0 00010000
e cosi' via...
Ecco perche' abbiamo detto che la rotazione avviene "attraverso" il bit C.
Questo sistema permette la lettura in sequenza di tutti i bit di un dato
registro semplicemente effettuando le rotazioni e controllando lo stato del
bit C.
Sintassi

RRF
RLF

delle

istruzioni:

F,D
F,D

RRF effettua la rotazione destra di un dato registro F. Il risultato viene posto


in F se D=1, altrimenti viene posto in W. Il parametro D ci viene in aiuto nel
caso in cui volessimo lasciare inalterato il registro F; infatti se D=1 tutte le

versioni traslate del registro F si susseguono in W. Ovviamente il bit C viene


aggiornato
opportunamente
sia
con
D=0
che
con
D=1.
RLF effettua la rotazione sinistra di un dato registro F. Valgono le stesse
considerazioni
viste
per
RRF.
Esempio:
Inviare in forma seriale e sulla linea RB0 della PORTA B tutti e otto i bit del
registro alla locazione 0x45. Il primo bit inviato deve essere quello meno
significativo. L'invio dei bit deve avvenire ogni 2us. Si suppone un clock di
4MHz
STATUS EQU 0x03
PORT_B EQU 0x06
TRIS_B EQU 0x06
BSF STATUS,5
MOVLW 0xFF
MOVWF TRIS_B
BCF STATUS,5
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP
NOP
RRF 0x45
MOVF STATUS,0
MOVWF PORT_B
NOP

;selezione del banco RAM 1


;imposta tutte le linee B come output
;selezione del banco RAM 0
;carica il registro STATUS in W
;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

;carica il registro STATUS in W


;scrive W in PORT_B (il bit C va in RB0)

NOP

Nibble-swap (SWAPF)
Effettua uno scambio dei quattro bit piu' significativi con i quattro meno
significativi. In gergo il termine "nibble" significa "gruppo di quattro bit".
L'istruzione scambia tra loro il nibble alto ed il nibble basso di un dato
registro.
Sintassi

della

SWAPF

istruzione:

F,D

Effettua lo scambio delle due meta' del registro F. Se D=1 il risultato viene
posto in F, altrimenti viene posto in W senza toccare F.
Esempio:
Scambiare tra loro le due meta' del registro 0x32 e porre il risultato nel
registro 0x33 lasciando inalterato 0x32.
SWAPF 0x32,0
MOVWF 0x33

Chiamata a funzione
Il microcontrolle 16F84 mette a disposizione del programmatore anche
l'utilissimo sistema di chiamata a funzione, un classico per qualsiasi CPU. In
pratica tramite apposite istruzioni e' possibile richiamare una pezzo di
codice di programma; in questo senso il sistema e' simile ad un GOTO con
ritorno, cioe' si salta in un altro punto della memoria di programma, si
esegue un certo pezzo di codice e poi si ritorna all'istruzione
immediatamente successiva a quella dove abbiamo saltato. L'istruzione di
salto e' CALL, mentre l'istruzione di ritorno puo' essere o RETURN, oppure
RETLW. Il pezzo di codice eseguito e' detto "subroutine". In pratica, quando
si vuole richiamare una subroutine, e' sufficiente invocare la CALL
specificando l'indirizzo della prima istruzione di tale subroutine (o una label,
come visto per le istruzioni di salto): il codice viene quindi eseguito dal
punto specificato in poi sino a che non si giunge ad una istruzione di
RETURN o RETLW. Giunti a questo punto si ritorna ad eseguire il programma
dall'istruzione immediatamente successiva a CALL. Si capisce facilmente
quindi che l'ultima istruzione di una data subroutine deve essere o RETURN
o RETLW; ovviamente se tali istruzioni non sono presenti il ritorno non avra'
luogo. Inoltre, piu' CALL possono riferirsi ad una stessa subroutine,
esattamente come piu GOTO possono riferirsi ad uno stesso punto di codice.
La figura sottostante mostra il sistema di chiamata a funzione:

La subroutine e' in rosso, con in evidenza la prima istruzione e l'ultima


(RETURN). Il programma principale non e' mostrato per intero ma puo'
trovarsi ovunque, prima della subroutine, dopo, oppure tutt'attorno alla
subroutine
stessa.
Quando nel programma principale si incontra una CALL viene eseguito il
salto verso la locazione specificata (cioe' alla prima istruzione della
subroutine), si esegue il corpo della subroutine e poi si ritorna verso
l'istruzione immediatamente successiva alla CALL grazie a RETURN (o
RETLW).
La CALL richiede sempre due cicli macchina (e' un salto).
In che modo il microcontrollore riesce a "ricordarsi" il punto al quale tornare
indietro? Esiste una piccola memoria ausiliaria, detta stack, in grado di
memorizzare
l'indirizzo
di
ritorno
(l'indirizzo
della
istruzione
immediatamente successiva alla CALL); quando si esegue un RETURN tale
indirizzo viene ripescato dallo stack e si puo' ritornare al programma
chiamante. Lo stack e' una memoria di tipo LIFO (Last In, First Out) che

puo' contenere sino ad un massimo di otto indirizzi di programma; grazie a


cio' e' possibile effettuare una CALL anche dentro una subroutine e ripetere
tale processo ricorsivamente sino ad un massimo di otto volte (cioe' otto
CALL consecutive). In sostanza, ogni volta che si effettua una CALL si salta
e viene inserito automaticamente nello stack l'indirizzo di ritorno, ogni volta
che si effettua un RETURN si "toglie" un indirizzo dallo stack e si salta a
quello (la rimozione consiste nel togliere l'ultimo elemento inserito) .

Chiamata a funzione (CALL)


Effettua

la

chiamata

Sintassi

funzione

(subroutine).

della

istruzione:

CALL

addr

Salta all'indirizzo di programma "addr" e carica nello stack l'indirizzo della


prossima istruzione per rendere possibile il ritorno. "addr" puo' essere
direttamente un indirizzo esadecimale della memoria di programma che
punta alla prima istruzione della subroutine, oppure piu' semplicemente una
label testuale come visto per il GOTO. Come detto in precedenza, per
eseguire l'istruzione CALL occorrono due cicli macchina.
Ritorno semplice (RETURN)
Ritorna
Sintassi

indietro

verso

la

CALL

della

chiamante.
istruzione:

RETURN
L'istruzione non ha parametri. L'esecuzione comporta il recupero dell'ultimo
indirizzo di programma inserito nello stack ed il salto verso tale locazione.
L'istruzione prevede due cicli macchina per essere eseguita.
Esempio:
Chiamare una subroutine che azzera il contenuto del registro 0x30, poi
incrementare di una unita' tale registro.
CALL azzera
INCF 0x30
azzera:
CLRF 0x30
RETURN

;vai alla subroutine "azzera"


;label di inizio della subroutine "azzera"
;azzera 0x30
;ritorna indietro al programma principale

Ritorno con parametro (RETLW)

Questa istruzione e' simile a RETURN ma con un'aggiunta significativa: oltre


a tornare indietro viene inizializzato il registro di lavoro W con un valore
prefissato.
Sintassi

della

RETLW

istruzione:

Si effettua il ritorno all'istruzione immediatamente successiva alla CALL


chiamante (esattamente come RETURN) ma in piu' provvede a caricare il
registro W con il valore K che e' stato specificato nell'istruzione RETLW.
Queso tipo di ritorno puo' essere molto utile in tutti quei casi in cui occorre
passare un byte di risultato al programma chiamante.
Esempio:
Effettuare la somma dei due registri alle locazioni 0x20 e 0x21 utilizzando
una apposita subroutine. Passare il risultato al programma chiamante
tramite il registro W.
MOVLW 0x10
MOVWF 0x20
MOVLW 0x3A
MOVWF 0x21
CALL somma

;carica i registri 0x20 e 0x21 con i valori


;da sommare
;vai alla subroutine "somma"

somma:
;label di inizio della subroutine "somma"
MOVF 0x20,0
;copia il contenuto di 0x20 in W
ADDWF 0x21,0
;effettua la somma
RETLW
;ritorna indietro al programma principale

Torna all'indice

Esempio pratico: led


lampeggiante
A titolo dimostrativo viene proposto un piccolo circuito in cui un led viene
fatto lampeggiare. Le fasi da seguire sono essenzialmente quelle descritte
alla sezione precedente e sono: creazione del progetto in MPLAB, scrittura
del programma, compilazione, debugging, inserimento del codice nel
microcontrollore e realizzazione del circuito.

Il programma
Una possibile soluzione per poter far lampeggiare un led e' quella ad
esempio di avere una linea di I/O che emette un segnale in cui si alternano
le tensioni di 0 e +5 volt. A tale linea collegheremo in serie il led ed una
resistenza limitatrice di corrente. Allo scopo puo' essere utilizzato il
programma precedente che alterna i valori 0 e 1 sul bit meno significativo
(il
primo
bit)
del
registro
PORTB.
Riportiamo il codice del programma:

;******************************************************
;
Programma di prova
;
Alterna 1 e 0 sul primo bit di PORTB ogni 100ms
;******************************************************
;direttive
PROCESSOR
RADIX
;definizioni
R1
R2
R3
PORTB
TRISB
TRISA
PORTA
PORTB
STATUS
FSR
INDF

16F84A
HEX

EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU

;inizio programma
ORG
0x0000
bsf

STATUS,5

0x20
0x21
0x22
0x06
0x06
0x05
0x05
0x06
0x03
0x04
0x00

movlw
movwf
bcf
loop:
bsf
call
bcf
call
goto

0x00
TRISB
STATUS,5
PORTB,0
wait
PORTB,0
wait
loop

;routine di attesa (circa 100ms)


wait:
movlw
0xBE
movwf
R1
longloop1:
movlw
0xBE
movwf
R2
longloop2:
decfsz R2,1
goto
longloop2
decfsz R1,1
goto
longloop1
return
;fine del programma
END

In ogni caso andremo ad utilizzare un oscillatore al quarzo da 4Mhz quindi


in ICPROG occorrera' selezionare l'oscillatore XT. Il Watch-Dog non serve
percio' deve essere disabilitato (WDT non selezionato). Possiamo lasciare
disabilitate le rimanenti due opzioni PWRT e CP.

Circuito di prova
Una volta compilato e programmato correttamente il PIC occorrera'
realizzare questo piccolo circuito:

Per ingrandire cliccare qui


In alto abbiamo il modulo alimentatore, costituito da un regolatore di
tensione 78L05. DCIN deve essere collegato ad una tensione continua da
almeno
9
volt.
In basso abbiamo il circuito principale costituito dal pic e da pochi altri
componenti. Al piedino #MCLR e' collegato il circuito standard di reset visto
nella prima sezione mentre in OSC1 e OSC2 e' collegato l'oscillatore da
4Mhz al quarzo (tipo XT). VDD e VSS sono ovviamente il piedino del
positivo e di massa. Ad RB0 sono collegati il led e la resistenza limitatrice:
dato che i pin di I/O del pic possono assorbire/fornire all'incirca 20mA
ciascuno, allora la resistenza dovra' avere un valore non inferiore a:
(5-0.7)volt / 0.02A = 215 ohm
Dove 0.7 e' la caduta di potenziale introdotta dal led e 5 e' la tensione
massima sul piedino (corrispondente al livello logico 1). Per stare sicuri e
non stressare la linea con assorbimenti al limite e' stata inserita una
resistenza
da
330ohm.
Di default le linee della porta A sono impostate come input e siccome non
vengono utilizzate occorre inserire delle resistenze di pull-up da 10K per
definirne i valori di tensione. Queste resistenze possono essere evitate
semplicemente impostando da programma le linee della porta A come
output.

Torna all'indice

Il timer interno
Il TIMER0 e' un dispositivo interno al
microcontrollore in grado di assolvere alla
duplice funzione di contatore e temporizzatore.
Le due modalita' sono mutualmente esclusive e
possono essere scelte via software. In ogni caso
il modulo e' ad 8 bit, cio' significa che e' solo in
grado di contare in step di 1 unita' e ripetere
ciclicamente i valori nell'intervallo da 0 a 255. Al
TIMER0 e' associato un prescaler in grado di
rallentare la velocita' di incremento dei valori in
8 possibili modalita' (1:2, 1:4, 1:8, 1:16, 1:32,
1:64, 1:128, 1:256). Inoltre, se si vuole, e'
possibile impostare il modulo in maniera tale da
produrre un interrupt ogni volta che si ha un
overflow del TIMER0, cioe' ogni volta che il
modulo passa dal valore 255 (0xFF) a 0 (0x00).
Al TIMER0 e' associato un registro, il TMR0
(mappato nel banco 0 alla locazione di memoria
0x01); in questo registro e' possibile leggere lo
stato attuale di avanzamento del contatore
oppure impostarne il valore.

Modalita' temporizzatore o contatore


Il
TIMER0
funziona
in
due
modalita':
temporizzatore o contatore. Nella prima il
TIMER0 si incrementa ad ogni istruzione (questo
in assenza di prescaler, altrimenti secondo il
rapporto imposto). Il valore del registro TMR0 e'
impostato a 0 per default, ma puo' essere
modificato in qualsiasi momento tramite
operazioni
di
scrittura.
Per selezionare la modalita' temporizzatore
occorre impostare a 0 il bit T0CS del registro
OPTION_REG.
Veniamo ora alla modalita' contatore. In questa
modalita' il TIMER0 si incrementa ad ogni risingedge o falling-edge del segnale applicato sul pin
RA4/T0CKl; la modalita' di incremento (rising o
falling-edge) e' selezionata impostando in
maniera opportuna il bit T0SE del registro
OPTION_REG (T0SE=0 per il rising-edge,
T0SE=1 per il falling-edge). T0CS a 1 imposta la
modalita' contatore.

Generazione dell'interrupt dal TIMER0


Ogni volta che il TIMER0 passa dal valore 255
(0xFF) a 0 (0x00) viene lanciata una
interruzione che automaticamente setta ad 1 il
bit T0IF del registro INTCON. L'interrupt puo'
essere inibito settando a 0 il bit T0IE. T0IF deve
necessariamente
essere
riportato
a
0
dall'eventuale routine di gestione dell'interrupt
affinche' possa verificarsi un nuovo interrupt.

Prescaler
Il prescaler e' un modulo per "rallentare" la
frequenza di aggiornamento del TIMER0 tramite
8 fattori di divisione prestabiliti. Il prescaler puo'
essere assegnato al TIMER0 oppure (in maniera
mutualmente esclusiva) al Watch-Dog interno (il
Watch-Dog e' un modulo aggiuntivo del
microcontrollore spiegato piu' avanti nella
guida). Il bit PSA del registro OPTION_REG
determina a chi associare il prescaler (al TIMER0
con PSA=0 oppure al Watch-Dog con PSA=1). I
tre bit PS0,PS1 e PS2 determinano una tra gli 8
possibili rapporti di divisione di frequenza
cioe' 1:2, 1:4, 1:8, 1:16, 1:32, 1:64, 1:128,
1:256. Ovviamente se il prescaler non viene
assegnato al TIMER0 (cioe' PSA=1) otteniamo
automaticamente un rapporto di divisione di
1:1.
Quando il prescaler e' associato al TIMER0, ogni
istruizione che vada a modificare il registro
TMR0 (ad esempio, CLRF 0x01, MOVWF 0x01,
BSF 0x01,3 etc...) comporta automaticamente il
reset a 0 dei tre bit PS0,PS1 e PS2.
Per finire, nella figura seguente sono mostrati i
collegamenti interni che collegano il prescaler al
TIMER0 e al Watch-Dog:

Torna all'indice

Sistema di interrupt
Il sistema di interrupt e' stato gia' introdotto a
grandi
linee
alla
sezione
"Memoria
RAM/FLASH/EEPROM". Grazie alla gestione degli
interrupt, il microcontrollore puo' essere istruito
in maniera tale da "porsi in ascolto" di eventi
esterni durante l'esecuzione normale del
programma; quando uno di questi eventi si
manifesta
il
program
counter
viene
temporaneamente salvato nello stack e si salta
alla locazione di memoria 0x0004 (vettore di
interrupt). Compito del programmatore e' quello
di inserire, a partire da questa locazione, una
subroutine in grado di gestire l'attuale istanza di
interruzione. La subroutine di gestione delle
interruzioni dovra' terminare con l'istruzione
RETFIE:
questa
istruzione
ricarica
automaticamente l'ultimo program counter
salvato nello stack e cosi' permette la normale

continuazione del programma precedentemente


interrotto. E' molto simile a RETURN e RETLW
ma in piu' riporta a 1 il bit GIE del registro
INTCON.
La figura sottostante ripropone il flusso di
controllo che il microcontrollore segue al
manifestarsi di una interruzione esterna:

Gli eventi di interruzione che possono essere


gestiti dal pic 16F84 sono di quattro tipi:

Interrupt dal pin RB0/INT

Interrupt per cambio di stato dei pin


RB4..RB7

Interrupt per completamento della fase di


scrittura della EEPROM interna

Interrupt dal timer

Esiste un registro opportuno, tale INTCON,


grazie al quale e' possibile abilitare o disabiltare
tutti o in parte i quattro eventi di interrupt
elencati sopra. Inoltre alcuni suoi bit vengono
settati dal controllore stesso e permettono (se si
vuole) di sapere, all'interno della subroutine,
quale interrupt tra i quattro possibili si e' appena
verificato.
Gli interrupt possono essere disabilitati del tutto
settando a 0 il bit GIE (Global Interrupt Enable)
del registro INTCON (il valore di default al reset
e' proprio questo). Un programma che voglia far
uso del sistema di interrupt dovra' preoccuparsi
di porre ad 1 questo bit. Quando si verifica una
interruzione,
il
microcontrollore
setta
automaticamente a 0 il bit GIE: in questo modo
si evita che possano verificarsi altri interrupt
durante la gestione di quello attuale. L'istruzione
RETFIE, oltre a ripassare il controllo al
programma interrotto, riabilita automaticamente
gli interrupt, settando ad 1 il bit GIE.

Interrupt dal pin RB0/INT

L'interruzione sul piedino RB0/INT e' scatenata


dal comportamento del segnale TTL su questo
piedino. L'interruzione su RB0/INT viene abilitata
settando ad 1 il bit INTE del registro INTCON.
Ovviamente deve essere settato ad 1 anche il
bit
GIE.
Quando il bit INTE e' settato a 0 oppure il GIE a
0 il piedino RB0/INT e' a tutti gli effetti la prima
linea della porta B (RB0) e come tale puo'
essere usata. Questo e' il comportamento di
default del piedino. Quando pero' si attiva
l'interrupt il pin non puo' piu' essere usato
come linea della porta B ma esclusivamente
come linea per ricevere le interruzioni
dall'esterno.
Esistono due modalita' di interrupt:

Rising edge

Falling edge

Con la prima (rising edge) l'interrupt viene


attivato nel momento in cui la tensione sulla
linea INT passa da 0 a +5 volt, quindi si
presume che normalmente il livello di tensione

sia il primo (0 volt). La seconda invece attiva


l'interrupt quando la tensione sul piedino INT
passa da +5 volt a 0, presupponendo quindi che
+5
volt
sia
la
tensione
standard.
La
modalita'
(rising/falling
edge)
viene
impostata settando opportunamente il bit
INTDEG
del
registro
OPTION_REG.
Una volta che il microcontrollore ha lanciato
l'interruzione viene automaticamente settato ad
1 il bit INTF del registro INTCON. Questo bit
puo' essere controllato dalla routine di gestione
dell'interrupt per sapere se si e' verificato
proprio l'interrupt associato alla linea INT
oppure era qualche altra interruzione. Nel primo
caso la routine dovra' provvedere a riportare a 0
tale bit altrimenti saranno inibiti i prossimi
interrupt dal piedino INT.

Interrupt dal timer

Quando il contatore TIMER0 va in overflow (cioe'


passa dal valore 0xFF a 0x00) viene lanciata una
interruzione. L'interruzione deve pero' essere
prima abilitata settando ad 1 il bit T0IE del
registro INTCON. Inoltre, al verificarsi della
interruzione, viene automaticamente settato ad
1 il bit T0IF del registro INTCON, e deve essere
riportato a 0 dalla routine di gestione definita
dal programmatore per poter mantenere
abilitato il sistema di interruzione dal timer.

Interrupt per cambio di stato dei pin


RB4..RB7

In questo caso viene sollevata una interruzione


quando avviene un cambio di stato qualsiasi
(cioe' un cambio di tensione) tra uno o piu' dei
piedini RB4,RB5,RB6 e RB7. Questo tipo di
interrupt puo' essere abilitato o disabilitato
settando opportunamente il bit RBIE del registro
INTCON.
Al
lancio
della
interruzione
viene
automanticamente settato ad 1 il bit RBIF del
registro INTCON.

Interrupt per completamento della fase di


scrittura della EEPROM interna

Senza addentrarci troppo nel sistema di


lettura/scrittura della EEPROM interna si puo'
dire che, siccome la fase di scrittura e'
estremamente lenta (20 ms) comparata con la
velocita' tipica di esecuzione delle istruzioni del
microcontrollore, e' stato pensato di permettere
alla EEPROM di lanciare una interruzione al
completamento della fase di scrittura. In questo
modo il programma utente puo' dare il comando
di scrittura e continuare subito con le istruzioni
successive, per poi gestire con comodo il
completamento
della
scrittura
rilevando l'interruzione.
Questo tipo di interruzione si abilita tramite il bit
EEIE del registro INTCON. Per rilevare questa
interruzione occorre controllare assieme i bit
RBIF,INTF e T0IF: se sono tutti a 0 allora
l'interruzione e' stata lanciata dalla EEPROM.

Torna all'indice

La memoria
EEPROM
La memoria EEPROM e' un modulo di memoria
all'interno del microcontrollore in grado di
mantenere le informazioni anche a sistema
spento. La memoria e' costituita da 64 byte in
tutto, accessibili sia in lettura che in
scrittura tramite quattro appositi registri, cioe':

Il

EECON1

EECON2

EEDATA

EEADR

registro

EEDATA,

mappato

in

RAM

alla

locazione 0x08 e banco 0, funziona da porta di


ingresso/uscita per i dati rispettivamente da e
verso la EEPROM; in soldoni si puo' dire che tale
registro contiene il byte da leggere/scrivere
nella
EEPROM.
EEADR, mappato in RAM alla locazione 0x09 e
banco 0, serve per specificare la locazione di
memoria da accedere in memoria EEPROM (cioe'
per specificare in che locazione della EEPROM
andra' scritto il byte contenuto in EEDATA,
oppure per specificare la locazione della EEPROM
da
leggere)
.
La EEPROM permette unicamente di leggere o
scrivere un byte alla volta. Una scrittura implica
automaticamente la cancellazione del byte
precedentemente memorizzato in quella cella di
memoria, mentre una lettura non produce
alcuna modifica nei dati della EEPROM.
I registri EECON1 e EECON2 sono due registri
ausiliari utilizzati per "comandare" la EEPROM
posizionati rispettivamente alle locazioni 0x08 e
0x09
del
banco
1
della
RAM.
EECON1 e' il registro di controllo con
implementati solo i cinque bit meno significativi.
I bit di controllo RD e WR avviano
rispettivamente il processo di lettura e scrittura
delle EEPROM; questi due bit possono solo
essere settati ad 1, mentre invece vengono
riportati a 0 dal pic al completamento della fase
di lettura (se RD) e scrittura (se WR).
Il bit WREN (WRite ENable) abilita o disabilita le
scritture nella EEPROM; quando viene settato ad
1 permette di effettuare le scritture, quando e'
settato a 0 le scritture sono inibite. Al reset del
microcontrolle tale bit e' settato a 0.
Il bit WRERR viene settato ad 1 dal pic quando o
un reset oppure un time-out del watch dog ha
interrotto prematuramente una scrittura nella
EEPROM (va ricordato che la scrittura nella
EEPROM e' una operazione relativamente lenta
se rapportata alla velocita' di esecuzione delle
istruzioni
di
programma).
Il bit EEIF funge da indicatore per il
completamento della fase di scrittura. Prima di
iniziare una scrittura deve essere posto a 0 e
viene posto automaticamente ad 1 dal pic
quando
la
scrittura
e'
terminata.

Registro EECON1

Il registro EECON2 e' un semplice registro di


appoggio utilizzato esclusivamente nella fase di
scrittura della EEPROM. Prima di poter scrivere
un byte nella EEPROM occorre scrivere in
EECON2 una serie prestabilita di valori, cioe' una
specie di sequenza di avvio, in grado di abilitare
la EEPROM a ricevere il byte in questione. La
sequenza deve essere ripetuta ogni volta che si
vuole memorizzare un byte.

Lettura

Leggere un byte dalla EEPROM e' estremamente


semplice. Per prima cosa occorre scrivere in
EEADR l'indirizzo del byte che si vuole leggere e
poi settare ad 1 il bit di avvio di lettura (bit RD
del registro EECON1). Al prossimo ciclo
macchina (4 colpi di clock) il registro EEDATA si
carica automaticamente con il valore del byte
richiesto che quindi puo' essere letto.
Esempio:
Leggere dalla EEPROM il byte alla locazione
0x0C e memorizzarlo nel registro di lavoro W
BCF 0x03,5
;selezione del banco RAM 0
MOVLW 0x0C
;l'indirizzo al quale leggere
MOVWF 0x09
;lo metto in EEADR
BSF 0x03,5
;selezione del banco RAM 1
BSF 0x08,0
;setto a 1 il bit RD di EECON1
;adesso EEDATA contiene il valore del byte
cercato
BCF 0x03,0
;selezione del banco RAM 0
MOVF 0x08,0
;copio il valore di EEDATA in
W

Scrittura

L'operazione di scrittura di un byte in EEPROM e'


relativamente piu' complicata. Per prima cosa
occorre disabilitare gli interrupt impostando a 0
il bit GIE di INTCON: in questo modo eventuali
interruzioni
non
verranno
prese
in
considerazione e non andranno a disturbare il
processo di scrittura in EEPROM. Adesso occorre
impostare in EEADR l'indirizzo al quale vogliamo
scrivere il byte ed il registro EEDATA deve
essere caricato con il byte in questione. Poi il bit

WREN di EECON1 deve essere portato ad 1 per


abilitare
la
scrittura.
Fatto cio' occorre seguire una sequenza
standard in grado di attivare la scrittura in
EEPROM:

Scrittura del valore 0x55 in EECON2

Scrittura del valore 0xAA in EECON2

Impostare ad 1 il bit WR di EECON1 per


avviare la scrittura

Riabilitare gli interrupt impostando a 1 il


bit GIE di INTCON

Tale sequenza deve essere ripetuta per ogni


byte che si vuole scrivere in EEPROM. La
disabilitazione degli interrupt non e' obbligatoria
ma e' considerata una buona pratica per evitare
problemi di scrittura qualora questa venisse
interrotta prematuramente da una interruzione
esterna.
Al termine della scrittura il bit WR viene
automaticamente impostato a 0 mentre il bit
EEIF
viene
posto
a
1.
Il programma utente puo' sapere quando
avviene il
completamento
della
scrittura
semplicemente verificando in un loop il valore
del bit WR oppure tramite il piu' efficiente
meccanismo
delle
interruzioni.
Da notare che il bit WREN di abilitazione NON
viene riportato a 0 automaticamente, quindi per
effettuare una nuova scrittura occorrera'
reipostarlo
ad
1.
Ecco un esempio di scrittura in EEPROM:

Esempio:
Scrive nella EEPROM il valore 0x02 alla locazione
0x10
BCF
0x03,5
MOVLW
MOVWF 0x08
MOVLW
MOVWF 0x09
BSF
0x03,5
BCF
0x0B,7
;inizio
sequenza
BSF
0x08,2
MOVLW
MOVWF 0x09
MOVLW
MOVWF 0x09

;selezione del banco RAM 0


0x02
;scrive 0x02 in EEDATA
0x10
;scrive 0x10 in EEADR
;selezione del banco RAM 1
;disabilita tutti gli interrupt
di
avvio
scrittura
;imposta ad 1 il bit WREN
0x55
;inserisco 0x55 in EECON2
0xAA
;inserisco 0xAA in EECON2

BSF 0x08,1
;bit WR ad 1 -> inizio scrittura
BSF
0x0B,7
;riabilita gli interrupt
;fine della sequenza

Torna all'indice

Modalit sleep
Lo sleep e' una particolare modalita' nella quale il microcontrollore
cessa momentaneamente di eseguire istruzioni e si porta in uno stato
di stand-by durante il quale i consumi di energia sono ridotti al
minimo; e' possibile poi "risvegliare il PIC" in qualsiasi momento
successivo. Questa modalita' e' utile nel caso in cui si voglia usare il
PIC in sistemi alimentati a batteria o comunque dove i consumi sono
un aspetto critico.

Entrare in modalita' sleep


Per far entrare il microcontrollore in modo sleep occorre eseguire
l'istruzione SLEEP. I bit PD e TO del registro
rispettivamente portati a 0 e 1, e l'oscillatore interno del pic viene
arrestato (l'esecuzione del programma si ferma all'istruzione SLEEP).
Inoltre le porte di I/O mantengono lo stato che avevano prima che il
sistema
si
fermasse.

Risveglio dalla modalita' sleep


Il microcontrollore puo' essere risvegliato dalla modalita' grazie al
verificarsi di uno dei seguenti eventi esterni:

Reset del microcontrollore (pin MCLR a massa)

Watch-Dog time-out

Interrupt dal piedino RB0/INT

Cambio di stato dei piedini RB0..RB7

Completamento della fase di scrittura della EEPROM

Al risveglio il microcontrollore si comportera' consistentemente


all'evento esterno verificatosi: nel primo caso (Reset) si avra'

effettivamente un reset, nel secondo il PIC continuera' con


l'esecuzione del programma dall'istruzione successiva a SLEEP. Negli
ultimi tre casi invece, se gli interrupt sono abilitati (GIE=1) verranno
"eseguiti" gli interrupt (esecuzione del programma a partire dalla
locazione 0x0004 e cosi' via) altrimenti (GIE=0) si proseguira' con
l'istruzione successiva a SLEEP.

Torna all'indice

Indirizzamento
indiretto
L'indirizzamento indiretto e' una delle
caratteristiche piu' interessanti del PIC
16F84. Grazie a questo sistema e' possibile
accedere sia in lettura che in scrittura ad un
dato registro GPR in modo indirietto,
ovverosia specificandone l'indirizzo sotto
forma di dato contenuto in un altro registro.
All'apparenza
il
meccanismo
potrebbe
apparire come una complicazione non
strettamente necessaria, in realta' e'
estremamente utile. Un esempio aiutera' a
capirne l'ambito di utilizzo: normalmente
per poter accedere in scrittura ad un dato
registro occorre specificarne l'indirizzo
direttamente
nell'istruzione.
Ad esempio, il codice seguente carica il
registo
0x0C
con
il
valore
0xA3:
MOVLW
MOVWF

0xA3
0x0C

Il valore 0x0C e' ovviamente specificato


nell'istruzione MOVWF. Fin qui niente di
male. Ma come si potrebbe fare per caricare
tutti i registri GPR con il medesimo valore
0xA3? Con il metodo tradizionale l'unico
sistema e' quello di ripetere l'istruzione
MOVWF per tutti i registri, in questo modo:
MOVLW
MOVWF
MOVWF
MOVWF
...
...
...
MOVWF

0xA3
0x0C
0x0E
0x0F

0x4F

L'operazione richiede quindi la ripetizione di


MOVWF ben 68 volte, con grande spreco di
memoria di programma. In effetti non si
possono creare cicli per ripetere MOVWF in
quanto l'indirizzo del registro corrente deve
essere
necessariamente
codificato
nell'istruzione (non e' un dato che si puo'
incrementare
di
uno).
Per risolvere il problema ci viene in contro il
sistema dell'indirizzamento indiretto. Grazie
ad esso si puo' accedere ad un dato registro
GPR tramite due registri di appoggio:

FSR

INDF

FSR si trova all'indirizzo di memoria 0x04 ed


e' mappato in entrambi i banchi RAM.
INDF si trova all'indirizzo di memoria 0x00
ed e' mappato in entrambi i banchi RAM.
Per vedere la loro posizione in memoria RAM
consultare la
sezione
"Memoria
RAM/FLASH/EEPROM".
Il registro FSR serve per specificare
l'indirizzo di memoria del registro che
vogliamo accedere; per farlo e' sufficiente
scrivere il relativo indirizzo in questo
registro. Una volta fatto questo, tutte le
eventuali letture e scritture effettuate nel
registro INDF saranno svolte come se si
stesse effettivamente accedendo al registro
in
questione
e
puntato
da
FSR.
Esempio:
Scrivere una subroutine che cancella tutti i
registri GPR (il primo registro GPR e' alla
locazione 0x0C e l'ultimo alla locazione
0x4F)

FSR
INDF
STATUS

EQU
EQU
EQU

0x04
0x00
0x03

:clearall
MOVLW 0x0C
;inizializzo il puntatore
al primo registro GPR
MOVWF FSR
;e lo scrivo nel registro
FSR
next:
CLRF INDF
;cancello il contenuto di
INDF
INCF FSR
;incremento di uno il
puntatore
MOVF FSR,0
SUBLW 0x50

BTFSS STATUS,2
;se FSR=0x50 vai a
RETURN, altrimenti vai a next
GOTO next
RETURN

Torna all'indice

Watch-Dog
Il Watch-Dog e' un oscillatore RC secondario
inserito all'interno del microcontrollore. Tale
oscillatore non richiede alcun componente
esterno per funzionare ed e' completamente
indipendente da quello che regola il
funzionamento del PIC: cio' significa che il
Watch-Dog continua a funzionare anche nel
caso in cui viene a mancare il segnale di
clock esterno. Il Watch-Dog ha associato un
tempo di default pari a 18 ms terminato il
quale effettua il reset del microcontrollore
(oppure, se in modo sleep, lo risveglia). Se
si desiderano tempi piu' lunghi e' possibile
associare il prescaler al Watch-Dog settando
a 0 il bit PSA del registro OPTION_REG e poi
impostare il rapporto di moltiplicazione
tramite i bit PS0,PS1 e PS2 (il prescaler,
quando viene associato al Watch-Dog,
funziona da moltiplicatore anziche' da
divisore come invece accadeva quando
associato al TIMER0). In sostanza lo scopo
del Watch-Dog e' quello di attendere il
periodo di tempo selezionato (di default 18
ms) e poi scatenare automaticamente il
reset del microcontrollore (nel caso il PIC sia
in modalita' sleep viene semplicemente
risvegliato continuando l'esecuzione del
programma). L'unico modo per prevenire il
reset del PIC e' quello di resettare il WatchDog, cioe' quello di riazzerare il suo conto
alla
rovescia
tramite
l'esecuzione
dell'istruzione
CLRWDT.
Il Watch-Dog e' utile in tutti quei casi in cui
non si vuole che, per qualche errore di
programmazione, il microcontrollore si
blocchi in un loop infinito in qualche parte
del programma. Si predispongono le
istruzioni di CLRWDT lungo tutto il codice del
programma in maniera tale da riazzerare

ogni volta il Watch-Dog senza quindi farlo


mai scattare; se il PIC si inserisce in qualche
loop indesiderato dal quale non puo' piu'
uscire non avra' modo di riazzerare il WatchDog che quindi provvedera' (dopo il tempo
di time-out) al reset. Ovviamente questo
esempio vale solo nel caso in cui nel codice
di loop non sia presente anche la stessa
istruzione
CLRWDT.
Il
Watch-Dog
parte
automaticamente
all'accensione del PIC, ma puo' essere
disabilitato
del
tutto
in
fase
di
programmazione.
In figura si riporta lo schema logico di
collegamento del Watch-Dog e del prescaler
(in questo caso chiamato postscaler):

Torna all'indice

Potrebbero piacerti anche