LO HAI MAI REALIZZATO CON UN PIC?

Un approccio ai timer dei PICMicro

 

Sebbene i timer siano dispositivi molto semplici, in relazione a tutte le periferiche disponibili oggi sui microcontrollori, emerge come, per i principianti, rappresenti una seria difficoltà impostare correttamente i registri associati ai timer per sfruttarne appieno le caratteristiche. In questo capitolo de "LO HAI MAI REALIZZATO CON UN PIC?" vediamo come sia possibile avvicinarsi ai timer dei PICMicro, come al solito con un esempio quanto più possibile diretto. Nel seguito verrà illustrato come impostare il TIMER 0 di un PIC16F84A, prendendo spunto da una richiesta pervenuta nel forum di Electroportal.

 

 

PIC16F84A e TIMER 0

I PICMicro della famiglia mid-range dispongono di uno o più timer; con i PIC18F, PIC24F, dsPIC e PIC32 il numero dei timer aumenta in modo considerevole. Rimanendo nella famiglia dei PICMicro a 8 bit (PIC12F, 16F e 18F), le strutture dei timer sono a 8 o a 16 bit. Alcuni possono essere adibiti a funzioni specifiche (come ad esempio avviene per TMR2, adibito a base dei tempi per il segnale PWM) ma tutti possono essere comunque utilizzati come timer veri e propri. Per semplicità, vediamo lo schema a blocchi del timer 0 del PIC16F84A. Nonostante questo PIC non sia tra quelli "di piccola taglia" con ampie potenzialità, si è distinto nel tempo per diversi utilizzi cui è stato applicato.


Timer 0 presenta un registro a 8 bit in grado di incrementarsi in modo continuo dal valore 0 al valore 255 (0xFF in notazione esadecimale); inoltre, non appena avviene la transizione da 0xFF a 0x00, il timer può scatenare un interrupt del quale tenere conto durante lo svolgimento delle funzionalità.
La sorgente che fornisce i TICK al timer perché si incrementi, può essere scelta tra l'oscillatore interno oppure quello esterno e, in questo caso particolare, l'incremento del registro TMR0 può avvenire sul fronte di salita o su quello di discesa del segnale esterno.
Il segnale viene fatto transitare attraverso un prescaler, ossia un divisore che è programmabile; i valori slezionabili vanno da 1:2 fino a 1:256. Questo significa che, se considerassimo ad esempio il prescaler impostato a 1:4, ogni 100 impulsi in entrata al prescaler, ce ne saranno 25 in uscita da quest'ultimo. Nel caso in cui non si desideri l'utilizzo di un prescaler, è sufficiente assegnare questa risorsa (il prescaler) ad un altro timer, il Watch Dog Timer.

Il setup del timer 0 avviene con il registro OPTION_REG; i bit di tale registro assumono in seguente significato:

  • T0CS (bit 5): se posto a 0 seleziona l'oscillatore di sistema (con frequenza Fosc/4) come sorgente per il timer; se posto a 1 i TICK del timer giungono dal pin RA4/T0CKI.

 

  • T0SE (bit 4): questo bit va considerato solamente se il bit T0CS è posto a 1, ossia nel caso in cui i TICK del timer giungano dal pin RA4/T0CKI. Se T0SE è posto a 0, l'incremento del timer avviene sui fronti di salita del segnale applicato a RA4/T0CKI, se posto a 1, l'incremento avviene sul fronte di discesa.

 

  • PSA (bit 3): se viene posto a 0, il blocco prescaler viene assegnato al timer 0, con la possibilità di selezionare un valore tra otto disponibili; se viene posto a 1, il blocco prescaler viene assegnato al Watch Dog Timer e timer 0 viene automaticamente connesso ai blocchi successivi senza alcuna divisione di frequenza.

 

  • PS2, PS1, PS0 (bit 2, 1, 0): questi sono i bit che selezionano il valore del prescaler, secondo dei valori tabulati. Nella figura che segue è riportata la tabella con i valori del prescaler ed il corrispondente valore di questi tre bit.


I bit 6 e 7 non intervengono sulla impostazione del timer 0.

Il timer e l'interrupt

