Tutorials - PIC18 by Data Sheet

 

pag.123 - TMR0


Timer 0

Il Timer0 (TMR0) è comune a tutti i PIC. 
Nei PIC18 sono presenti alcune caratteristiche che lo differenziano dai PIC16:

  • possibilità di selezionare da software operazioni con conteggio a 8 bit oppure a 16 bit 

  • prescaler programmabile a 8 bit dedicato interamente ( e non condiviso con WDT)

  • più ampio range del prescaler

  • un unico registro di controllo contiene tutti i bit necessari alla gestione del timer

Inoltre, come per tutte le sorgenti di interrupt della serie PIC18, anche Timer0 può essere impostato per generare richieste di interrupt a due livelli di priorità.

Il registro T0CON è la via per controllare  le funzioni del Timer0, compreso il prescaler :

T0CON

bit

7

6

5

4

3

2

1

0

nome

TMR0ON

T08BIT

T0CS

T0SE

PSA

T0PS2

T0PS1

T0PS0

funzione

R/W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

default

1

1

1

1

1

1

1

1

  • bit 7     TMR0ON : controllo on/off di Timer0
                 1 = Timer 0 attivo
                 0 = Timer 0 disabilitato

  • bit 6     T08BIT : operazione a 8 o 16 bit
                 1 = Timer 0 a 8 bit
                 0 = Timer 0 a 16 bit

  • bit 5     T0CS : sorgente del clock
                 1 = Transizione sul pin T0CKI
                 0 = Clock interno

  • bit 4     T0SE : edge detect del clock da T0CKI
                 1 = high to low
                 0 = low to high

  • bit 3     PSA : assegnazione prescaler
                 1 =  Timer0 senza prescaler
                 0 =  Timer0 con prescaler

  • bit 2:0  T0PS2:0 :  impostazione prescaler
                 111 =  1:256
                 110 =  1:128
                 101 =  1 :64
                 100 =  1:32
                  011=  1:16
                  010 = 1:8
                  001 = 1:4
                  000 = 1:2

I valori di default al POR sono indicati nella tabella.

Va notato che al RESET, TMR0 è :

  • attivo

  • in modo a 8 bit (compatbiltà con i Mid Range)

  • con ingresso da T0CKI (falling edge)

  • senza prescaler (che, comunque, è impostato a 1:256).

I registri collegati all' uso di TMR0 sono i seguenti :

Nome

bit 7

bit 6

bit 5

bit 4

bit 3

bit 2

bit 1

bit 0

TMR0L

Registro low

TMR0H

Registro high

INTCON

GIE/GIEH

PEIE/GIEL

TMR0IE

 

 

TMR0IF

 

 

T0CON

TMR0ON

T08BIT

T0CS

T0SE

PSA

T0PS2

T0PS1

T0PS0

TRISA

 

 

 

RA4

 

 

 

 

I bit non indicati non sono usati dalla gestione di TMR0.

 


TIMER0 a 8 e 16 bit

I diagrammi seguenti schematizzano il funzionamento di Timer0 nel funzionamento a 8 bit :

e in quello a 16 bit :

Il front end del timer è identico in entrambi i casi, cambia solamente la struttura del registro di conteggio.

I diagrammi rappresentano le funzioni logiche di ogni bit di controllo.
Il clock del timer può arrivare dal pin T0CKI oppure dal clock interno (Fosc/4), a seconda del valore del bit T0CS.
In più, il bit T0SE seleziona il fronte attivo del segnale esterno, per permettere una più precisa sincronizzazione con eventi particolari.
A seconda del bit PSA, il clock viene passato così come sta oppure diviso dal prescaler, il cui fattore di divisione dipende dai bit T0PS2:0.

Nel caso di clock esterno, è presente un sistema di sincronismo che impone un ritardo di 2 Tosc. Questo non è introdotto nel caso di uso del clock interno, con il quale il sincronismo è già evidentemente presente.
Il segnale così condizionato passa al registro di conteggio, che, come in tutti i timer dei PIC è a crescere.

