Tutorial → PIC18 in C → Il modulo Timer0

Questa pagina descrive l'uso del Timer0, uno dei numerosi counter/timer
presenti all'interno del PIC18
Un timer è un contatore hardware: semplicemente conta quanti sono i fronti
del segnale applicato al suo ingresso, senza l'intervento del processore.
Qualche esempio di possibile utilizzo:
- Permette di conoscere quanti giri ha fatto una ruota
- Dividendo il numero di impulsi contati per il
tempo trascorso, misura (a volte in modo ingenuo...) la frequenza.
- Collegando all'ingresso del contatore un clock di frequenza nota,
possiamo "contare" il trascorrere del tempo
La struttura di Timer0
Il modo relativamente più semplice per comprenderne il funzionamento di
Tmer0 è
leggere il suo schema, presente sui fogli tecnici, lasciano ad un
secondo momento l'individuazione degli SFR che ne configurano il
funzionamento.

Qualche osservazione sulla struttura di Timer0:
- Può utilizzare un clock esterno, funzionando quindi come
contatore degli impulsi presenti sul pin T0CKI. E' possibile selezionare
il fronte attivo desiderato (T0SE)
- In alternativa, può utilizzare un clock pari ad un quarto della
frequenza di clock per processore
- Può funzionare sia a 8 che a 16 bit (lo schema è relativo alla
versione a 8 bit)
- E' associato ad un prescaler (cioè un divisore di frequenza), permettendogli di generare intervalli
di tempo anche molto lunghi
- Al termine del conteggio (passaggio da 0xFFFF a 0 oppure da 0xFF a 0) può generare un
interrupt
- Il processore può leggere e scrivere il valore interno del contatore
(ma non del prescaler)
In questo primo esempio Timer0 è utilizzato per contare quante volte viene premuto un
pulsante. Tale valore viene continuamente letto e visualizzato su 8 LED.
L'hardware prevede il
collegamento di 8 LED a PORTC e di un pulsante all'ingresso T0CKI. Di
seguito lo schema di principio e, in apertura, una realizzazione pratica con
PICdemo R2.
Il codice è riportato a fine pagina.