La gestione più interessante dei timer è quella che avviene mediante l'uso degli interrupt. Per capire meglio come funziona il timer ci si riferisce ad un esempio piuttosto pratico, quello che porterà a realizzare un timer di un secondo, il più preciso possibile.
Dovensodi basare sul clock di sistema, tanto più preciso è il quarzo tanto maggiore sarà la precisione della temporizzazione. Eventuali derive termiche inevitabilmente porteranno il timer ad accelerare o rallentare.
Se al PIC viene connesso un quarzo con Fosc = 4MHz, si ha che Fosc/4 = 1 MHz. Suponendo di impostare il prescaler ad un valore unitario (1:1), si ha che il registro del timer si incrementa con periodo pari a 1us e dopo 256 us il registro è saturato. Si potrebero contare un milione di microsecondi per aver realizzato il timer. Ma forse il numero di microsecondi da contare è troppo elevato. Allora si può pensare di impostare in modo oppurtuno il prescaler, ad esempio con un valore pari a 1:128; ciò comporta che il timer si incrementi con cadenza pari a 1MHz / 128 = 7812,5 Hz che corrisponde a 128 us. Quindi, se si pensasse di riempire il registro TMR0 partendo da 0 e arrivando a 255, si otterrebbe un ritardo di tempo pari a 32.768ms, dato dal prodotto di 128us per 256. Questo numero non è comodo da utilizzare per contare il tempo che ci si è prefissi, non è un sottomultiplo intero di 1 secondo. Un altro escamotage è quello di far contare il PIC da un valore di TMR0 diverso da 0. Se il conteggio partisse dal valore 119, per arrivare a saturare il registro TMR0 è necessario contare 137 TICK il che significa che sono trascorsi 17.5 ms, ottenuti come prodotto di 128us per 137. Se nella routine di interrupt vengono contati 57 intervalli, ciò che si ottiene è un periodo di tempo complessivo pari a 999.52 ms, ottenuto dal prodotto di 17.5 ms per 57.
Il valore ottenuto è molto prossimo al valore di un secondo; ora, se si considera che il codice dovrà eseguire le oprazioni di context switch e che l'interrupt ha un tempo di latenza pari a due o tre cicli macchina, sembra che l'obiettivo sia stato raggiunto.

Non resta che abilitare l'interrupt; per far ciò si impiegano i bit T0IE (Timer 0 Interrupt Enable) e GIE (General Interrupt Enable), entrambi nel registro INTCON.

Schema elettrico

Lo schema elettrico, facile da realizzare, è riportato nel seguito

 

Il firmware

L'esempio che realizza il timer di un secondo è stato scritto in assembly utilizzando l'ambiente di sviluppo integrato MPLAB. Il codice sorgente è stato commentato ampiamente, per modo di capire, contestualmente a quanto scritto in questo articolo, come si comporta il microcontrollore.

;
; Esempio di funzionamento del TIMER 0
; su PICMicro PIC16F84A
;


processor   PIC16f84a
#include "p16F84A.inc"
radix      dec
__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
errorlevel   -302

; Definizione delle variabili    
; Quando questa variabile assume valore 0, è trascorso un secondo
OneSec      equ 0x20
; Variabile utilizzata per le operazioni di Context Switch sul registro W
W_TEMP      equ 0x21
; Variabile utilizzata per le operazioni di Context Switch sul registro STATUS
STATUS_TEMP equ 0x22    

ORG     0x00  
goto    start

; Interrupt vector
ORG     0x04

; ISR - Interrupt Service Routine
INTR
; Context save, come da datasheet
movwf     W_TEMP         ; Copy W to TEMP register,
swapf     STATUS, W     ; Swap status to be saved into W
movwf     STATUS_TEMP     ; Save status to STATUS_TEMP register

; chiamata alla ISR del TIMER 0
call    T0ISR

;Context restore, come da datasheet:
swapf     STATUS_TEMP,W ; Swap nibbles in STATUS_TEMP register and place result into W
movwf     STATUS         ; Move W into STATUS register (sets bank to original state)
swapf     W_TEMP, F     ; Swap nibbles in W_TEMP and place result in W_TEMP
swapf     W_TEMP, W     ; Swap nibbles in W_TEMP and place result into W

retfie