Nel modo a 8 bit il Timer0 dispone del solo TMR0L, che è del tutto compatibile con quello dei PIC base e mid-range; possiamo dire che si tratta di una "emulazione" che consente di mantenere uguale il software già scritto. E questa modalità è proprio quella di default.
Nel modo a 16 bit, proprio degli enhanced, si ampliano le possibilità operative del timer, che dispone di due registri a 8 bit in cascata. Questo modo va imposto da programma, modificando il bit T08BIT nel registro di controllo del timer.


Il clock di Timer0 .

Questo può derivare dal clock di sistema o da un clock esterno. 
La sorgente del clock esterno è il pin T0CKI, solitamente corrispondente a RA4; la sorgente interna è l' oscillatore principale del processore diviso 4, ovvero la frequenza di clock di un ciclo.
La scelta della sorgente è effettuata con il bit T0CS (T0CON<5>). 
Se T0CS si imposta a 1, scegliendo la sorgente esterna, RA4 (PORTA<4>) non è utilizzabile per altre funzioni e va definito come ingresso portando a 1 il bit 4 di TRISA.
In questo caso si tratta di un ingresso a trigger di Schmit per ottenere un segnale da conteggiare quanto più possibile pulito.

Usando la sorgente di clock esterna è possibile selezionare su quale fronte (salita o discesa) far scattare il conteggio. Questa selezione si effettua con il bit T0SE : se T0SE = 1 l' avanzamento del conteggio avverrà sul fronte di discesa del segnale esterno (falling edge), altrimenti su quello di discesa (rising edge). Il segnale esterno di clock dovrà comunque rispettare i limiti indicati nel foglio dati.

I registri di conteggio.
TIMER0 è del tipo a incremento , ovvero parte da 00 e termina a 0xFF (0xFFFF per il modo a 16 bit).
Nella modalità a 8 bit viene impiegato un solo registro di conteggio (TMR0L) mentre in quella a 16 bit vengono utilizzati due registri (TMR0H:L). 

Va notato che il registro di peso maggiore (TMR0H) non è  un registro reale, ma un buffer, in quanto il vero TMR0H non è raggiungibile nè in lettura nè in scrittura.
In lettura, il buffer è aggiornato con il contenuto reale di TMR0H  quando viene letto TMR0L : questo permette di avere disponibile la situazione effettiva del conteggio, una fotografia completa al momento della lettura, senza la necessità di verificare se lo stato dei contatori è valido (dato che una lettura in due tempi diTMR0H e TMR0L non arresta il contatore, che continua ad incrementare).
Così pure la scrittura di TMR0H è effettuata nel buffer e il trasferimento nel counter reale avverrà al momento della scrittura di TMR0L; questo assicura la partenza del conteggio da un valore preciso in un preciso istante.

In base a quanto sopra, allora, la corretta sequenza di lettura sarà quella di

  •  leggere prima TMR0L

  •  e poi TMR0H

mentre per la scrittura, 

  • si scriverà prima TMR0H  

  • e poi TMR0L

E' quindi da ricordare che, se viene scritto solo TMR0H senza riscrivere anche TMR0L, il valore eventualmente impostato per TMR0H non avrà effetto. 

Così pure leggendo prima TMR0H non si avrà il giusto valore conteggiato in quel momento.

 


Un esempio di inizializzazione

Un esempio di inizializzazione di Timer0 a 16 bit con interrupt senza priorità :

; 16 bit timer, internal clock, prescaler = 1:256
    movlw   b'00000111'      
    movwf   T0CON
    movlw   0x05         ; load start value
    movwf   TMR0H
    movlw   0xAA
    movwf   TMR0L        ; and write the buffer

    bsf     T0CON,TMR0ON ; enabled Timer0
   
