Le TIMER/COMPTEUR






Le TIMER/COMPTEUR
Faisant suite au PRESCALER, le TIMER/COMPTEUR joue un rôle central.
Son rôle est de compter les tops d'horloge à une vitesse sur laquelle l'utilisateur peut intervenir. A première vue cela parait simple et on se demande à quoi peut bien servir de compter les tops d'horloge.

En fait dans un microcontrôleur le timer/compteur joue un rôle essentiel, il permet :
• Mesurer le temps le temps qui passe (Instructions Delay ou Millis),
• Mesurer la durée d'une action d'un évènement (Génération d'un signal DCC),
• Compter le nombre d'évènement arrivant sur une broche (Principe de l'analyseur de trame DCC),
• Générer des signaux PWM (Modulation à Largeur d'Impulsion),
• Réaliser des comparaisons,
• Générer des interruptions,
• ...,

Un TIMER/COMPTEUR fonctionne de deux façons soit :
• En TIMER lorsqu'il compte les tops de son horloge, 16 Mhz pour un arduino uno,
• En COMPTEUR  lorsqu'il compte les tops d'une autre source.

L'Atmega 328 qui équipe l'arduino uno possède 3 timers/compteurs :
• Le TC0 est un timer/compteur de 8 bits qui compte jusqu'à 256 (0 à 255),
• Le TC1 est un timer/compteur de 16 bits qui compte jusqu'à 65536 (0 à 65535),
• Le TC2 est un timer/compteur de 8 bits qui compte jusqu'à 256 (0 à 255).

Chaque timer/compteur : TC est équipé,
   • D'un prédiviseur associé au TC correspondant,
   • De deux registres indépendants de comparaison par TC, de taille identique au timer/compteur : TCn Contrôle Registre A et TCn Contrôle Registre B.
   •
D'une possibilité de faire fonctionner le compteur en mode Normal ou CTC (Clear-Timer-on Compare).

De 3 sources d'interruption indépendantes :
   • Interruption par dépassement du comptage d'un timer/compteur : OVERFLOW,
        •
Signalé par un Flag (drapeau) TCn IFR Overflow,
   • Interruption par égalité entre les valeurs contenues dans un ou les deux registres de comparaison A et B,
        • Signalé par un Flag (drapeau) TCn IFR A ou B.

D'un masque d'interruption, autorisant individuellement les interruptions :
   • Par Comparaison A,
   • Par Comparaison B,
   • Par Overflow.

D'une autorisation générale des interruptions.

Chaque partie permettant le déclenchement d'une interruption est contrôlée par des registres internes au microcontrôleur.

Comme vu sur la page
 le timer/compteur peut se représenter comme un escalier.

compteur 8 bits
                  
compteur 16 bits
Compteur 8 bits
                                                                                                                    
Compteur 16 bits

L'horloge d'un arduino uno est de 16 Mhz. Cela vaut dire qu'un top d'horloge survient tous les 62,5 ns (nanoseconde) :

A chaque top d'horloge on gravit une marche de l'escalier.

• Pour le timer/compteur 0 ou 2 : 62,5 ns * 256 = 16 µs. L'escalier est gravi en 16µs (62,5 Khz),
• Pour le timer/compteur 1 : 62,5 ns * 65536 = 4,096 ms. L'escalier est gravi en 4,096 ms (244 Hz).
 
duree compteur 8 bits
       
duree compteur 16 bits
                                                                           
Compteur 8 bits
                                                                                                                    
Compteur 16 bits

Ces temps de comptage sont très rapides et peuvent être ralenti grâce au PRESCALER qui est commun aux trois timers/compteurs de l'arduino uno.

Chaque timer/compteur possède un prédiviseur qui lui est propre et qui permet de diviser la fréquence du signal d'horloge venant du PRESCALER.

prescaler suivit d'un timer/compteur
Prescaler suivit d'un timer/compteur avec son prédiviseur
Le PRÉDIVISEUR des TIMERS
Chaque prédiviseur est contrôlé par un registre :
TCCR0B pour le TC0,
TCCR1B pour le TC1,
TCCR2B pour le TC2.

Les rapports de division possible de ces prédiviseurs sont différents suivant le timer/compteur utilisé. Il sont à multiplier par le prescaler commun aux trois timers/compteurs s'il a été utilisé.

Exemple :
• Prescaler commun réglé sur un rapport de division par 8,
• Prédiviseur d'un TCn réglé sur un rapport de division par 8,

Le TCn correspondant s'incrémentera tous les :
• Rapport de division total : 8 * 8 = 64,
• Le TCn s'incrémentera tous les 64 tops d'horloge soit :
    • 4µs, soit une fréquence de : 250 Khz.

Pour le TC0 on peut régler le prédiviseur pour obtenir un rapport de division égale à : 1, 8, 64, 256, 1024.

rapport de division du prescaler du tc0
Rapport de division du prescaler du TC0 

Pour le TC1 on peut régler le prédiviseur pour obtenir un rapport de division égale à : 1, 8, 64, 256, 1024.

rapport de division du prescaler du tc1
Rapport de division du prescaler du TC1 

Pour le TC2 on peut régler le prédiviseur pour obtenir un rapport de division égale à : 1, 8, 32, 64, 128, 256, 1024.

rapport de division du prescaler du tc2
Rapport de division du prescaler du TC2 
Le FONCTIONNEMENT des TIMERS/COMPTEURS
Les timers/compteurs peuvent être utilisés seuls ou en association avec d'autres registres.

Chaque timer/compteur possède un registre qui contient le nombre de top d'horloge qui arrive jusqu'à lui, et qui est fonction du prescaler et de son propre prédiviseur. Ces registres sont :
TCNT0 pour le TC0, composé d'un registre 8 bits,
TCNT1 pour le TC1, composé de deux registres 8 bits : TCNT1H et TCNT1L. Par souci de simplification nous ne parlerons que d'une manière globale pour TCNT1,
TCNT2 pour le TC2, composé d'un registre 8 bits.

prescaler et timer 0
       
prescaler et timer 1
        
prescaler et timer 2
                              
Prescaler et timer 0
                                                                             
Prescaler et timer 1
                                                                              
Prescaler et timer 2

Le comptage d'un timer dépend du réglage du prescaler (commun aux trois timers/compteurs) et du prédiviseur de chaque timer/compteur.

Configuration d'un timer/compteur
Un timer compteur peut s'utiliser en mode :
Normal ou,
CTC (Clear Timer on Compare).

Pour utiliser un timer/compteur en mode normal il faut le configurer.

Ce rôle est réalisé par des bytes contenus dans deux registres différents :

Ces bytes sont : WGMn2, WGMn1, WGMn0.
WGMn1 et WGMn0 sont contenus dans TCCRnA,
WGMn2 est contenu dans TCCRnB,
• Pour le timer/compteur 1, un byte supplémentaire WGM13, est contenu dans TCCR1B uniquement pour le timer/compteur 1.

n
, représentant le numéro du timer/compteur.

Ces bytes doivent être mis à 0 pour un fonctionnement du timer/compteur correspondant en mode normal.


wgmn1 et 0 dans registre tccrna
Position de WGMn1 et WGMn0 dans TCCRnA pour les 3 timers/compteurs
Ligne de commande :
TCCRnA = 0b00000000;
ou :
bitClear (TCCRnA, WGMn0);
bitClear (TCCRnA, WGMn1);
ou,
TCCRnA &=~ (1 << WGMn0);
TCCRnA &=~ (1 << WGMn1);


wgmn2 dans registre tccrnb
Position de WGMn2 dans TCCRnB pour le timer/compteur 0 et 2
Ligne de commande :
TCCRnB = 0b00000000;
ou,
bitClear (TCCRnB, WGMn2);
ou,
TCCRnB &=~ (1 << WGMn2);

Comme on l'a vu précédemment plusieurs bytes jouant des rôles différents peuvent se trouver dans le même registre d'un microcontrôleur.

Ainsi les bytes :
• CSn0, CSn1, CSn2 et WGMn2 se trouvent dans le registre TCCRnB,
• CSn0, CSn1, CSn2 et WGMn2 et WGM13 se trouvent dans le registre TCCR1B.

Il faut veiller lors de la manipulations des bytes de ce registre à ne modifier que ceux que l'on désire sans toucher aux autres.. 


position bytes registre tccrn
CS0, CS1, CS2 et WGMn2 dans le registre TCCRnB pour le timer/compteur 0 et 2
Ligne de commande :
TCCRnB = 0b00000000;
ou,
bitClear (TCCRnB, WGMn2);
ou,
TCCRnB &=~ (1 << WGMn2);

position bytes registre tccr1b
WGM13 et WGM12, CS12, CS11, CS10 dans le registre TCCR1B pour le timer/compteur 1

Particularité dans le timer/compteur, il existe un byte WGM13 en plus.

Notre schéma devient en incorporant la sélection du mode de fonctionnement du timer/compteur.

mode normal ou ctc
Mode normal ou CTC timer/compteur 0 et 2

Le FLAG des TIMERS/COMPTEURS
Nous avons vu que le timer/compteur incrémentait son registre TCNTn, au rythme de l'horloge et du réglage du prescaler ou du prédiviseur du timer/compteur considéré.

Lorsque le timer/compteur déborde, retour à zéro, cela déclenche un FLAG ou Drapeau.

Le timer/compteur possède un registre TIFRn, qui contient un byte appelé TOV qui est mis à 1 lorsque le timer/compteur déborde.

byte tov du registre tifrn
Byte TOV du registre TIFRn

En surveillant ce byte on peut utiliser le timer/compteur pour mesurer le temps qui passe.

Il suffit de surveiller le passage à 1 de TOV pour savoir que le timer/compteur a débordé.

flag tov du registre tifrn
Flag TOV du registre TIFRn
MISE en ŒUVRE du TIMER/COMPTEUR
Pour illustrer ce que l'on vient de voir je vous propose un petit programme utilisant le prescaler et le timer/compteur. 
Dans ce programme on utilisera des : 
• Instructions de haut niveau pour l'allumage et l'extinction de la Led, 
• Instructions de bas niveau pour la commande du prédiviseur et du timer/compteur 1. 


On utilisera une platine arduino UNO ainsi que la LED incorporée, branchée sur la sortie numéro 13. Aucun autre composant n'est nécessaire.

arduino uno et sa led incorporee
Arduino UNO et sa Led incorporée

Description du programme :
Ces deux programmes réalisent le clignotement d'une led à une fréquence de 1 Hz.

Pour obtenir cela on utilise le prescaler avec une division par 4 et le prédiviseur du timer/compteur 1 par 64.

La division totale est de : 4*64 = 256. On aurait pu utiliser le prescaler du timer/compteur 1 réglé directement sur 256.

La fréquence au timer/compteur 1 est de : 16Mhz / 256 = 62,50 KHz = 16µs. Le timer/compteur 1 est incrémenté toutes les 16 µs.

Pour réaliser une tempo de 500 ms il faut : 0.5s / .000016s = 31250.

Le timer/compteur doit compter 31250 impulsions pour que cela réalise 0,5 s.

Si l'on veut faire déborder le timer/compteur 1 pour activer le Flag correspondant il faut prépositionner le registre :
•  TCNT1 à : 65536 - 31250 = 34286.

Le registre TCNT1 peut compter jusqu'à 65536. Pour cela le microcontrôleur utilise deux registres.
TCNT1H qui est l'octet de poids fort,
• TCNT1L qui est l'octet de poids faible.

Dans le premier programme on utilise le timer/compteur 1 d'une manière générale TCNT1 = 34286.

Dans le deuxième on utilise les deux registres du timer/compteur 1 :
TCNT1H = 0b10000101 = 133,
• TCNT1L = 0b11101110 = 238.


const byte led = 13;//Déclaration de la broche de la Led 
 
void setup()  

pinMode (led, OUTPUT);//Broche 13 en Sortie 
 
CLKPR = 0b10000000; //Activation du prescaler 
CLKPR = 0b00000010; //Division par 4 
 
TCCR1A = 0b00000000;//WGM10 et WGM11 à 0 
TCCR1B = 0b00000011;//WGM12 et WGM13 à 0 et division par 64 
//Division Totale 256 
 
TCNT1 = 34286; //Registre TCNT1 général
 

 
void loop()  

if (bitRead(TIFR1, 0) == 1) //Test du Flag

//Fréquence du Clignotement de la Led : 1Hz  
digitalWrite(led, digitalRead(led) ^ 1); // Inversion de la LED 
 
TCNT1 = 34286;  
 
bitSet (TIFR1, 0); //Remise à 0 du FLAG TIFR1 
 

}
       
