Esplora E-book
Categorie
Esplora Audiolibri
Categorie
Esplora Riviste
Categorie
Esplora Documenti
Categorie
by Vincent
Musica - Elettronica Didattica
Dispensa stilata da studente ITIS E. Majorana di Grugliasco (TO)
Relazione sui
APPUNTI
SU
PIC 16F84
INDICE
Microcontrollore PIC16F84.................................................................................................................3
PIEDINATURA E AREE DI MEMORIA............................................................................................3
ISTRUZIONI........................................................................................................................................5
ISTRUZIONI DI CARICAMENTO DATI A 8 BIT............................................................................6
ISTRUZIONI ARITMETICHE............................................................................................................7
ISTRUZIONI LOGICHE.....................................................................................................................7
ROTAZIONI E SET/RESET DI SINGOLI BIT..................................................................................8
SALTI E SUBROUTINE.....................................................................................................................8
ISTRUZIONI DI CONTROLLO SISTEMA.......................................................................................8
RIEPILOGO ISTRUZIONI.................................................................................................................9
NOTE PER LA PROGRAMMAZIONE............................................................................................10
GENERALITA'..............................................................................................................................10
EQU................................................................................................................................................10
INCLUDE.......................................................................................................................................11
ORG................................................................................................................................................11
#DEFINE........................................................................................................................................12
CONFIGURAZIONE HARDWARE.................................................................................................12
ESEMPIO DI PROGRAMMA..........................................................................................................13
LOOP..................................................................................................................................................14
LOOKUP TABLE (TABELLE DATI)...............................................................................................15
RITARDI SOFTWARE......................................................................................................................16
PORTE DI INGRESSO/USCITA.......................................................................................................18
TRASMISSIONE SERIALE SOFTWARE.......................................................................................20
POTENZIAMENTO ASSEMBLER..................................................................................................21
CONFRONTO TRA NUMERI A 8 BIT............................................................................................23
RICEZIONE SERIALE.....................................................................................................................25
LOOP CON ALTO NUMERO DI CICLI..........................................................................................26
MULTITASKING..............................................................................................................................30
SISTEMA DI DEBUG.......................................................................................................................35
Pagina 3 di 37
Microcontrollore PIC16F84
PIEDINATURA E AREE DI MEMORIA
Pagina 4 di 37
Il PIC 16F84 dispone di una memoria programma separata dalla memoria dati.
La memoria programma e' lunga 1024 locazioni (1kwords) con indirizzi da 0 a
1023. Il PIC all'accensione (o dopo un reset) inizia a leggere il programma
dall'indirizzo 0. L'indirizzo 4 e' il punto di partenza dell' interrupt handler, la
discriminazione della sorgente dell'interrupt va effettuata via software.
La memoria dati e' lunga 80 locazioni (indirizzi da 0 a 79) e prende il nome di
register file in quanto ogni locazione puo' essere considerata come un registro
a 8 bit. I primi 12 indirizzi (0..11) servono per il controllo dell'hardware del PIC,
i seguenti 68 indirizzi (12..79) sono usabili dall'utente per memorizzare i propri
dati di lavoro. Inoltre i primi 12 indirizzi dispongono di due banchi di registri,
alcuni dei quali sono visibili in entrambi i banchi, mentre altri sono presenti
solo in un banco. Il registro STATUS per esempio e' visibile in entrambi i
banchi, puo' cioe' essere letto o scritto sia se il banco attivo e' lo 0, sia se e' l'
1. La commutazione da un banco all'altro avviene settando il bit 5 di STATUS
(bit RP0), se vale 0 e' attivo il banco 0, se vale 1 e' attivo il banco 1. I 68
registri utente dall'indirizzo 12 (0CH) in poi sono invece sempre visibili e non
sono influenzati dal banco attivo. Il registro STATUS contiene anche i flags, il
bit 0 e' il flag C, il bit 2 e' il flag Z.
Pagina 5 di 37
Picture by Microchip
ISTRUZIONI
Il PIC dispone di un set di 35 istruzioni elementari, e ogni istruzione occupa
una sola locazione della memoria programma. Quasi tutte le istruzioni
vengono eseguite in 4 cicli di clock, un PIC cloccato a 4Mhz e' percio' in grado
di eseguire 1milione di istruzioni al secondo (1 mips) e ogni istruzione dura
1S. Le istruzioni di branch (salto) possono richiedere 8 cicli di clock anziche'
4. Nella terminologia Microchip un gruppo di 4 cicli di clock e' detto "ciclo", per
cui le istruzioni vengono eseguite in uno o due cicli.
Le aree di memoria su cui si puo' agire sono i registri della memoria dati e il
registro accumulatore W (che non fa parte dell'area dati, ma e' un ulteriore
registro hardware interno al PIC utilizzato nelle operazioni aritmetico logiche).
Pagina 6 di 37
n -> W
movwf addr
W -> (addr)
movf
addr,w
Z=X
(addr) -> W
movf
addr,f
Z=X
swapf addr,w
swap(addr) -> W
swapf addr,f
clrf
clrw
addr
Z=1
(addr) = 0
Z=1
W = 0
Pagina 7 di 37
ISTRUZIONI ARITMETICHE
addlw n
C=X Z=X
W = W + n
addwf addr,w
C=X Z=X
W = W + (addr)
addwf addr,f
C=X Z=X
(addr) = W + (addr)
sublw n
C=X Z=X
W = n - W
subwf addr,w
C=X Z=X
W = (addr) - W
subwf addr,f
C=X Z=X
(addr) = (addr) - W
incf
addr,w
Z=X
W = (addr) + 1
incf
addr,f
Z=X
(addr) = (addr) + 1
decf
addr,w
Z=X
W = (addr) - 1
decf
addr,f
Z=X
(addr) = (addr) - 1
ISTRUZIONI LOGICHE
andlw n
Z=X
W = W AND n
andwf addr,w
Z=X
W = W AND (addr)
andwf addr,f
Z=X
iorlw n
Z=X
W = W OR n
iorwf addr,w
Z=X
W = W OR (addr)
iorwf addr,f
Z=X
(addr) = W OR (addr)
xorlw n
Z=X
W = W XOR n
xorwf addr,w
Z=X
W = W XOR (addr)
xorwf addr,f
Z=X
comf
addr,w
Z=X
W = NOT(addr)
comf
addr,f
Z=X
(addr) = NOT(addr)
Pagina 8 di 37
addr,w
C=X
W = rlf(addr)
rlf
addr,f
C=X
(addr) = rlf(addr)
rrf
addr,w
C=X
W = rrf(addr)
rrf
addr,f
C=X
(addr) = rrf(addr)
bcf
addr,b
Bit b di (addr) = 0
bsf
addr,b
Bit b di (addr) = 1
SALTI E SUBROUTINE
btfsc
addr,b
btfss
addr,b
incfsz addr,w
W = (addr)+1
incfsz addr,f
(addr) = (addr)+1
decfsz addr,w
W = (addr)-1
decfsz addr,f
(addr) = (addr)-1
goto
addr
Salto a addr
call
addr
Chiamata di subroutine
return
retlw
retfie
Skip se W = 0
Skip se (addr) = 0
Skip se W = 0
Skip se (addr) = 0
Ritorno da subroutine
n
Nessuna operazione
clrwdt
sleep
Standby mode
Pagina 9 di 37
RIEPILOGO ISTRUZIONI
Il valore di d puo' valere 0 o 1 (o, rispettivamente, W o F), addr indica un
registro dati oppure un indirizzo di programma nelle istruzioni goto e call, b
indica un bit all'interno di un byte, n e' un valore costante (literal) a 8 bit.
Mnemonic operands Cyc. Flag Description
Byte oriented file register operations
addwf
addr,d
andwf
addr,d
d = W AND (addr)
clrf
addr
(addr) = 0
W = 0
clrw
C d = W + (addr)
comf
addr,d
d = NOT(addr)
decf
addr,d
d = (addr) - 1
decfsz
addr,d
1(2)
incf
addr,d
incfsz
addr,d
1(2)
iorwf
addr,d
d = W OR (addr)
movf
addr,d
(addr) -> d
movwf
addr
W -> (addr)
rlf
addr,d
C d = rlf(addr)
rrf
addr,d
C d = rrf(addr)
subwf
addr,d
swapf
addr,d
xorwf
addr,d
d = (addr)-1
Z
Skip se d = 0
d = (addr) + 1
d = (addr)+1
Skip se d = 0
C d = (addr) - W
swap(addr) -> d
d = W XOR (addr)
addr,b
Bit b di (addr) = 0
bsf
addr,b
Bit b di (addr) = 1
btfsc
addr,b
1(2)
btfss
addr,b
1(2)
andlw
call
addr
Chiamata di subroutine
clrwdt
C W = W + n
W = W AND n
goto
addr
iorlw
movlw
n -> W
Nessuna operazione
nop
W = W OR n
Pagina 10 di 37
Ritorno da interrupt
return
Ritorno da subroutine
sleep
Standby mode
retlw
sublw
xorlw
C W = n - W
W = W XOR n
EQU
Le 68 locazioni della memoria dati utente, e le 12 per il controllo hardware del
PIC, possono essere indirizzate scrivendo direttamente il loro indirizzo nelle
istruzioni (gli addr nella lista istruzioni precedente) o usando la direttiva EQU
per specificarne un nome simbolico. Il registro di stato all'indirizzo 3 puo'
essere infatti definito con una EQU:
STATUS
EQU
STATUS,2
3,2
EQU
Pagina 11 di 37
STATUS,Z
Anche alle locazioni utente puo' essere assegnato un nome, e quindi possono
essere usate come se fossero 68 registri ciascuno col suo nome:
var1
var2
contat
...
EQU
EQU
EQU
12
13
14
INCLUDE
Per ogni tipo di pic esiste un file gia' pronto (fornito assieme all'assemblatore)
che contiene tutte le definizioni standard. Per esempio in un programma scritto
per il pic 16F84A andra' incluso il file P16F84a.INC con la direttiva:
INCLUDE
"P16F84a.INC"
ORG
La direttiva di compilazione ORG (origine) che serve per due scopi differenti, a
seconda che si applichi al programma o all'area dati. Nel primo caso serve per
indicare all'assemblatore l'indirizzo fisico dove dovranno essere caricate le
istruzioni successive (generalmente un programma inizia sempre all' ORG 0).
Nel secondo caso invece permette di definire l'indirizzo fisico di partenza di
un'area dati, e di definirne poi l'occupazione tramite nomi simbolici e la
direttiva RES (riserva). Le nostre 3 variabili dell'esempio precedente
potrebbero per esempio essere dichiarate con:
var1
var2
contat
...
ORG
RES
RES
RES
0CH
1
1
3
Pagina 12 di 37
tutti gli indirizzi fisici associati a ciascun nome simbolico. Come si vede
nell'esempio, con res si puo' riservare anche piu' di un byte, la variabile contat
e' a 24 bit, e possiamo riferirci ai suoi tre bytes come contat, contat+1 e
contat+2.
#DEFINE
Esiste poi la possibilita' di ridefinire dei comandi comuni usati spesso
assegnando loro un nome piu' comodo. Per esempio per impostare il banco
attivo nella prima parte dell'area dati (quella che controlla l'hardware) si deve
settare o resettare il bit RP0 del registro status:
bsf
bcf
STATUS,RP0
STATUS,RP0
;attiva banco 1
;attiva banco 0
Per evitare di scrivere ogni volta queste istruzioni le possiamo ridefinire con le
parole Bank1 e Bank0:
#define
#define
Bank0
Bank1
bcf STATUS,RP0
bsf STATUS,RP0
CONFIGURAZIONE HARDWARE
I pic dispongono di un registro di configurazione hardware, che viene scritto
una sola volta al momento della programmazione, e che stabilisce il
funzionamento di alcuni circuiti interni, come il watch dog timer (wdt) e
l'oscillatore di clock. Questa operazione e' conosciuta anche con il nome di
"settaggio dei fuses". Nei micro dotati di memoria flash non si parla
naturalmente di fusibili e questi settaggi possono essere cambiati
semplicemente riprogrammandoli. Ogni programma per pic inizia con una
intestazione in cui si dichiara, oltre al tipo di micro usato e al formato di default
dei numeri (decimale, esadecimale ecc...), anche la configuration word che ne
determinera' il funzionamento (specificata con __CONFIG):
PROCESSOR
RADIX
INCLUDE
__CONFIG
16F84a
DEC
"P16F84a.INC"
1111111110001b
Pagina 13 di 37
Il significato completo dei singoli bit della configuration word e' scritto nel
datasheet, e varia per ogni tipo di micro. Quella qui riportata significa:
oscillatore al quarzo, wtd disabilitato, power up timer abilitato, protezione
programma disabilitata. Se si vuole abilitare il wdt si deve mettere a 1 il bit 2
(ricordandosi sempre che il bit 2 e' il terzo bit a partire da destra!).
ESEMPIO DI PROGRAMMA
Il seguente e' un semplice esempio di programma completo. Funzionalmente
non fa altro che leggere i 3 stati logici presenti sugli ingressi RB0..RB2 e
trasferirli pari pari sulle uscite RA0..RA2.
;---------------------------------------------------------------------; TEST1.ASM - Programma di test per PIC
;---------------------------------------------------------------------; RIEPILOGO USO PORTE:
;
; RA0 out
Uscita 0
; RA1 out
Uscita 1
; RA2 out
Uscita 2
; RA3 out
Non usato, sempre 0
; RA4 out(oc)
Non usato, sempre 0
;
; RB0 in(p-up) Ingresso 0
; RB1 in(p-up) Ingresso 1
; RB2 in(p-up) Ingresso 2
; RB3 in(p-up) Non usato
; RB4 in(p-up) Non usato
; RB5 in(p-up) Non usato
; RB6 in(p-up) Non usato
; RB7 in(p-up) Non usato
;---------------------------------------------------------------------; DEFINIZIONI
;---------------------------------------------------------------------PROCESSOR
16F84a
;clock 4 Mhz
RADIX
DEC
INCLUDE
"P16F84a.INC"
__CONFIG
1111111110001b
#define
#define
Bank0
Bank1
bcf STATUS,RP0
bsf STATUS,RP0
;---------------------------------------------------------------------; PROGRAMMA
;---------------------------------------------------------------------ORG
0
goto inizio
;----------INTERRUPT HANDLER (se usato)
ORG
4
istruzioni
Pagina 14 di 37
...
...
interrupt
;----------PREDISPOSIZIONE HARDWARE
inizio
Bank1
clrf TRISA
bcf OPTION_REG,7
Bank0
;attiva il banco 1
;Predispone porta A come uscite
;Attiva pull-ups su porta B
;attiva il banco 0
movf PORTB,w
andlw 00000111B
movwf PORTA
goto mainloop
;legge porta B
;maschera i 3 bit inferiori
;li scrive sulla porta A
;ripete il ciclo
;---------------------------------------------------------------------END
LOOP
Un loop (ciclo) puo' essere realizzato in modo semplice se il numero di
iterazioni non e' maggiore di 256. In questo caso basta un registro a 8 bit
come contatore (Impostando 0 in contat il si ottengono 256 iterazioni):
contat
ciclo
EQU
0CH
movlw
movwf
...
...
...
decfsz
goto
30
contat
;predispone 30 iterazioni
contat,f
ciclo
Se occorrono piu' di 256 iterazioni si devono usare piu' di registri. Per esempio
con due registri (contenenti le parti alta e bassa di un numero a 16 bit) si
possono realizzare fino a 65536 iterazioni:
lcont
hcont
EQU
EQU
movlw
movwf
movlw
0CH
0DH
85
lcont
4
ciclo
movwf
...
...
...
decf
incf
btfsc
decf
movf
iorwf
btfss
goto
Pagina 15 di 37
hcont
lcont,f
lcont,w
STATUS,Z
hcont,f
lcont,w
hcont,w
STATUS,Z
ciclo
addwf
retlw
retlw
retlw
retlw
retlw
....
PCL,f
126
0
44
255
188
movlw
movwf
movlw
call
....
PCLATH
4
table1
Indirizzi
000H - 0FFH
PCLATH
0
Pagina 16 di 37
100H - 1FFH
200H - 2FFH
300H - 3FF
EQU
0CH
table1
movlw
movwf
movf
addwf
retlw
retlw
retlw
retlw
retlw
....
....
PCLATH
PTAB,w
PCL,f
126
0
44
255
188
movlw
movwf
call
2
PTAB
table1
;pagina
RITARDI SOFTWARE
In alcuni programmi puo' essere necessario regolare con precisione la durata
temporale di una routine software, inserendo qua e la delle apposite istruzioni
che hanno l'unico scopo di creare un piccolo ritardo. Usando l'istruzione NOP
e' possibile inserire un ritardo di 1S (con clock di 4Mhz), se serve un ritardo
di 2S possono essere scritte due NOP una dietro l'altra, oppure, per
risparmiare memoria programma, si puo' usare una goto fittizia che salta
all'istruzione successiva (una goto infatti dura sempre 2S e occupa una sola
locazione della memoria programma... l'uso delle goto fittizie e' pero' uno stile
di programmazione un po' scorretto, e va usato solo se e' assolutamente
necessario risparmiare quelle poche locazioni di memoria, altrimenti e'
sicuramente preferibile usare due NOP):
Ritardo 1S:
nop
Ritardo 2S:
nop
nop
Pagina 17 di 37
goto $+1
ciclo
movlw
movwf
decfsz
goto
100
contat
contat,f
ciclo
2 + 3*99 + 2 = 301
ciclo
goto
movlw
movwf
decfsz
goto
$+1
100
contat
contat,f
ciclo
Pagina 18 di 37
PORTE DI INGRESSO/USCITA
OPTION_REG,7
Pagina 19 di 37
TRISA
;attiva banco 1
;azzera bit di TRISA (tutte uscite)
;ritorna al banco 0
11000B
PORTA
Usando le istruzioni BCF e BSF e' possibile resettare o settare un singolo bit
di una qualsiasi cella di memoria o di un pin di ingresso/uscita. Il seguente
esempio mostra come generare un breve impulso di 1S (con clock 4Mhz) dal
pin di uscita RA2 (naturalmente deve essere attivo il banco0):
BSF
BCF
PORTA,2
PORTA,2
SEGNALE
PORTA,2
SEGNALE
SEGNALE
Detto questo sappiamo come configurare i pin delle porte, come scrivere o
leggere su di essi (e anche su uno singolo), sappiamo quanta corrente ci puo'
passare e che RA4 e' un open-collector (o meglio open-drain), e abbiamo
visto come attivare le resistenze di pull-up della porta B.
Pagina 20 di 37
Lo schema del circuito e' riportato nella figura seguente. I pin RB0..RB7 sono
gli ingressi, il pin RA4 e' l'uscita che comanda un transistor adattatore di livello
TTL/EIA, in modo da inviare verso il PC le tensioni corrette sia in ampiezza
che in polarita'. La tensione negativa richiesta dalla seriale del PC e' presa dal
pin TX della seriale stessa, infatti, visto che il PC non deve trasmettere nulla,
su questo pin sono sempre presenti -12V. Il terminale chiamato RX e' la
ricezione del PC, quello chiamato TX e' la sua trasmissione (e' evidente che il
nostro PIC deve trasmettere verso la ricezione del PC, in caso di dubbi sulla
piedinatura della porta seriale o sul formato dei dati vedere l'apposita pagina).
Pagina 21 di 37
POTENZIAMENTO ASSEMBLER
Si e' visto che usando la direttiva di ridefinizione #DEFINE e' possibile
condensare un'intera istruzione in un nome simbolico, piu' comodo da
ricordare e piu' semplice da scrivere. Per esempio i comandi BANK0 e BANK1
usati precedentemente sono ridefinizioni delle istruzioni complete "BCF
STATUS,RP0" e "BSF STATUS,RP0".
Il compilatore mette anche a disposizione la direttiva macro che permette di
racchiudere con un nome unico un gruppo di istruzioni, e consente anche di
usare degli argomenti, cioe' dei parametri che assumono di volta in volta il
valore necessario.
Per comprendere l'utilita' di una macro vediamo un semplicissimo esempio.
Possiamo pensare alle seguenti due istruzioni che servono per caricare 44
nella variabile contatore (che naturalmente va definita prima con una equ o
una res):
MOVLW
MOVWF
44
CONTATORE
Pagina 22 di 37
Non esiste nessun modo per caricare direttamente 44 nella variabile, pero'
possiamo scrivere una macro che racchiuda queste due istruzioni e usarla
come se fosse una nuova istruzione singola (macroistruzione), per esempio:
LDF
CONTATORE,44
...che vorrebbe dire: carica nella variabile contatore il valore 44... la macro da
usare per "creare" questa nuova istruzione e':
LDF
MACRO arg1,arg2
MOVLW arg2
MOVWF arg1
ENDM
Il nome della macro e' LDF, arg1 e arg2 sono gli argomenti, in pratica i nomi e
i valori che noi scriveremo quando useremo l'istruzione LDF e che il
compilatore pensera' poi a sostituire automaticamente nelle istruzioni
all'interno della macro..
Usando ridefinizioni e macro e' quindi in un certo modo possibile "costruire"
delle nuove istruzioni, se non fisicamente per il processore, almeno
idealmente per il programmatore. Per prova ho voluto aggiungere nuove
istruzioni come i salti condizionati dai flag (Jc Jnc Jz Jnz) che sui pic
mancano.
Nel file pwrasm.inc sono contenute le ridefinizioni e le macro necessarie per
implementare le nuove istruzioni (e un breve riepilogo), questo file puo' essere
copiato in ogni programma con un copia/incolla (sconsigliato) o incluso come
le altre definizioni per il pic (metodo corretto).
Le nuove istruzioni comprendono la serie completa di salti (J..) e skip (SK)
condizionati dal valore dei flags (Z C), Abbiamo anche il salto condizionato
dal valore di un bit, per esempio JNB effettua un salto se il bit testato vale 0
(clear). Oltre al caricamento diretto in una variabile (LDF) abbiamo anche lo
spostamento da una variabile a un'altra: MOVFF var1,var2 che trasferisce
var1 in var2. Molte istruzioni lavorano su 16, 24 e 32 bit, comprese rotazioni,
shift, operazioni logiche e aritmetiche. I singoli bit possono essere anche
mossi da un registro all'altro (MOVB) o cambiati di stato (BTG). Ci sono poi le
istruzioni di attesa condizionata sul valore di un bit (WAITB WAITNB) e di
ritardo (DELAY). E per finire la comodissima DJNZ che permette di
decrementare una variabile e saltare se non e' arrivata a zero. Le
macroistruzioni per il cambio banco sono 4 per usare anche i pic 16F628 e la
serie 16F87x.
Pagina 23 di 37
=
=
=
W = 45 - W
W = var - W
var = var - W
Facendo la sottrazione si puo' pensare nei termini di: confronto il valore con
quello dell'accumulatore, per vedere se l'accumulatore e' maggiore, minore,
uguale ecc...
Condizioni
W = var (uguale)
Z=1
Pagina 24 di 37
Z=0
C=0
Z=0 and C=1
Z=1 or C=0
C=1
ESEMPI:
;IF pippo>26 THEN GOTO label
MOVF
SUBLW
BTFSS
GOTO
pippo,W
26
STATUS,C
label
120
pippo,W
STATUS,C
label
pippo,W
pluto,W
STATUS,C
label
pluto,W
pippo,W
STATUS,C
label
Pagina 25 di 37
RICEZIONE SERIALE
Similmente a quanto visto per la trasmissione seriale software e' possibile
realizzare un ricevitore seriale che acquisisce un byte dal PC e lo presenta in
formato binario sui pin RB0..RB7 (questa volta configurati come uscite).
Questo circuito puo' essere utile a tutti coloro che hanno bisogno di un
convertitore seriale/parallelo per comandare attraverso la porta seriale fino a 8
carichi indipendenti come ad esempio dei rele'.
Lo schema del circuito e' riportato nella figura seguente. I pin RB0..RB7 sono
le uscite, il pin RA0 e' l'ingresso da cui si ricevono i dati attraverso un
transistor adattatore di livello EIA/TTL. Il terminale chiamato TX e' la
trasmissione del PC (in caso di dubbi sulla piedinatura della porta seriale o sul
formato dei dati vedere l'apposita pagina).
Pagina 26 di 37
Il formato di ricezione e' asincrono start-stop 9600 8-N-1. Gli unici controlli
effettuati sulla corretteza dei dati sono ll valore del bit di start (che deve essere
0) e quello del bit di stop (che deve essere 1). I bytes non corretti vengono
ignorati, quelli corretti vengono portati sull'uscita.
Togliendo il transistor adattatore di livello sia da questo circuito che da quello
del trasmettitore e' possibile collegare tra loro due pic (uno trasmettitore e
l'altro ricevitore) e fare in modo di portare sulle uscite dell'uno i valori in
ingresso dell'altro (puo' servire per esempio per trasferire su due soli fili lo
stato di 8 segnali digitali)... attenzione pero' che questo sistema non e' ad alta
affidabilita', e percio' e' adatto solo per scopi didattici o dove la presenza di
errori di trasmissione e' una cosa accettabile.
Pagina 27 di 37
macro
decf
incf
var,addr
var,f
var,w
btfsc
decf
STATUS,Z
var+1,f
movf
iorwf
btfss
goto
endm
var,w
var+1,w
STATUS,Z
addr
torna a 0
alta
Per realizzare un ciclo a 16 bit con questa macro dobbiamo riservare 2 bytes
per una variabile di conteggio, chiamata ad esempio cont:
ORG
cont
0CH
RES
Poi dobbiamo assegnare ai suoi 2 bytes il valore che ci interessa, per esempio
per 25000 iterazioni abbiamo 97 come parte alta e 168 come parte bassa
(256*97+168=25000), possiamo percio' caricare i due valori in cont
ricordandoci che all'indirizzo piu' basso va messa la parte bassa. L'esempio
seguente mostra come realizzare questo loop a 16 bit:
label
movlw
movwf
movlw
movwf
.....
.....
Djnz16
168
cont
97
cont+1
cont,label
Allo stesso modo possiamo anche pensare ad una macro per un loop a 24 bit
(che puo' eseguire fino a 16.777.216 iterazioni):
Djnz24
macro var,addr
decf
var,f
incfsz var,w
goto
decf
incf
$+5
var+1,f
var+1,w
btfsc
decf
STATUS,Z
var+2,f
torna a 0
torna a 0
Pagina 28 di 37
parte alta
movf
iorwf
iorwf
btfss
goto
endm
var,w
var+1,w
var+2,w
STATUS,Z
addr
0CH
RES
contatore
movlw
movwf
movlw
movwf
movlw
movwf
label
.....
.....
Djnz24
label se non 0
160
cont
187
cont+1
13
cont+2
cont,label
macro
movlw
movwf
endm
var,n
n
var
Ldf16
macro
movlw
movwf
movlw
movwf
endm
var,n2,n1
n1
var
n2
var+1
macro
movlw
movwf
movlw
movwf
movlw
movwf
endm
Pagina 29 di 37
var,n3,n2,n1
n1
var
n2
var+1
n3
var+2
cont,97,168
Ldf24
cont,13,187,160
I vari valori vanno scritti dal piu' significativo (parte alta) al meno significativo
(parte bassa), in questo modo e' anche semplice determinarli scrivendone le
doppiette esadecimali:
25000 = 61A8
900000 = 0DBBA0
Ldf16 cont,61H,0A8H
Ldf24 cont,0DH,0BBH,0A0H
label
Ldf
....
Djnz
cont,165
;165 iterazioni
cont,label
label
Ldf16 cont,44,0
....
Djnz16 cont,label
label
Ldf24 cont,8,0,133
....
Djnz24 cont,label
;11264 iterazioni
;524421 iterazioni
Pagina 30 di 37
MULTITASKING
Multitasking significa avere in esecuzione su un unico micro piu' programmi
contemporaneamente. Ogni programma svolge una particolare funzione di controllo e
puo' essere chiamato processo o task. I task possono essere indipendenti tra loro
oppure svolgere un compito di gruppo scambiandosi informazioni attraverso celle di
memoria. Naturalmente il parallelismo e' solo apparente, in quanto in realta' il micro
porta avanti singolarmente i vari programmi un'istruzione per volta, ma se questo
meccanismo e' sufficientemente rapido si ottiene l'apparenza (e di fatto l'esistenza) di
piu' programmi contemporanei in esecuzione. L'uso del multitasking permette di
semplificare enormemente la scrittura di programmi complessi che devono tenere sotto
controllo molti processi nello stesso tempo.
Il multitasking puo' essere di tipo preempitive o non preempitive. Nel primo caso i
programmi sono scritti nel modo consueto (come se fossero l'unico programma
presente nel micro) ed e' compito dello scheduler del sistema operativo avviarli e
interromperli a intervalli di tempo prestabiliti, questa soluzione pero' e' molto complessa
e sicuramente non realizzabile con un piccolo microcontroller come il PIC 16F84.
Pagina 31 di 37
.....
.....
return
routine1
.....
.....
return
Pagina 32 di 37
routine2
.....
.....
return
;-------------------------------------------------------------
ATTENZIONE: usando l'istruzione addwf PCL (detta anche computed goto cio salto
calcolato) e' semplice creare delle strutture "on n goto", pero' bisogna stare molto
attenti all'impostazione del PCLATH e ai bordi delle pagine di programma da 256 bytes
ciascuna come gia' visto nel paragrafo sulle lookup tables. In pratica nessuna parte del
task deve essere posta ad indirizzi che si trovano a cavallo tra due pagine. E'percio'
meglio ricorrere al controllo numerico del valore della variabile di stato come mostrato
piu' avanti.
Un modo per risolvere il problema delle pagine quello di impostare correttamente il
registro PCLATH, che contiene i bit pi significativi dell'indirizzo di memoria programma
quando vengono effettuate operazioni che coinvolgono direttamente il registro PCL.
Nell'esempio seguente si usano le direttive per il compilatore HIGH e LOW per indicare
la parte pi alta e pi bassa dell'indirizzo della memoria programma a cui iniziano le
istruzioni di salto. A questo indirizzo viene sommato lo stato corrente aggiustando il
PCLATH se la parte bassa dell'indirizzo supera il valore 255:
;------------------------------------------------------------task1
movlw HIGH Ttask1 ;carica in w parte alta indirizzo Ttask1
movwf PCLATH
;la mette nel PCLATH
movlw LOW Ttask1 ;carica in w parte bassa indirizzo Ttask1
addwf stat,w
;la somma allo stato
btfsc STATUS,C
;se non overflow skip
incf PCLATH,F
;altrimenti incrementa PCLATH
movwf PCL
;mette parte bassa indirizzo nel program
counter
Ttask1
goto routine0
;goto eseguito se stat=0
goto routine1
;goto eseguito se stat=1
goto routine2
;goto eseguito se stat=2
routine0
.....
.....
return
routine1
.....
.....
return
routine2
.....
.....
return
;-------------------------------------------------------------
Pagina 33 di 37
stat
Bank0
Bank1
bcf STATUS,RP0
bsf STATUS,RP0
ORG
0CH
RES
;----------------------------------------------------------------------------------; INIZIALIZZAZIONE
;----------------------------------------------------------------------------------ORG
0
;---------------PREDISPOSIZIONE TIMER
Bank1
Pagina 34 di 37
OPTION_REG,PSA ;prescaler assegnato a tmr0
OPTION_REG,T0CS ;conteggio da clock interno
11111100b
OPTION_REG,f
;divisione prescaler per 32
;---------------INIZIALIZZAZIONE VARIABILI
Bank0
clrf
stat
;---------------AVVIA CONTEGGIO
clrf
bcf
TMR0
INTCON,T0IF
task1
;e chiama il task 1
goto
mainloop
;-----------------------------------------------------------------------------------
Come si diceva prima, per evitare ogni problema di di indirizzamento in cui si puo'
incorrere modificando direttamente il program counter (PCL), si pu realizzare la
struttura del task in un modo un po' piu' complesso, e cioe' facendo dei confronti
numerici con il valore della variabile di stato:
;------------------------------------------------------------task1
movf stat,f
btfsc STATUS,Z
goto routine0
movlw 1
subwf stat,w
btfsc STATUS,Z
goto routine1
movlw 2
subwf stat,w
btfsc STATUS,Z
goto routine2
goto routine3
routine0
...
...
return
routine1
...
...
return
Pagina 35 di 37
...
...
return
routine3
...
...
return
;-------------------------------------------------------------
macro
movlw
subwf
btfsc
goto
endm
task1 Cpje
uguale
Cpje
Cpje
Cpje
...
var,val,addr
val
var,w
STATUS,Z
addr
stat,0,routine0
stat,1,routine1
stat,2,routine2
stat,3,routine3
Come in tutti i sistemi multitasking bisogna fare un po' di attenzione a non creare
conflitti tra i processi. Un esempio tipico e' la scrittura su porta di uscita o in memoria.
Se due processi vogliono scrivere nello stesso indirizzo la scrittura dell'uno potrebbe
annullare quella dell'altro causando effetti indesiderati difficili da scoprire. Una
soluzione puo' essere quella di usare un solo "processo scrittore" incaricato di questa
funzione che prende i dati inviati dagli altri processi unendoli senza creare conflitti. In
generale e' bene che ogni processo "tocchi" solo quello che gli compete,
lasciando del tutto inalterato il resto del sistema, come se non fosse mai stato eseguito.
SISTEMA DI DEBUG
Per mettere in pratica tutto quello che si e' detto riguardo a trasmissione seriale e
multitasking si puo' creare un task di debug che trasmette ciclicamente 4 bytes di dati
verso un PC. I 4 valori da trasmettere li possiamo prendere da 4 celle di memoria
adibite a celle di debug (chiamate per esempio dbg1 dbg2 dbg3 dbg4). Il protocollo
puo' consistere in un byte di allineamento (sincronismo) seguito dai 4 bytes dati inviati
nel consueto formato start/stop 9600,N,8,2 e da un carattere di checksum che e' la
somma a 8 bit di tutti i precedenti. Sul PC un programma apposito deve ricevere questi
bytes e visualizzarli in formato binario, esadecimale e decimale. A questo punto si ha a
disposizione un metodo semplice per visualizzare i valori interni al programma del PIC,
Pagina 36 di 37
infatti basta scrivere nelle celle dbg1..4 i valori per vederli trasferire immediatamente sul
video, e questo puo' servire per verificare lo stato delle porte di ingresso, o il risultato di
un'operazione aritmetico/logica, o l'evoluzione dello stato di un task al verificarsi degli
eventi previsti. Per sperimentare il multitasking possiamo anche far
contemporaneamente lampeggiare un led, per esempio collegato sul pin RA0.
Durante il debug deve essere usato un altro pin configurato come uscita, che va
collegato ad un adattatore TTL/EIA per essere compatbile con la seriale del PC. Il
programma di ricezione su PC contolla continuamente la congruenza dei dati in arrivo,
se ci sono errori segnala sulla barra del titolo "NO SYNC OR FRAME ERROR", o se
per 200mS non riceve dati segnala "NO SIGNAL".
Il programma dimostrativo dbgdemo.asm legge i bit della porta B e li invia come primo
bytes del campo dati della trama, gli altri 3 bytes sono a zero. Prima del campo dati
viene trasmesso il carattere di sincronizzazione 170, alla fine dei dati viene trasmesso il
checksum (la somma a 8 bit di tutti i precedenti compreso il sincronismo). Viene inviato
un byte ogni 4,096 mS, quando sono stati inviati tutti i 6 bytes della trama completa c'e'
Pagina 37 di 37
una pausa di circa 41mS, poi viene inviata un'altra trama. In contemporanea a questa
attivita' il led sul pin RA0 lampeggia costantemente alla frequenza di circa 1Hz.
ADATTATORE TTL --> EIA "IN CABLE"
Per poter collegare rapidamente il PIC alla seriale del PC, senza montare ogni volta un
apposito circuito, ho racchiuso quest'ultimo all'interno del coperchio di un connettore
DB25 femmina. Il cavetto che esce dal connettore ha 3 poli (massa, +5V e segnale a
livello TTL) e va collegato alla bredboard su cui si sta provando il PIC. In questo modo
ci si puo' dimenticare del problema dell'adattamento e si possono inviare i dati al PC
usando una normale uscita TTL (anche open collector). Lo schema e' il seguente: