In un altro articolo si è visto, in un modo molto semplice, come settare i registri interessati dal timer tmr0 per generare un interrupt (Link) periodico. Un importante aspetto di questo timer è il corretto settaggio del prescaler. Gli Interrupt generati dai timers sono una importante peculiarità comune alla maggioranza dei microcontrollori e sono largamente utilizzati dove il fattore 'tempo' è importante.
E' necessario subito chiarire cosa un prescaler rappresenti: è semplicemente un divisore di frequenza. Il timer tmr0 è un timer ad 8 bit che normalmente incrementa il suo valore ogni (fosc/4) impulsi di clock per generare un interrupt nel momento in cui il conteggio supera il valore di 255 (FF) e ricominciare da capo da zero (interrupt su overflow). E' normale che questa velocita' sia considerata troppo alta, la generazione di un interrupt ogni fosc/4/256 rappresenterebbe un carico molto pesante per il programma al punto tale da rallentare troppo il picmicro.
![]() |
dove clock = Fosc/4 ed il prescaler settato a 1/8 Se il clock del picmicro è di 4mhz ed il prescaler settato ad 1/8 il timer tmr0 agirà con un clock di 125Khz. |
Per un essere umano ragionare in termini di clock a 125khz avrebbe poco significato e talvolta anche controproducente. Siamo soliti pensare in termini di millesimi di secondo, secondi, di ore e quindi, al fine di poter operare con termini piu' consoni alla nostra mente si deve trovare un modo per convertire e per adeguare i settaggi del timer con lo scopo di ottenere temporizzazioni piu' consone al nostro programma ed a noi piu' comprensibili.
La funzione del prescaler è proprio questa, quella di dividere il clock del timer in modo da fargli effettuare 'x' interrupt in un determinato periodo di tempo. Talvolta l'operazione non è cosi semplice o precisa. Sappiamo che il periodo è l'inverso della frequenza e quindi 125khz = 1/125000 = 8microS. In questo caso esemplificativo verrà generato un interrut ogni 8*256uS = 2,048mS ... per una mente umana un risultato quasi senza senso ed inoltre abbiamo bisogno spesso di temporizzazioni piu' precise.
![]() |
![]() |
La domanda ora è: come ottenere un interrupt ogni 'x' esatti periodi di tempo?
Innanzitutto si deve tenere presente che noi possiamo variare il numero di impulsi necessari at ottenere l'overflow del timer0. Abbiamo detto, infatti, che la variabile tmr0 inizia il conteggio da zero sino a 255 generando un interrupt e ripartendo nuovamente il contegio da zero.
Per variare questo comportamento noi possiamo precaricare un valore alla variabile/timer tmr0 in modo da ridurre il numero di impulsi necessari ad ottenere l'overflow (se poniamo la variabile tmr0=10 ogni inizio di conteggio, ad esempio, saranno necessario solo 246 impulsi).
A questo punto per calcolare tutti i valori sia di prescaler che di offset della variabile tmr0 abbiamo due possibilità: la prima è quella di utilizzare uno dei tanti calcolatori presenti su internet :-) http://eng-serve.com/pic/pic_timer.html ad esempio, oppure possiamo calcolare manualmente i valori sulla base della seguente formula:
freq = (fosc/4) / (256-tmr0value) * Prescaler
in questo caso qualora si desideri un interrupt ogni 1ms con un cristallo di 4 mhz ed un prescale settato ad 1/4 il valore offset da assegnare alla variabile tmr0 dovrebbe essere 6.
Sempre, prima di lasciare la routine interrupt() dovremo ricordare di:
- resettare la variabile tmr0 al valore di offset;
- riabilitare l'interrupt (INTE=1)
- pulire il flag (INTF = 0)
void interrupt()
{
if (INTCON.INTF ==1) // timer 0 interrupt flag
{
PORTB.F0 = ~PORTB.F0; // inverte portb bit0
INTCON.INTF = 0; // pulisce flag
INTCON.INTE = 1; // riabilita l' interrupt
TMR0 = 6; // resetta il tmr0
}
}
void main()
{
TRISB = 0x00;
PORTB = 0;
OPTION_REG.T0CS = 0; // Tmr0 clock interno
OPTION_REG.T0SE = 0; // TMR0 edge in salita
OPTION_REG.PSA = 0; // Assegna il Prescaler al Timer0
OPTION_REG.PS2 = 0; // Setta il Prescaler ad 1/4
OPTION_REG.PS1 = 0;
OPTION_REG.PS0 = 1;
TMR0 = 6; // valore di offset
INTCON = 0; // pulisce il registro di controllo dell'interrupt
INTCON.INTE = 1; // abilita il TMR0
INTCON.INTF = 0; // bit2 pulisce il flag del timer0
INTCON.GIE = 1; // bit7 abilita gli interrupts
do
{
} while(1) ;
}
In molti casi l'uso dei temporizzatori interni al picmicro rappresenta una soluzione ottima per la soluzione di molti problemi di programmazione (vedremo poi anche gli altri timers di cui si dotano molti picmicro); purtroppo pero' in molte altre situazioni, dove occorrono temporizzazioni precise (come in un orologio digitale, ad esempio), l'uso di questi timers non è consigliabile. E' preferibile, infatti, affidarsi a precisi generatori esterni di clock come il Dallas DS1307 o equivalenti.