Tips & Tricks - PIC

 

I banchi nei PIC16F


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: 

   movwf P     
  
 

il compilatore Assembler interpreta la riga come se avessimo scritto 

   movwf 0x006     
  
 

in quanto nel file PIC16F88.INC viene data l' equivalenza che sostituisce la label PORTB:

PORTB   EQU    H'0006'

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:

ERRORLEVEL -302

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
,
; 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

  1. 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.
     
  2. 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.
     
  3. 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.
     

 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 20/10/10.