bcf     INTCON, T0IF ; safety flag clear
    bsf     INTCON,GIE   ; enable general interrupts

Da notare che se viene scritto un registro TMR0x, il conteggio è sospeso per due cicli e l' utente deve tenerne conto nella stesura del programma.


Il Prescaler

Il prescaler di Timer0 è interamente dedicato a questo e non è condiviso con altre funzioni. 

Viene abilitato portando a zero il bit PSA (T0CON<3>) e il valore del rapporto di divisione è determinato dai bit T0PS2:0 (T0CON<2:0>).

I registri di conteggio vengono incrementati ad ogni transizione del clock filtrata dal prescaler, ovvero se il prescaler è programmato su 128, l' incremento di un punto del contenuto dei registri TMR0x avviene ogni 128 impulsi.  Il contenuto del prescaler non è accessibile, mentre il rapporto di pre divisione è leggibile e scrivibile in qualsiasi momento. Il prescaler, sia come assegnazione che come divisore è interamente sotto controllo del software e può essere variato a piacere durante l' esecuzione del programma.

Va notato, però, che, assegnando il prescaler attraverso PSA, il contenuto di TMR0H:L viene azzerato.
Per contro, qualunque operazione di scrittura di TMR0L (clrf, movf, bsf, ecc) azzera il contenuto di conteggio eventualmente presente nel prescaler (non il suo rapporto di divisione).
Scrivendo TMR0H, dato che si tratta di un buffer che non viene trasferito se non a seguito della scrittura di TMR0L, il contenuto del prescaler non viene modificato.


Interrupt

Timer0 può essere una fonte di richiesta di interrupt; questo è generato quando il registro di conteggio passa da 0xFF a 0x00 in modo a 8 bit o da 0xFFF a 0x0000 in modo 16 bit.

Questo overflow setta il bit TMR0IF (INTCON<2>). La richiesta di interrupt può essere mascherata con il bit TMR0IE (INTCON<5>). Prima di riabilitare l' interrupt, TMR0IF deve essere portato a zero da programma, altrimenti, come al solito, all' uscita della gestione dell' interrupt si genererà immediatamente una nuova chiamata inaspettata.

Dato che TMR0 viene spento in modalità Sleep, non può essere una sorgente di wake-up

Per utilizzare il Timer0 occorrerà :

  1. impostare PSA a seconda sia richiesto o no il prescaler
    scegliere l' eventuale valore di divisione
     

  2. scegliere la sorgente del clock, interna o esterna
     
    Ad esempio:

    ; 8 bit timer, internal clock, prescaler = 1:256
        movlw b'01001111'      
        movwf T0CON

  3. Attivare il timer.
    Ad esempio:
     
    ; Timer0 on
        bsf  T0CON, TMR0ON

La gestione interrupt del Timer0 richiede poi :

  • l' abilitazione del bit TMR0IE (INTCON<5>), che va posto a 1 .

  • selezionare la priorità dell' interrupt, se richiesto. Il bit interessato è TMR0IP (INTCON2 <2>). E' a 1 se si desidera l' alta priorità, altrimenti va a posto a 0. 
    Se è stato scelto di utilizzare interrupt con priorità, occorre aver settato il bit IPEN (RCON<7>) ; in caso contrario viene impiegato un sistema di interrupt senza livelli di priorità e l' impostazione di
    TMR0IP è indifferente.

Il flag di identificazione è TMR0IF (INTCON<2>) che va a 1 a seguito all' overflow del counter e va cancellato da software.

L' interrupt di Timer0 fa riferimento al registro INTCON e quindi è considerata una interruzione non periferica 

Ricordiamo che le interruzioni periferiche dipendono dai registri PIRx, PIEx e IPRx e da PEIE. 
Per questo motivo l' attivazione dell' interrupt relativo implica solo GIE .

Esempi di attivazione dell' interrupt di Timer0 :