La configurazione di Timer0 è in questo caso effettuata usando il solo
registro T0CON (TIMER0 CONTROL REGISTER):
- Configuriamo il contatore per funzionare a 8 bit
T0CONbits.T08BIT = 1; //Timer0 is configured as an
8-bit timer/counter
- Colleghiamo il clock esterno
T0CONbits.T0CS = 1; // Timer0 Clock Source:
transition on T0CKI pin
- Individuiamo il fonte attivo del clock, da basso ad alto
T0CONbits.T0SE = 0; // Timer0 Source Edge: increment
on low-to-high transition
- Disattiviamo il prescaler
T0CONbits.PSA = 1; // TImer0 prescaler is NOT
assigned
- Accendiamo Timer0
T0CONbits.TMR0ON = 1; // Enables Timer0
- Azzeriamo il contenuto del contatore
TMR0L = 0; // Clear Timer0
Una volta avviato Timer0 non è più richiesto alcun intervento del
software ed il conteggio degli impulsi in ingresso ha inizio. Il
main() non fa
altro che leggere e visualizzare continuamente il numero contenuto nel
contatore:
PORTC = TMR0L; // Display timer counter
Vi è un "piccolo" problema: a seconda del tipo di pulsante utilizzato,
vengono conteggiati numerosissimo "impulsi fantasma", quasi un generatore di
numeri casuale. Perché? Qui un
suggerimento
Facciamo lampeggiare un LED
Questo esempio utilizza Timer0 in modalità 16 bit per generare un interrupt
quando raggiunge la fine del conteggio, cioè al raggiungimento del valore
0xFFFF.
Lo schema dell'hardware di riferimento è costituito da un LED (e relativa
resistenza in serie) connesso tra RC0 e massa
Il codice, a fondo pagina, fa lampeggiare i due LED:
- LED2 nel modo già visto,
utilizzando la macro __delay_ms(100)
all'interno di un loop infinito
- LED1 senza utilizzare funzioni di ritardo, ma sfruttando un
interrupt generato da Timer0
Esaminiamo il main() presente nel programma di esempio:
- Le prime righe di codice configurano PORTC, a cui sono collegati i
due LED
- Viene quindi configurato Timer0: 16 bit, clock interno, prescaler
che divide per 4 il clock
T0CONbits.T08BIT = 0; // Timer0 is configured as a
16-bit timer/counter
T0CONbits.T0CS = 0; // Internal instruction cycle clock
T0CONbits.PSA = 0; // Timer0 prescaler is assigned: clock comes from
prescaler
T0CONbits.T0PS = 0b001; // 1:4 prescaler value
- Vene impostato il valore iniziale del conteggio, iniziano dagli otto
bit più significativi. Nel codice di esempio il valore di
TIMER0_VALUE è pari a 3036
TMR0H = TIMER0_VALUE >> 8; // Write 8 MSB to Timer0
TMR0L = TIMER0_VALUE & 0xFF; // Write 8 LSB to Timer0
- Timer0 è configurato per generare un interrupt a bassa priorità al termine
del conteggio
INTCON2bits.TMR0IP = 0; // Set low priority interrupt
INTCONbits.TMR0IE = 1; // Enables the TMR0 overflow interrupt
- Vengono attivati gli interrupt
- Infine Tmer0 viene attivato:
T0CONbits.TMR0ON = 1; // Enable Timer0
- Inizia quindi un ciclo infinito che fa lampeggiare LED2 collegato a PORTC0, usando
la stessa tecnica già vista
L'aspetto forse più interessante è legato al numero magico 3 036.
Esso permette di generare un interrupt dopo un secondo esatto dall'avvio di
Timer0. Infatti:
- la frequenza del clock di Timer0 è pari ad un quarto della frequenza
base del PIC18, in questo esempio f = 1 MHz / 4 = 250 kHz. Un
impulso di clock ha quindi durata 4 µs
- Il prescaler è impostato a 1:4, quindi il periodo del segnale di
uscita viene moltiplicato per 4.
- Il contatore conta da 3 036
(numero caricato dal software) fino a 65 536
(216), per un totale di 62 500 impulsi
Quindi viene generato un interrupt dopo 4 µs ·
4 · 62 500 = 1 s.
Il calcolo del numero magico da caricare in Timer0 può essere fatto con la formula inversa e qualche tentativo (i
risultati corretti possono essere infatti più di uno, tutti però spesso
approssimati) oppure usando uno dei tanti calcolatori che si trovano on-line.
La funzione di interrupt my_isr_l() è piuttosto convenzionale:
- Verifica della sorgente di interrupt (bit TMR0IF)
if (INTCONbits.TMR0IF == 1) // Interrupt from Timer0?
- Riscrive il valore iniziale in Timer0
TMR0H = TIMER0_VALUE >> 8; // Write 8 MSB to Timer0 -
BEFORE!
TMR0L = TIMER0_VALUE & 0xFF; // Write 8 LSB to Timer0
- Modifica di LED1 connesso a PORTC1
LATCbits.LC1 = ~LATCbits.LC1; // Change LEDs status
- Azzera TMR0IF
INTCONbits.TMR0IF = 0; // Clear interrupt status
Occorre osservare che, mentre questo metodo è rigoroso nel generare
ritardi, è solo approssimativo se siamo interessati a segnali di frequenza
rigorosamente costante. Infatti:
- Il valore iniziale del clock è impostato ogni volta all'interno
della routine di interrupt, via software, Questo richiede un certo tempo
aggiuntivo. Si potrebbe,
in teoria, tener conto di questo ritardo e compensarlo, aumentando il
numero da caricare in Timer0
- E' possibile che la routine di interrupt venga a sua volta
interrotta, causando un ritardo non casuale e quindi non eliminabile
Un approfondimento sull'argomento
Data di creazione di questa pagina: settembre 2014
Ultima modifica: 23 maggio 2016
Una pagina simile: PIC18:
il modulo Timer0 (in assembly)