const byte led = 13;//Déclaration de la broche de la Led 
 
void setup()  

pinMode (led, OUTPUT);//Broche 13 en Sortie 
 
CLKPR = 0b10000000; //Activation du prescaler 
CLKPR = 0b00000010; //Division par 4 
 
TCCR1A = 0b00000000;//WGM10 et WGM11 à 0 
TCCR1B = 0b00000011;//WGM12 et WGM13 à 0 et division par 64 
//Division Totale 256 
 
TCNT1H = 0b10000101; //Registre TCNT1H (octet de poids fort)
TCNT1L = 0b11101110; //Registre TCNT1L (octet de poids faible)

 
void loop()  

if (bitRead(TIFR1, 0) == 1) //Test du Flag

//Fréquence du Clignotement de la Led : 1Hz  
digitalWrite(led, digitalRead(led) ^ 1); // Inversion de la LED 
 
TCNT1H = 0b10000101; 
TCNT1L = 0b11101110; 
 
bitSet (TIFR1, 0); //Remise à 0 du FLAG TIFR1 

}
                                      
Programme TIMER/COMPTEUR 1, TCNT1 général
                                                 
Programme TIMER/COMPTEUR 1, TCNT1 décomposé

La surveillance du Flag TOV n'est pas la meilleure solution puisque le programme doit surveiller en permanence la mise à 1 de celui-ci.
Pour décharger le microcontrôleur de cette surveillance et optimiser grandement l'efficacité du programme il est préférable d'utiliser les INTERRUPTIONS.