; abilitazione e attivazione interrupt senza priorità
   bcf INTCON,T0IF      ;clear flag per sicurezza
   bsf INTCON,T0IE      ;abilita interrupt di Timer0
   bsf INTCON,GIE       ;attiva l' interrupt generale

; abilitazione e attivazione interrupt a priorità alta
    bsf RCON, IPEN       ;attivare interrupt a priorità
  
bsf INTCON2, TMR0IP  ;selezionare priorità alta
   bcf INTCON,T0IF      ;clear flag per sicurezza
   bsf INTCON,T0IE      ;abilita interrupt di Timer0
   bsf INTCON,GIEH      ;attiva l' interrupt generale H


; abilitazione e attivazione interrupt a priorità bassa
   bsf RCON, IPEN       ;selezionare interrupt a priorità
  
bcf INTCON2, TMR0IP  ;selezionare priorità bassa
   bcf INTCON,T0IF      ;clear flag per sicurezza
   bsf INTCON,T0IE      ;abilita interrupt di Timer0
   bsf INTCON,GIEH      ;attiva l' interrupt generale H
  
bsf INTCON,GIEL      ;attiva l' interrupt generale L


TIMER0 con clock esterno

La sorgente del clock di conteggio può essere interna o esterna; questo dipende dal valore del bit T0CS nel registro T0CON. Portando questo bit a 1 la sorgente del clock diventa il pin T0CKI, solitamente collegato con RA4.

Se non viene utilizzato il prescaler, la sincronizzazione tra l' ingresso T0CKI end il clock interno è possibile rispettando alcuni limiti nel segnale di ingresso ( foglio dati par 13.1).
Se si utilizza il prescaler , il clock esterno è diviso dal prescaler stesso, che ha una uscita simmetrica; il clock, diviso dal prescaler, è necessario abbia un periodo di almeno  4 TSCLK diviso per il valore impostato del prescaler.(parametri 40,41 e 42)


TIPS & TRICKS

Le tabelle seguenti riportano i limiti di tempo in funzione del clock, del prescaler e della modalità a 8 o a 16 bit. Se non diversamente indicato i tempi sono in millisecondi.

Xtal 4MHz - Tcyc 1 us

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.255

0.512

1

2

4

8

16

32

64

16

65

131

262

524

1 s

2 s

4 s

8 s

16 s

Xtal 8 Mhz - Tcyc 500 ns

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.127

0.255

0.512

1

2

4

8

16

32

16

32

65

131

262

524

1 s

2 s

4 s

8 s

Xtal 10 Mhz - Tcyc 400 ns

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.102

0.204

0.409

0.819

1.6

3.2

6.5

13

26

16

26

52

105

209

419

839

1.6 s

3.3 s

6.7 s

Xtal 16 MHz - Tcyc

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.064

0.127

0.255

0.512

1

2

4

8

16

16

16

32

65

131

262

524

1 s

2 s

4 s

Xtal 20 MHz - Tcyc 200 ns

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.051

0.102

0.204

0.409

0.819

1.6

3.3

6.5

13

16

13

26

52

105

209

419

839

1.6 s

3.3 s

Xtal 40 MHz - Tcyc 100 ns

Prescaler

no

1:2

1:4

1:8

1:16

1:32

1:64

1:128

1:256

Bit
mode

8

0.025

0.051

0.102

0.204

0.409

0.819

1.6

3.3

6.5

16

6.5

13

26

52

105

209

419

839

1.6 s

I valori indicati sono approssimati. Lo scopo delle tabelle non è quello di calcolare con precisione il tempo, ma quello di permettere di valutare rapidamente le possibilità del timer ad un certo clock.
Ad esempio, volendo ottenere ritardi di 10 ms con un quarzo da 20MHz, è possibile usare il Timer 0 in modalità a 8 bit con un prescaler di 256 oppure in modalità a 16 bit senza prescaler.
Ancora, volendo tempi di 1,8 secondi con il clock di 40MHz, se ne nota l' impossibilità, in quanto il massimo ottenibile è 1,6 secondi.

