L'8051 classico dispone di due timer, che possono essere configurati, controllati e letti in maniera indipendente. I timer dell'8051 hanno tre funzioni generali:
Come fa un timer a contare? La risposta e' semplice. Esso conta sempre
incrementando il suo valore. Non importra se viene usato come timer o come
contatore o come generatore di baud rate. E' sempre incrementato dal microcontrollore.
Ovviamente uno degli usi primari dei timer e' quello di misurare il tempo. Parleremo prima di questo uso e poi di quello relativo al conteggio di eventi. Quando un timer e' usato per misurare il tempo e' anche chiamato "interval timer" poiche' esso sta misurando l'intervallo temporale tra due eventi.
Quanto tempo impiega un timer a contare?
Quando il timer e' nel modo "interval timer" ed e' correttamente configurato,
esso viene incrementato di uno ogni ciclo macchina, ovvero 12 impulsi di
clock.
Percio' un timer attivo sara incrementato:
Ovviamente non e' molto utilie sapere che sono trascorsi 0,0542 secondi. Se voi voleste far partire un evento ogni secondo dovreste aspettare il conteggio del timer da 0 a 50.000 per 18,45 volte. Come si puo' aspettare per un tempo non intero? Non e' possibile. Allora proviamo a fare un calcolo imporatnte.
Supponiamo di voler sapere quante volte il timer viene incrementato
ogni 0,05 secondi. Possiamo fare questo semplice calcolo:
Una volta stabilito che esso impiega un ventesimo di secondo e vogliamo eseguire un evento ogni secondo sara' sufficiente che il timer conti da 0 a 46.079 per venti volte; a questo punto eseguite le azioni richieste dall'evento, resettate il timer e aspettate che il timer completi di nuovo il conteggio altre venti volte. In questa maniera eseguirete effettivamente il vostro evento una volta al secondo con una precisione al ventesimo di secondo.
Abbiamo ora un sistema capace di misurare il tempo. Quello che ci serve ora e' sapere come controllare il timer e inizializzarlo in maniera che possiamo dargli le informazioni corrette.
Come precedentemente affermato, l'8051 ha due timer che funzionano ambedue nella stessa maniera. Uno e' chiamato TIMER0 e l'altro TIMER1. I due timer condividono due registri SFR (TCON e TMOD) che servono a controllarli. Ciascun timer pero' dispone di due SFR per loro uso esclusivo (TH0/TL0 e TH1/TL1).
Ai registri SFR abbiamo dato un nome mnemonico per individuarli facilmente, in effetti essi hanno un valore numerico. E' spesso utile conoscere l'indirizzo corrispondente al nome mnemonico del registro SFR. Per quello che riguarda i timer essi sono:
Nome SFR | Descrizione | Indirizzo SFR |
TH0 | Timer 0 Byte Alto | 8Ch |
TL0 | Timer 0 Byte Basso | 8Ah |
TH1 | Timer 1 Byte Alto | 8Dh |
TL1 | Timer 1 Byte Basso | 8Bh |
TCON | Timer Control | 88h |
TMOD | Timer Mode | 89h |
Quando inserite il nome di un registro SFR nell'assembler, esso viene internamente automaticamente convertito in un indirizzo, Per esempio:
Per ricordarvi come funziona la notazione byte basso/alto, ricordatevi di moltiplicare il byte alto per 256 e addizionare il byte basso al risultato ottenuto. Ovvero:
Poiche' solo due byte sono usati per ogni timer risulta evidente che
il massimo valore che un timer puo' assumere e' 65.535 (decimale). Nel
momento in cui viene raggiunto tale valore un ulteriore impulso di clock
lo resettera' o per meglio dire lo mandera' in overflow.
Il registro TMOD e' usato per controllare il modo operativo di ambedue
i timer. Ogni bit del registro fornisce al microntrollore una specifica
informazione che riguarda il funzionamento del timer. I 4 bit piu' significativi
(da 4 a 7) sono relativi al timer 1 mentre i 4 bit meno significativi (da
0 a 3) hanno il medesimo significato ma sono relativi al timer 0.
I bit del registro TMOD hanno il seguente significato :
TMOD (89h) SFR
Bit | Nome | Spiegazione della funzione | Timer |
7 | GATE1 | Quando questo bit e' settato, il timer 1 e' attivo solo quando il pin P3.3 e' nello stato alto. Se tale bit e ' resettato il timer 1 sara' svincolato dallo stato del pin P3.3. | 1 |
6 | C/T1 | Quando questo bit e' settato il timer 1 contera' il numero degli eventi sul pin T1 (P3.5). Se il bit e' resettato il timer 1 verra' incrementato ogni ciclo macchina. | 1 |
5 | T1M1 | Bit 1 di modo del timer 1(vedi sotto) | 1 |
4 | T1M0 | Bit 0 di modo del timer 1(vedi sotto) | 1 |
3 | GATE0 | Quando questo bit e' settato, il timer 0 e' attivo solo quando il pin P3.2 e' nello stato alto. Se tale bit e ' resettato il timer 0 sara' svincolato dallo stato del pin P3.2. | 0 |
2 | C/T0 | Quando questo bit e' settato il timer 0 contera' il numero degli eventi sul pin T1 (P3.5). Se il bit e' resettato il timer 0 verra' incrementato ogni ciclo macchina. | 0 |
1 | T0M1 | Bit 1 di modo del timer 0(vedi sotto) | 0 |
0 | T0M0 | Bit 0 di modo del timer 0(vedi sotto) | 0 |
Come potete vedere dalla tabella di sopra, quattro bit, due per ciascun
timer sono usati per specificare il modo operativo. I modi operativi sono:
TxM1 | TxM0 | Timer Mode | Descrizione |
0 | 0 | 0 | Timer a 13-bit |
0 | 1 | 1 | Timer a 16-bit |
1 | 0 | 2 | Timer a 8-bit con auto-reload |
1 | 1 | 3 | Timer in splitted mode |
Il modo "0" corrisponde al funzionamento del timer a 13 bit. Questo
e' un retaggio del passato che e' stato mantenuto per avere la compatibilita'
con il suo predecessore: l'8048.
Generalmente questo modo di funzionamento non viene utilizzato in nuovi
sviluppi.
In questa modalita' di funzionamento, TLx contera' da 0 a 31.
Quando TLx viene incrementato da 31, esso si resettera' e incrementera'
THx. Percio' effettivamente, soltanto 13 bit del timer a 2 byte sono utlizzati:
i bit 0-4 di TLx e i bit 0-7 di THx. Questo significa anche in definitiva
che il timer puo' contare solo fino ad un valore pari a 8192. Se caricate
il timer con il valore 0, esso andra' in overflow e quindi a 0 dopo 8192
cicli macchina.
Il modo "1" corrisponde al funzionamento del timer a 16 bit. Questo e' un modo molto utilizzato. TLx e' incrementato da 0 a 255. All'overflow di TLx, THx viene incrementato di 1. Tenuto conto che e' un timer a 16 bit, esso puo' assumere 65536 valori distinti. Se caricate il timer con il valore 0, esso andra' in overflow e quindi a 0, dopo 65536 cicli macchina.
Il modo "2" corrisponde al funzionamento del timer ad 8 bit con auto-reload. Quando il timer e' configurato in modo 2, THx contiene il valore che deve essere caricato in TLx quando va in overflow. TLx inizia a contatore in avanti. Quando raggiunge 255 e viene ulteriormente incrementato invece di tornare a 0 (come nei modi 0 e 1), assume il valore caricato in THx.
Se per esempio TH0 contiene il valore FDh e TL0 il valore FEh il conteggio procedera' nella seguente maniera:
Ciclo Macchina | Valore di TH0 | Valore di TL0 |
1 | FDh | FEh |
2 | FDh | FFh |
3 | FDh | FDh |
4 | FDh | FEh |
5 | FDh | FFh |
6 | FDh | FDh |
7 | FDh | FEh |
Notate che il valore di TH0 non cambia mai e TL0 viene incrementato.
Una volta raggiunto un valore pari a FFh, al successivo ciclo macchina
TL0 assume lo stesso valore di TH0.
Ma qual'e' il vantaggio di usare tale modo di funzionamento? Ipotizziamo
che vogliate che il timer assuma dei valori compresi tra 200 e 255. Nel
modo 0 e 1 siete costretti a verificare da programma quando il timer va
in overflow e quindi settarlo al valore 200. Questo lavoro richiede tempo
prezioso e non vi garantisce un elevata accuratezza a meno che non vogliate
rimanere a controllare solo lo stato del timer. Quando usate il modo 2,
non dovete preoccuparvi di tutto questo, poiche' l'hardware del microcontrollore
lo fara' automaticamente per voi.
Il modo 2 e' usato molto spesso per generare il baud rate. Ne parleremo
piu' approfondidamente nel capitolo dedicato alla Comunicazione Seriale.
Il modo "3" e' il cosidetto modo a timer separati. Quando il timer 0 e' configurato per lavorare in questa modalita', si trasforma in due timer a 8 bit separati. Per capirci meglio, il timer 0 diventa TL0 e il timer 1 diventa TH0. Ambedue i timer contano da 0 a 255 e dopo l'overflow tornano a 0. Tutti i bit relativi al Timer 1 vengono assegnati a TH0.
Una volta che il Timer 0 e' configurato in modo split, il vero Timer 1 (cioe' TH1 e TL1) puo' essere configurato nei modi 0, 1, 2 come sempre, ma non puo' essere abilitato/disabilitato poiche' i suoi bit di controllo sono assegnati a TH0. Allora, il vero Timer 1 funzionera' in maniera libera e si incrementera' ad ogni ciclo macchina senza condizioni.
L'unico vero uso di questa modalita' e' quello per cui sia necessario avere due timer separati e, in aggiunta, un generatore di baud rate. In questo caso il Timer 1 viene utilizzato come baud generator e i due registri TH0 e TL0 come se fossero due timer separati.
Come ultimo c'e' un altro registro SFR che controlla i due timer e fornisce importanti informazioni sul loro funzionamento. Il registro TCON ha la seguente struttura:
TCON (88h) SFR
Bit | Nome | Indirizzo a Bit | Spiegazione della funzione | Timer |
7 | TF1 | 8Fh | Timer 1 Overflow. Questo bit e'settato quando il timer 1 e' andato in overflow | 1 |
6 | TR1 | 8Eh | Timer 1 Run. Quando questo bit viene settato il timer 1 e' abilitato, altrimenti e' fermo. | 1 |
5 | TF0 | 8Dh | Timer 0 Overflow.Questo bit e'settato quando il timer 0 e' andato in overflow | 0 |
4 | TR0 | 8Ch | Timer 0 Run. Quando questo bit viene settato il timer 0 e' abilitato, altrimenti e' fermo. | 0 |
Come potete notare, abbiamo definito solo 4 degli 8 bit. Cio' e' dovuto al fatto che gli altri 4 bit del registro TCON non hanno nulla a che vedere con i timer. Essi sono relativi agli interrupt e quindi ne discuteremo nel capitolo dedicato a questo argomento.
Nella tabella precedente, c'e' un ulteriore colonna che riguarda l'indirizzo a bit. E' stata riportata perche' tale registro puo' essere indirizzato a bit, cio' vuol dire che, se vogliamo settare il bit TF1, che e' il bit piu' significativo di TCON, potremmo eseguire la seguente istruzione:
Ora che conoscaiamo tutti i registri SFR relativi ai timer, siamo in grado di scrivere il codice per inizializzare un timer e farlo partire.
Come vi ricordate, bisogna per prima cosa decidere in quale modalita' il timer verra' utilizzato. In questo caso scegliamo un modo a 16 bit che non ha alcuna dipendenza da pin esterni.
Dobbiamo per prima cosa, inizializzare il registro TMOD. Se lavoriamo con il timer 0 useremo i 4 bit meno significativi di TMOD. I primi due bit GATE0 e C/T0 vanno ambedue posti a 0 poiche' vogliamo che il timer sia indipendendente da pin esterni. Il modo a 16-bit corrisponde al modo "1" e quindi dobbiamo: resettare T0M1 e settare T0M0. In breve l'unico bit da settare e' il bit 0 di TMOD. Per inizializzare il timer eseguiremo la seguente istruzione:
Ci sono due maniere per leggere lo stato di un timer a 16-bit in funzione della specifica applicazione. Voi potete o leggere il valore attuale a 16 bit oppure stabilire soltanto se il timer e' andato in overflow.
Se il timer e' configurato nel modo ad 8 bit, sia nel modo con auto-reload che in split mode, la lettura del valore e' effettuata semplicemente leggendo il byte corrispondente al timer che state usando.
Quando pero' state usando il modo a 13 o 16 bit la lettura e' leggermente piu' complessa. Cio' e' dovuto al fatto che mentre state leggendo il valore del byte meno significativo del timer e questo valore e' pari a 255, potreste leggere un valore errato del byte piu' significativo che, in quel momento si sta incrementando. Per esempio se lo stato del timer e' 14/255 (byte alto = 14, byte basso = 255), potremmo leggere il valore 15/255 nel momento in cui il timer sta cambiando di stato al valore 15/0.
Quale potrebbe essere la soluzione? In verita' essa non e' molto complessa. Dovreste leggere prima il byte alto, poi quello basso e rileggere nuovamente il byte alto confrontandolo con il valore letto precedentemente. Sei i due valori coincidono, il timer non ha cambiato stato durante la lettura e quindi il valore letto e' utile, altrimenti va ripetuto il ciclo precedente. Il codice sara' del tipo:
REPEAT: | MOV A,TH0 |
MOV R0,TL0 | |
CJNE A,TH0,REPEAT | |
... |
Rivelare l'Overflow di un timer
In alcuni casi e' sufficiente conoscere soltanto se il timer e' stato resettato o meno. Questo quando non interessa conoscere il valore del timer ma solo sapere se il timer ha raggiunto il massimo del conteggio (overflow). Una volta che il timer raggiunge il valore di 255, il microcontrollore lo riporta automaticamente a zero e setta il bit TFx corrispondente nel registro TCON. Per cui quando il bit TF0 e' settato vuol dire che il timer 0 ha raggiunto l'overflow e alla stessa maniera se TF1 e' settato vuol dire che il timer 1 e' tornato a zero.
Possiamo usare questo approccio per costringere il programma ad eseguire delle operazioni ad intervalli di tempo prestabiliti. Riprendiamo pure l'esempio precedente nel quale era richiesto il conteggio da 0 a 46.079 per un periodo di un ventesimo di secondo. Se vogliamo impostare un tempo pari a quello indicato, tenuto conto che il timer segnala l'overflow quando passa per lo zero, dobbiamo settare il timer ad un valore pari a 65.536 meno 46.079, cioe' 19.457. Quindi per eseguire una pausa di un ventesimo di secondo usiamo il seguente frammento di codice:
Misurare la durata di un evento
L'8051 dispone di un altro utile modo che puo' essere usato per misurare la durata di un evento.
Facciamo un esempio: vogliamo risparmiare energia in un ufficio e vogliamo conoscere quanto tempo una lampada rimane accesa durante la giornata. Quando la lampada e' accesa vogliamo conteggiare il tempo. Quando la lampada e' spento fermiamo il conteggio. Una possibile soluzione e' quella di collegare lo stato dell'interruttore ad un pin del micro. A questo punto il programma deve continuamente monitorare lo stato del pin utilizzato e attivare o disattivare il timer in funzione dello stato del pin stesso. Anche se il sistema funziona correttamente, In realta', l'8051 fornisce un metodo piu' semplice di quello appena prospettato.
Se diamo un'occhiata al registro TMOD, c'e' un bit chiamato GATE0. Finora
abbiamo lasciato sempre il bit a zero perche' volevano che il timer continuasse
a girare indipendentemente dallo stato dei fili esterni. Adesso pero',
tale capacita' ci torna utile. Tutto quello che serve e' di collegare lo
stato dell'interruttore al pin INT0 (P3.2) dell'8051 e settare il bit GATE0.
A questo punto, il timer eseguira' il conteggio solo quando il pin P3.2
si trovera' nello stato alto (interruttore on, luce accesa). Quando il
pin P3.2 si trovera' nello stato basso (interruttore off, luce spenta)
il timer si blocchera' automaticamente.
USARE I TIMER COME CONTATORI
DI EVENTI
Finora abbiamo discusso di come un timer possa tener traccia del tempo trascorso, ma l'8051 ci permette di usare il timer come contatore di eventi.
Quando puo' essere utile questa funzione? Supponiamo di avere un sensore nel mezzo di una strada che invia un impulso ogni volta che passa un'auto. Questo potrebbe essere utilizzato per misurare l'intensita' del traffico su quella strada. Potremmo allora collegare il sensore ad un pin di I/O dell'8051 e monitorare lo stato del pin per contare il numero degli impulsi. Fare questo non e' eccessivamente difficile, ma richiede l'utilizzo di un po' di codice. Supponiamo per esempio di aver collegato il sensore al pin P1.0; il codice che conta il numero di auto che passano sara' del tipo:
JNB P1.0,$ | ; Aspetta che un'auto attivi la linea. |
JB P1.0,$ | ; La linea e' ora attiva, la macchina sta passando. |
INC COUNTER | ; La linea e' bassa, quindi la macchina e' passata, possiamo contare l'evento. |
Fortunamente l'8051 ci fornisce un'alternativa per usare il timer come contatore di eventi senza doverci preoccupare di come realizzarlo in software. E' estremamente semplice e indolore: basta configurare un bit addizionale.
Per esempio vogliamo utilizzare il Timer 0 per conteggiare il numero delle auto che transitano. Allora, se diamo uno sguardo al registri TCON possiamo notare la presenza di un bit chiamato C/T0, bit 2 di TCON (TCON.2). Se tale bit e' settato, il timer 0, invece di incrementare se stesso ad ogni ciclo macchina, effettuera' il monitoring della linea P3.4. In questa maniera incrementera' se stesso ogni volta che la linea passa dal valore alto a quello basso. A questo punto e' sufficiente collegare il sensore sulla linea P3.4 e configurare il registro TCON opportunamente.
E' importante pero' notare che l'8051 controlla lo stato della linea P3.4 una volta per ciclo macchina. Questo significa che, se la frequenza con la quale cambia lo stato del pin e' troppo elevata, l'8051 non riuscira' piu' a contare il numero di eventi in maniera corretta. Piu' precisamente, l'8051 riesce a contare un numero di eventi ad un massimo di un ventiquattresimo della frequenza di clock. Cio' vuol dire che, se per esempio usiamo un quarzo che oscilla a 12 MHz, esso riesce a conteggiare fino a 500.000 eventi al secondo (12 MHz * 1/24 = 500.000).