Timer0 è un timer ad 8 bit (conta cioè da 0 a 255), con prescaler a 8 bit (condiviso col Watchdog Timer). A Timer0 corrisponde il registro speciale TMR0, il cui contenuto mostra il valore corrente del conteggio.
La tabella seguente mostra in sintesi i registri associati al Timer0 e i relativi bit:
Le principali impostazioni di controllo di Timer0 dipendono dal registro OPTION_REG:
I bit 6 e 7 non sono utilizzati per il timer e dunque al momento non ci interessano. Per quanto riguarda gli altri, il significato è il seguente:
NOTA:
All'avvio del PIC o dopo un reset, il registro OPTION_REG sarà
impostato con tutti i bit a 1.
Con questa impostazione, il Timer0 è abilitato come contatore ed
il registro TMR0 verrà incrementato dagli impulsi che arrivano
sul pin T0CKI, su fronte di discesa, senza prescaler (1:1), il quale
sarà assegnato al WDT, con rapporto 1:128.
Il prescaler di Timer0 non è leggibile o scrivibile e, come abbiamo visto, può anche essere destinato al Watch Dog Timer (WDT): essendo in comune tra i due, dovremo scegliere noi la sua assegnazione, impostando il bit PSA nel registro OPTION_REG. Un'altra cosa molto importante da sapere è che, qualsiasi istruzione che si riferisce al registro TMR0 o al WDT, azzera il conteggio contenuto nel prescaler, che ripartirà a contare da zero. Per questo motivo, si deve prestare attenzione quando, nel corso del programma, si vuole assegnare il prescaler da una all'altra periferica e viceversa.
La tabella seguente mostra come i bit PS2, PS1 e PS0 determinano il rapporto di divisione del prescaler (i rapporti impostati per il Watch Dog Timer sono differenti):
PS(2:0) (Prescaler rate Select) PS2 PS1 PS0 TMR0 0 0 0 = 1:2 0 0 1 = 1:4 0 1 0 = 1:8 0 1 1 = 1:16 1 0 0 = 1:32 1 0 1 = 1:64 1 1 0 = 1:128 1 1 1 = 1:256
Facciamo un esempio pratico: supponiamo di
usare come sorgente il clock del PIC (TOCS=0) con frequenza
Fosc = 4MHz, di aver assegnato il Prescaler al Timer0 (PSA=1) e di aver
impostato il prescaler a
In generale la frequenza con cui il Timer0 si incrementa è pari a:
dove FOSC è la frequenza di clock del sistema e PS è il valore del prescaler, cioè nel nostro caso abbiamo (4MHz/4)/32 = 31250 Hz. Si osservi che la frequenza utilizzata è quella del clock (Fosc) divisa per 4, poiché il timer viene incrementato ad ogni ciclo di istruzione, cioè ogni 4 periodi del clock.
Alla frequenza di incremento precedente corrisponde un periodo (1/f = 1/31250) pari a 32 µs.
Quando il registro TIMER0 va a 0 (ovvero passa da 255 a 0 andando in overflow e ricominciando il conteggio daccapo), viene settato il bit T0IF nel registro INTCON, generando un interrupt (se abilitato con il bit T0IE).
Consideriamo di nuovo l'esempio precedente di un timer con frequenza di clock 4MHz e prescaler 32. Il conteggio si azzera ogni 256 periodi, cioè ogni 256*32 µs = 8,192 ms.
Supponiamo di aver bisogno di generare un interrupt ogni 3 ms. In teoria potremmo agire sulla frequenza di clock del sistema, ma in questo modo cambieremmo la velocità di esecuzione di tutte le istruzioni del PIC (certo non una buona scelta). In alternativa potremmo cambiare il prescaler PS in modo da cercare di ottenere un azzeramento del contatore ogni 3 ms. E' facile vedere però che non esiste nessuna frequenza di prescaler che (con il clock a 4 MHz dato) permette di avere un interrupt ogni 3 ms.
Infatti essendo FOSC la frequenza di oscillazione del sistema e PS il valore del prescaler, abbiamo:
Tfine conteggio = 256 * Periodo = 256 * PS/(Fosc/4)
da cui posso ricavare il valore del prescaler PS
PS = Tfine conteggio /256 * (Fosc/4)
cioè nel nostro caso
PS = 3ms / 256 * (4MHz/4) = 11,72
E' evidente che questo valore di prescaler non è disponibile.
Possiamo allora ricorrere a un piccolo "trucco". Basta semplicemente fare in modo che Timer0 non inizi il suo conteggio da zero, ma da un valore che andiamo a determinare. Calcoliamoci anzitutto quanti cicli di conteggio sono necessari, con il clock dato a 4MHz, per generare 3ms. Poiché 256 conteggi avvengono in 8,192 ms, usando una semplice proporzione, abbiamo molto rapidamente che:
x : 3 ms = 256 : 8,192ms da cui x = 3/8,192 * 256 = 93,8
Arrotondiamo il valore precedente all'intero più vicino (94) e abbiamo che, per ottenere un interrupt da Timer0 ogni 3ms, è necessario precaricare il contatore col valore 256-94 = 162. Una volta generato l’interrupt reimposteremo di nuovo il Timer0 a 94, e così ogni volta, ad ogni interrupt.
Per semplificare i calcoli consigliamo il software gratuito PicTimer il quale effettua il calcolo del valore migliore da assegnare al Prescaler ed al Timer0 (8 bit) in maniera tale da ottenere o avvicinarsi al tempo di interrupt scelto, in base alla frequenza dell’oscillatore.
Un'altra tecnica consiste nel fissare un valore di prescaler e un valore totale di conteggio "ragionevoli" e quindi contare un certo numero di interrupt fino ad ottenere il valore di tempo desiderato. Supponiamo per esempio di voler ottenere un tempo di 2 secondi. Si tratta di un valore piuttosto elevato e, usando un clock FOSC = 4 MHz non esiste certamente nessun valore di prescaler PS che permette di ottenerlo.
Possiamo allora procedere nel seguente modo. Fissiamo ad esempio il prescaler a 32 (PS = 32) e facciamo eseguire il conteggio 250 volte invece di 256 (la ragione per cui abbiamo scelto questo valore sarà più chiara fra poco). In questo modo viene generato un interrupt ogni:
Tfine conteggio = 250 * 32/(4MHz/4) = 8 ms
Dunque il nostro timer genera un interrupt ogni 8ms. Poiché noi vogliamo invece un tempo di 2s, sarà sufficiente contare (all'interno della routine di servizio dell'interrupt) 2/8m = 250 volte. In pratica basta una variabile contatore che viene incrementata a ogni interrupt: quando questa variabile raggiunge il valore 250 (nel nostro esempio), si potrà azzerare la variabile stessa ed eseguire le operazioni che si vogliono compiere ogni 2 secondi.
Come si può notare è stato scelto di contare fino a 250 (invece di 256) in modo da ottenere un valore esatto (senza virgola) nel risultato finale.
Sito realizzato in base al
template offerto da
http://www.graphixmania.it