La scelta del timer a 8 o a 16 bit, quando possibile in entrambi i modi in relazione al tempo desiderato, va a preferenza sulla modalità a 8 bit, dove è necessario ricaricare un solo registro. La modalità a 16 bit, a parte la diversa estensione di tempo possibile, permette una maggior raffinatezza sul valore ottenuto, ma richiede il caricamento di due registri.

Il calcolo del tempo in secondi per la modalità ad 8 bit può essere effettuato con la seguente espressione:

Time = (256 - InitTMR0 * prescaler) / (XtalFreq/4)

Oppure, volendo il valore da inserire nei registri del timer per ottenere un determinato tempo :

InitTMR0 = 256 - ( Time * XtalFreq ) / ( 4* Prescaler)

Per la modalità a 16 bit, le formule diventano :

Time = (65535 - InitTMR0 * Prescaler) / (XtalFreq/4)

InitTMR0 = 65535 - ( Time * XtalFreq ) / ( 4* Prescaler)

 


Alcuni esempi pratici

Su un PIC18F2321, operante a 8Mhz , si desidera generare cadenze a 1/10/100/1000 ms.
Dalle tabelle sopra la cosa risulta possibile attraverso l' uso del prescaler e la modalità a 16 bit.

#define XTAL_FREQ  8000000          ;crystal frequency 
#define CLK        (XTAL_FREQ / 4)  ;crystal divided by four 
#define T0PS       128              ;selected prescaler
#define T0CS       (CLK/T0PS)       ;clock to timer 0
 InitT0_1s    = 65535 - ( 1 * CLK / T0PS)
 InitT0_100ms = 65535 - ( 0.1 * CLK / T0PS)
 InitT0_10ms  = 65535 - ( 0.01 * CLK / T0PS)
 InitT0_1ms   = 65535 - ( 0.001 * CLK / T0PS)

A 8MHz di clock, il clock delle istruzioni CLK è 8 / 4 = 2 MHz, ovvero 500 ns per istruzione.
Con un prescaler di 128, il clock T0CS è 2000000 / 128 = 15625 Hz, ovvero 64 us per impulso.
Per ottenere il tempo di 1 secondo occorrono 1000000 / 64 = 15625 impulsi.
Con un conteggio a 16 bit, il numero massimo conteggiabile è 65535. La predisposizione del valore da caricare nei registri del timer sarà 65535 - 15625 = 49910.
Infatti il timer conta a salire, per cui , per arrivare all' overflow a partire dal valore pre caricato di 49910 saranno necessari proprio 15625 impulsi.
Un modo ragionevole per indicare all' Assembler i valori da caricare nei due registri può essere il seguente :

t_1ms_h   equ  InitT0_1ms/256
t_1ms_l   equ  InitT0_1ms % 256
t_10ms_h  equ  InitT0_10ms/256
t_10ms_l  equ  InitT0_10ms % 256
t_100ms_h equ  InitT0_100ms/256
t_100ms_l equ  InitT0_100ms % 256
t_1sec_h  equ  InitT0_1s/256
t_1sec_l  equ  InitT0_1s % 256

Il caricamento dei registri :

t_1_ms movlw   t_1ms_h
       movwf   TMR0H
       movlw   t_1ms_l
       bra     t_end
t_10_ms movlw  t_10ms_h
       movwf   TMR0H
       movlw   t_10ms_l
       bra     t_end
t_100_ms movlw t_100ms_h
       movwf   TMR0H
       movlw   t_100ms_l
       bra     t_end
t_1_sec movlw  t_1sec_h
       movwf   TMR0H
       movlw   t_1sec_l
t_end  movwf   TMR0L

e per l' inizializzazione e l' uso del timer :

     movlw  b'00000110'