; Routine T0ISR - Risposta all'interrupt di TMR0
T0ISR
bcf        INTCON,T0IF    ; Pulizia dell'interrupt flag T0IF di timer 0
bcf     STATUS,RP0
movlw    1            ; Decremento di una unità della
subwf    OneSec        ; variabile OneSec.
bnz        quit            ; Se OneSec è diversa da zero, salta a "quit"
call    SetRB0            ; altrimenti invoca la funzione SetRB0
quit
movlw    d'119'        ; Si ripristina il valore del registro
movwf    TMR0            ; del timer e si ritorna.
return

SetRB0
bcf     STATUS,RP0        
movfw    PORTB   ; Lettura della porta PORTB
xorlw    1           ; esecuzione dell'operazione XOR
andlw    0x01        ; soltanto con il bit RB0 (toggle del bit della porta).
movwf    PORTB   ; Scrittura sulla porta PORTB
movlw    d'57'         ; Si ripristina il contenuto della variabile
movwf    OneSec  ; OneSec per il conteggio e si ritorna.
return

start
bsf     STATUS,5
movlw   0x00
movwf   TRISB
bcf     STATUS,5
clrf    PORTB
call    SetupTMR0 ; Chiamata alla routine di setup di TIMER 0

inizio
goto    inizio    ; ciclo infinito, in attesa dell'interrupt...

; Routine di setup di TIMER 0
; Il timer 0 viene impostato con prescaler 1:128 (registro OPTION_REG)
; Inoltre si abilita la risposta all'interrupt (registro INTCON)
SetupTMR0
bsf        STATUS,RP0

movlw    b'10000110'  ; TIMER 0 con prescaler 1:128

movwf    OPTION_REG ; e sorgente di TICK interna (Fosc/4)

movlw   b'10100000'    ; abilitazione dell'interrupt, agendo sui bit

movwf    INTCON    ; GIE e T0IE del registro INTCON

bcf        STATUS,RP0


movlw    d'119'    ; TMR0 non viene fatto contare da 0, bensì

movwf    TMR0        ; da un valore diverso (in questo caso 119)

movlw    d'57'        ; la variabile OneSec viene inizializzata

movwf    OneSec    ; al valore 57
return

; Fine del programma
END



Il codice va compilato all'interno di MPLAB, avendo cura di realizzare un ''progetto''; questo aspetto è molto importante in quanto consente di simulare il firmware con MPSIM.

La simulazione

L'ambiente di svilupo MPLAB contiene al suo interno MPSIM, attivabile dal menu Debugger -> Select Tool -> MPLAB SIM. Con questo strumento è possibile simulare il comportamento del firmware. In particolare due sono gli strumenti che in questo caso vengono in aiuto: uno è STOP WATCH, un vero e proprio cronometro che, se fissati opportunamente i break point lungo il codice sorgente, permette di misurare con estrema precisione la durata di routine.


Il secondo strumento utilizzato è il logic analyzer, un vero e proprio analizzatore di stati logici per quanto concerne la simulazione. Mediante il logic analyzer si è potuto constatare l'andamento di RB0 e verificare che il firmware andasse ad eseguire in modo corretto le istruzioni, come previsto.


Come si può notare, l'andamento di RB0 è come quello stabilito dai calcoli effettuati in precedenza. Volendo migliorare gli aspetti di misura del tempo sarebbe utile compensare il comportamento del timer affinché le derive dovute agli errori di conteggio restino non trascurabili.

E con altri PIC...?

Se, aprendo il cassetto dei componenti elettronici si scoprisse che il (glorioso) PIC16F84A non c'è ma al suo posto ci sono altri PICMicro a 8 bit, non è il caso di disperare. L'esempio proposto, adattando opportunamente il codice assembly scritto, è facilmente eseguibile da altri PICMicro, come il PIC16F628A, PIC16F819, PIC16F684A, PIC16F876A, ecc.

Riferimenti

*Ambiente di sviluppo MPLAB''': http://www.microchip.com/mplab
*Datasheet PIC16F84A''': http://ww1.microchip.com/downloads/en/DeviceDoc/35007b.pdf
*Collana "LO HAI MAI REALIZZATO CON UN PIC?":
Il contamarce
Una sorpresa musicale per Babbo Natale
Una tecnica antirimbalzo
Il dado elettronico


Pillole di microcontrollori PIC

Licenza Creative Commons
Questa opera viene distribuita con licenza Creative Commons Attribuzione - Non commerciale - Non opere derivate 3.0 Unported.
ShoppingASEhandelASErhvervIndexDKServiceIndexDK