I PIC mid-range (PIC16Fxxxx) hanno l' area RAM, che comprende la RAM vera
e propria (GPR - General Purpose Registers) e i registri delle periferiche (SFR
- Special Functions Registers) suddivisa in due o più banchi.
Si passa dai 2 banchi di PIC16F84 ai ben 32 banchi di
PIC16F1827, che vediamo sintetizzati i primi 8 nell'imponente tabella dell'
immagine qui sotto.
In effetti, i banchi significativi del
"piccolo" (18 pin) PIC16F1827 sono i primi nove e l' ultimo; gli altri fanno presumere "future expansions"
di un chip già zeppo di periferiche e funzioni.
Il motivo della proliferazione dei banchi dipende dalla
quantità di periferiche integrate nel chip: più periferiche, più registri
di controllo.
E' noto che il passaggio da un banco ad un' altro avviene
per mezzo:
-
dei bit RP che si trovano nello STATUS (16F84, 88,
628, 876, 870, ecc)
-
oppure attraverso uno speciale registro BSR (Bank
Select Register), tipico dei PIC18F (16F1826, 1827)
Entrambi i registri sono accessibili da tutti i banchi,
così da non richiedere che si debba "cambiare banco per cambiare il
banco".
Sia che si tratti di RP, sia che si abbia a disposizione
il molto più comodo BSR, si ha sempre a che fare con la necessità di
cambiare il banco quando si vuole accedere ad un determinato registro.
Al reset, per default viene impostata una selezione di RP
o BSR che punta al Banco 0.
In questo banco si trovano solitamente i PORT, ma i TRIS
relativi sono in Banco 1.
Questo richiede che si attivi una commutazione del banco nella fase di
inizializzazione degli I/O.
; Set PORTB come uscita
; Al reset il banco è Bank 0
banksel TRISB
; banco 1
movf 0x00, W
; Set PORTB come uscita
movwf TRISB
banksel PORTB ; banco 0
movlw b'00000001' ; RB0 = 1
movwf PORTB
|
Questa necessità di cambio tra i banchi avviene anche per
operazioni sulla stessa periferica, come ad esempio l' USART del PIC16F876/77
in cui il registri di stato della trasmisione e quello di emissione del dato
si trovano su due banchi diversi.
; check for send byte by USART
banksel TXSTA ; Yes, Select Bank 1 Registers
SO_0
btfss TXSTA,TRMT ; Is the Shift register empty?
goto SO_0
; wait
banksel TXREG
; Reset Bank 0 Registers
SO_1 movwf TXREG
; Send character
return
; Done
|
E' evidente che questa divisone in banchi degli SFR crea
due problemi non secondari:
- la prestazione del micro è ridotta dalla necessità
frequente di commutare i banchi
- e, maggior problema, richiede la continua sorveglianza
su dove si stia con i banchi in quel momento, pechè da questo dipende la
necessità di cambiare o meno il banco per accedere ad un altro registro
E questo ultimo punto è particolarmente sensibile in programmi complessi che
facciano uso di interrupt e di cui non sia immediato il seguire dove si stia nei
banchi.
Perchè va tenuto presente che quando scriviamo, ad esempio per un PIC16F88:
il compilatore Assembler interpreta la riga come se avessimo scritto
in quanto nel file PIC16F88.INC viene data l' equivalenza che sostituisce la
label PORTB:
Ora, movwf 0x006
ha l' effetto di portare il contenuto dell' accumulatore WREG nel registro RAM
all' indirizzo esadecimale 6h. Ma questo se l' indice del banco vale 0.
Sappiamo che l' indirizzamento dell' area RAM è effettuato dal codice dell'
istruzione, che contiene l' indirizzo-destinazione, integrato dai bit RP di
selezione del banco:
|
Infatti occorre un indirizzo di almeno 9 bit per coprire l' area della
RAm e l' istruzione ne codifica solo 7: gli altri due sono costituiti
dai bit RP.
Dunque, se RP0 e RP1 sono a 0, il nostro
movwf
0x006
scriverà a 0006h, ovvero in PORTB, come desiderato.
Ma se RP0 e RP1 hanno valori diversi, sarà puntato uno degli altri
banchi !! |
Così:
RP1 |
RP0 |
Indirizzo
opcode |
Indirizzo
definitivo |
Hex |
SFR |
0 |
0 |
0000110 |
000000110 |
006 |
PORTB |
0 |
1 |
0000110 |
010000110 |
086 |
TRISB |
1 |
0 |
0000110 |
100000110 |
106 |
PORTB |
1 |
1 |
0000110 |
110000110 |
186 |
TRISB |
se invece di RP0 e RP1 uguali a 0 avessimo RP1=0,
ma RP0=1, il nostro WREG andrebbe a riversarsi non in PORTB, cambiando lo stato
dei pin in uscita, ma finirebbe in TRISB, cambiando la direzione dei pin e, al
minmo, impedendo il funzionamento di quanto previsto.
Se poi indirizzassimo la scrittura a EEDATA senza considerare il banco, potremmo
trovarci a scrivere su PIR1 o PIE1 o EECON1, creando chissà quale dissesto nelle funzioni del chip.
|
Si può notare come alcuni SFR siano ripetuti in shadow nei vari
banchi alla stessa posizione.
Ad esempio STATUS è accessibile a 03h, 83h, 103h, 183h. E così pure
PCL, PCLATH, FSR, INTCON, si ripetono simmetrici su ogni banco.
Questo vuol dire che qualunque sia il valore di RP, ovvero in
qualunque banco ci si trovi in quel momento, questi registri di primaria
importanza sono accessibili comunque, senza nessun bisogno di modificare
i bit di indirizzamento dei banchi.
Soluzione molto pratica.
Una analoga situazione si trova per una piccola parte di RAM,
denominata ACCESS, che si ripete identica nei quattro banchi e che
quindi è accessibile senza nessun bisogno di modificare i bit RP.
Altri registri sono duplicati alternativamente, per cui PORTB è
accessibile egualmente in Banco 0 e 1 e TRISB è accessibile in Banco 1
e 3.
Stessa situazione dell' esempio prima portato con EEDAT/PIR1,PIE1 e
EECON1 si ha quando si vuole accedere alla RAM vera e propria, che è
suddivisa nei 4 banchi.
A parte l' area ACCESS, il rimanete dei byte è soggetto alla stessa
ferrea legge degli RP.
|
Ovvero, scrivendo all' indirizzo 20h si raggiunge questa
cella solo se RP0=0 e RP1=0. Negli altri casi si andrà a toccare una diversa
locazione:
RP1 |
RP0 |
Indirizzo
opcode |
Indirizzo
definitivo |
Hex |
0 |
0 |
0000010 |
000000010 |
020 |
0 |
1 |
0000010 |
010000010 |
0A0 |
1 |
0 |
0000010 |
100000010 |
120 |
1 |
1 |
0000010 |
110000010 |
1A0 |
il che porterebbe scompiglio nei valori contenuti nelle
celle. Se poi si puntasse alla cella 199h senza selezionare il Banco 3, si
potrebbe finire a scrivere nella cella 99h, sballando il clock della
trasmissione seriale.
Fortunatamente i PIC enhanced (PIC18) hanno tutti gli SFR in un unico banco ad
accesso facilitato (Access RAM), ma per chi deve ancora utilizzare i PIC
mid-range, i banchi sono in agguato costante.
Quali sono le soluzioni al problema ?
A parte il passare ai PIC18, si tratta di porre attenzione durante la scrittura
del programma a dove ci si trova con il banco.
In questo senso, MPASM ci viene in aiuto con il warning [302] che invia un
messaggio del tipo:
Message[302] file.ASM nnn :
Register in operand not in bank 0. Ensure that bank bits are
correct.
|
che ci ricorda la necessità di "stare all'
occhio" con gli switches dei banchi.
A questo proposito è opportuno evitare di inserire nel sorgente il comando:
che abolisce il warning relativo. Può essere seccante
trovarsi un sacco di avvisi [302], ma è più seccante ritrovarsi il programma
che non funziona a causa di una commutazione di banco non effettuata.
Peraltro, [302] non è un errore, ma un "warning", ovvero un messaggio
di allerta che non influisce e non blocca l' assemblaggio dell' oggetto; si
limita a dare un avvertimento (warning) al programmatore.
Una volta individuata la necessità di cambiare il banco,
si potrà agire in diversi modi.
La differenza tra la pseudo istruzione banksel
e il classico bsf
STATUS, RP0 è che banksel
si adatta al numero di banchi del processore e agisce sugli switch di banco (RP)
in modo tale da poter puntare con sicurezza al banco in cui sta la label
oggetto.
In questo caso, su entrambi gli RP, per cui è equivalente a:
banksel
|
assemblato
|
; Set PORTB come uscita
banksel TRISB
; banco 1
movf 0x00, W
; Set PORTB uscita
movwf TRISB
banksel PORTB ; banco 0
movlw
b'00000001' ; RB0 = 1
movwf PORTB
|
; Set PORTB come uscita
bsf STATUS, RP0 ; banco 1
bcf
STATUS, RP1 ; banco 1
movf 0x00, W
; Set PORTB uscita
movwf TRISB
bcf
STATUS, RP0 ;
banco 0
bcf
STATUS, RP1
; banco 1
movlw
b'00000001' ; RB0 = 1
movwf PORTB
|
Certamente se, durante il programma, non si è mai
toccato RP1, che è a 0 per default al reset, non c'è necessità di applicare
un bcf STATUS, RP1
ogni volta che si cambia banco.
Però bisogna essere sicuri di non averlo mai
toccato; e in un programma che utilizza tutti i banchi, tenere presente dove ci
si trova per agire solo ed esclusivamente sullo switch necessario per passare al
banco voluto può non essere così immediato. banksel,
agendo su tutti gli switch, sacrifica qualche istruzione in più, ma evita tante
grane.
Quindi, anche se
banksel dà origine a due istruzioni, è
auto esplicativa a livello di listato sorgente ed è da preferire, a meno che ci
siano limiti stretti di memoria o di velocità (cosa che capita molto, molto
raramente in listati semplici per vecchi PIC).
Note
-
Non tutti i PIC hanno lo stesso numero di banchi e non tutti i mid-range
utilizzano i bit RP per la selezione dei banchi. Come al solito, affrontando un
dispositivo non ancora sperimentato, è obbligo dare una lettura al foglio dati
per rendersi conto di eventuali differenze rispetto a quanto fino a quel momento
utilizzato.
-
Salvo casi molto particolari, la velocità di esecuzione delle istruzioni dei
PIC è molto elevata rispetto alla necessità del processo controllato. Quindi,
la "perdita di tempo" dovuta allo switch dei banchi, anche con banksel,
non comporta praticamente alcun problema.
- Come sempre, la stesura di un sorgente ordinato e l' uso intensivo di
label invece di assoluti, facilita grandemente il lavoro del programmatore
quando si arriva alla fase di collaudo del software.
|