; 1:128 prescale, internal clock, 16 bit mode, timer off

     movwf  T0CON
     bcf    INTCON,TMR0IF ; clear the overflow bit
    
nop
     btfsc  INTCON,TMR0IF
      bra   $-2           ; make sure overflow bit is clear
     bsf    T0CON,TMR0ON  ; enable timer
     return


Temporizzazioni di precisione

Nella gestione del tempo, nei casi in cui sia richiesta una elevata precisione, occorre considerare che l' operazione di ricarica dei registri di conteggio arresta il timer per due cicli e che una gestione anche minima richiede alcune istruzioni dal momento in cui viene intercettato l' overflow del contatore a quando vengono ricaricati i registri. Quindi va tenuto conto anche di queste variabili.

In generale un timer viene spesso usato in continuo, ovvero una volta raggiunto l' overflow, i registri di conteggio vengono ricaricati e così via,
Un primo problema è quello relativo a questa manovra : i registri vengono ricaricati dopo l' overflow, quindi, per una precisa cadenza, occorre tenere conto dei cicli impiegati in questa fase.
Infatti il timer non si arresta mai se non viene stoppato con l' apposito bit di T0CON , quindi va caricato non con un valore teorico, ma con un  esatto valore che compensi il tempo trascorso tra l' overflow e la ricarica dei registri.

A questo va aggiunto che il Timer0 a seguito della ricarica dei registri, si arresta per 3 cicli se non viene usato il prescaler e per 4 se viene usato. Inoltre , usando il prescaler, il suo contenuto viene azzerato alla scrittura dei registri di TMR0 (attenzione a non fare confusione : viene azzerato il contenuto già conteggiato, non il valore impostato come fattore di predivisione).

La compensazione di questi tempi può essere difficoltosa nel caso in cui si acceda al timer in polling, mentre se si utilizza l' interrupt, la valutazione è più precisa. Anche in questo caso, però, è opportuno prendere alcune precauzioni :

  • La  gestione dell' interrupt di TMR0 dovrebbe essere prioritaria sulle altre, in modo da semplificare la valutazione delle istruzioni spese prima della ricarica dei registri. Questo può essere ottenuto facilmente attribuendo la priorità alta al solo Timer0. Dovendo attribuire priorità uguali anche ad altre sorgenti di interrupt, il calcolo del tempo di latenza dell' overflow di TMR0 può diventare difficile.

  • Non si deve disabilitare l' interrupt generale azzerando GIE/GIEH=0 se è richiesta una valutazione precisa del tempo. Infatti, se è necessario disattivare l'interrupt per scrivere in EEPROM, non è possibile determinare esattamente il tempo  in cui l'interrupt resta disabilitato.

  • Ovviamente il tempo massimo di accesso e svolgimento delle routine di interrupt deve essere sufficientemente minore del ciclo dei timer, altrimenti si perde un overflow.

  • Inoltre, se si manda il processore in sleep, TMR0 viene spento !!

Una possibile proposta per avere una cadenza di elevata precisione è quella di arrestare il timer, caricare il valore calcolato e poi riavviarlo.
Ad esempio, per un conteggio a 16 bit in cui la cadenza deve essere di 10000 istruzioni :

;aggiusta_T0 = ... ;numero di ricarica del timer tenendo conto di tutte 
;le variabili
Init_T0     ; = D'65536'-D'10000' + aggiusta_T0

    bcf     T0CON,TMR0ON       ; timer stop
    movlw   high(Init_T0)      ; carica valore calcolato
    movwf   TMR0H              ; nel buffer alto
    movlw   low(Init_T0)       ; scrive registro basso
    movwf   TMR0L              ; e trasferisce anche TMR0H
    bsf     T0CON,TMR0ON       ; restart timer

Sul WEB sono disponibili diversi programmi che permettono di calcolare in modo automatico il valore corretto da caricare nei registri in funzione del numero di cicli da compensare.

 


 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 19/09/13.