6.7 Les Timers

Un timer est avant tout un compteur électronique. Il a une résolution qui dépend de son architecture et contraint l’amplitude des valeurs sur lesquelles il peut compter. Ainsi un timer avec une résolution de 8 bits pourra compter entre 0 à 255, un timer de 16 bits entre 0 à 65 535 et un timer de 32 bits de 0 à 4 294 967 295 ! 


L’incrémentation d’un timer se fait sur des événements. Ces événements sont usuellement générés par une horloge avec une fréquence fixée. Dans ce cas, le timer “compte le temps” d’où son nom. Il est cependant possible de configurer le timer pour compter des événements qui ne sont pas périodiques, par exemple un signal venant d’une broche et présentant des fronts montants et descendants. Dans ce cas, on parle alors plutôt de compteur pour désigner le timer. Dans la suite, nous ne nous focaliserons que sur le comportement du timer dans le cas d’une horloge comme source d’événements.

d

L’entrée d’horloge d’un timer est très souvent précédée d’un diviseur (prescaler). Son rôle est d’opérer une première division de la fréquence de l’horloge pour “ralentir” la fréquence de comptage du timer (nous verrons par la suite son intérêt pratique).


d


L’utilisation classique d’un timer va consister à réagir au débordement (overflow) de son compteur. En effet, la résolution du timer étant bornée, son incrémentation va inévitablement conduire à un débordement, c’est-à-dire que l’incrément suivant dépasse la capacité du compteur. Lorsque ce débordement survient, le compteur est remis à zéro et un événement interne est généré pour signaler cette rupture dans la séquence de comptage.


Sous Flowcode

Sous Flowcode pour utiliser un Timer, il faut ajouter le composant "Timer" qui se trouve dans Storage  

Image Timer

Allows you to measure the passage of time via the use of a hardware timer peripheral.Works like a stopwatch with macros to start, stop and reset the timer counter.Returns time as integer Seconds, Milliseconds, Microseconds and also Floating Point and String.

Aide sur le Timer:  https://www.flowcode.co.uk/wiki/index.php?title=Component:_Timer_(Storage)


Downloadable macro reference

Fc9-comp-macro.png

GetCountSeconds

Returns the count value as a number of whole seconds, rounded down to the nearest second 

Fc9-u32-icon.png - ULONG

Return

Fc9-comp-macro.png

StartCounter

Starts the timer counting. 

Fc9-bool-icon.png - BOOL

ResetCounters

Range: 0-1 where 0=Do Not Reset and 1=Reset 

Fc9-void-icon.png - VOID

Return

Fc9-comp-macro.png

GetCountString

Returns the count value as a floating point string in seconds. 

Fc9-u8-icon.png - BYTE

Precision

Number of decimal places to display in string 

Fc9-string-icon.png - STRING

Return

Fc9-comp-macro.png

GetCountMicroSeconds

Returns the count value as a number of whole microseconds, rounded down to the nearest microsecond 

Fc9-u32-icon.png - ULONG

Return

Fc9-comp-macro.png

GetCountMilliseconds

Returns the count value as a number of whole milliseconds, rounded down to the nearest millisecond 

Fc9-u32-icon.png - ULONG

Return

Fc9-comp-macro.png

StopCounter

Stops the timer counting. 

Fc9-void-icon.png - VOID

Return

Fc9-comp-macro.png

ResetCounter

 

Fc9-void-icon.png - VOID

Return

Fc9-comp-macro.png

GetCountReal

Returns the count value as a floating point value in seconds. 

Fc9-f32-icon.png - FLOAT

Return

Fc9-comp-macro.png

GetRawCount

Efficient command to collect the integer number of instructions since the timer was started. 

Fc9-u32-icon.png - ULONG

Return

Property reference

Fc9-prop-icon.png

Properties

Fc9-type-16-icon.png

Timer

Timer Peripheral to use to monitor the passage of time. 

Fc9-type-21-icon.png

Bit Depth

Number of bits in the timer peripheral. 8-bit timers can count from 0-255 before overflowing. 16-bit timers can count from 0 to 65535 before overflowing. 

Fc9-type-21-icon.png

Instruction Rate

 

Fc9-type-15-icon.png

Rollover Frequency

 

Fc9-type-15-icon.png

Rollover Time

 

Fc9-type-15-icon.png

Max Time Measurement

Max time in seconds that we can measure before rolling over and starting again. 

Fc9-conn-icon.png

Connections

Fc9-conn-icon.png

Simulations

Fc9-type-21-icon.png

Timer Delay (ms)

Number of milliseconds between timer events which increment the simulated timer counter variable. 10ms will yield the highest resolution. Values less than this will likely just increase the error and making the timer run slow. Higher delays will yield greater accuracy. 

Fc9-type-21-icon.png

Decimal Points

 


Exemple, un chronomètre 

Flowcode_328P_Timer.rar



Sous Arduino

Un timer est un registre à l’intérieur du microcontrôleur qui s’incrémente (ou se décrémente) chaque fois qu’il reçoit une impulsion d’un signal d’horloge. Ce signal d’horloge peut être propre au microcontrôleur ou bien extérieur à celui-ci. 

Un timer est donc un compteur capable de compter le temps qui s’écoule, d’où son nom anglais de timer counter. Dans ce qui suit, le timer est toujours employé dans un mode où il s’incrémente, mais dans d’autres modes, il peut se décrémenter. 

On peut télécharger la documentation du constructeur sur le lien suivant : https://cdn.sparkfun.com/datasheets/Kits/doc8161.pdf

Si le registre du timer comporte 8 bits, il est alors capable de compter de 0 à 255 (en hexadécimal, de 00 à FF). Lorsqu’il arrive à 255 (FF), un coup d’horloge supplémentaire devrait le faire passer à 256 (soit 100 en hexadécimal), ce qui n’est pas possible puisque le registre n’a que 8 bits. Le registre passe donc à 0 ; on dit qu’il subit un débordement (Overflow en anglais) [1], mais ce débordement entraîne la mise à 1 d’un bit bien particulier dans un registre de contrôle associé au timer. Ce bit est appelé un flag (drapeau en anglais) et indique que le timer vient de compter jusqu’à 256, ce qui permet d’attirer l’attention du programmeur, un peu comme ces boîtes aux lettres américaines qui possèdent un petit drapeau qui se dresse chaque fois que le facteur a déposé du courrier à l’intérieur.

L’intérêt d’un timer est qu’il compte sans cesse et que pendant ce temps, le programme peut réaliser autre chose, ce qui n’est pas possible si on utilise la fonction delay() qui est bloquante et qui ne permet pas de faire autre chose pendant ce temps d’attente. Le temps que le timer met pour compter 256 coups dépend bien sûr de la fréquence de l’horloge ; à 16 MHz (fréquence du microcontrôleur utilisé dans les modules Arduino), c’est très rapide, mais il est possible de diviser cette fréquence d’horloge grâce à des circuits internes au microcontrôleur appelés prédiviseur (prescaler en anglais). On peut alors diviser la fréquence de base (16 MHz) par 8, 32, 64, 128, 256 ou 1024 ; pour cela, il faut utiliser intelligemment d’autres registres de contrôle associés au timer. Par exemple, si on règle de prédiviseur pour diviser la fréquence par 1024, le timer comptera donc à une fréquence de 15625 Hz.

Comme pour tout registre, on peut lire la valeur d’un timer ou bien écrire une valeur particulière dans le timer. Mais ce qui est surtout important, ce sont les registres de contrôle associés au timer car ce sont eux qui permettent de modifier le comportement du timer et de contrôler ce qu’il fait. Il faut donc bien les connaître pour bien savoir les utiliser et la lecture de la documentation liée au microcontrôleur est souvent indispensable.


Timers du microcontrôleur de l’Arduino

Le module Arduino Uno est construit autour du microcontrôleur AVR ATmega328P d’Atmel qui possède 3 timers :

  • Le timer0, sur 8 bits, utilisé par les fonctions delay(), millis() et micros(). Il commande également des PWM (Pulse Width Modulation ou Modulation par Largeur d’Impulsion) sur les broches 5 et 6.
  • Le timer1, sur 16 bits, qui compte de 0 à 65535 (0 à FFFF en hexadécimal) et qui est utilisé par la bibliothèque Servo ou bien pour de la PWM sur les broches 9 et 10.
  • Le timer2, sur 8 bits, qui est utilisé par la fonction Tone() ou bien pour de la PWM sur les broches 3 et 11.

Le langage d’Arduino fait donc appel aux timers du microcontrôleur mais ceci reste transparent pour le programmeur. D’ailleurs, dans la majorité des applications, le langage d’Arduino est souvent très suffisant et permet de développer des applications sans avoir besoin de faire appel à l’architecture du microcontrôleur. Néanmoins, dans quelques cas particuliers, il peut être intéressant de savoir programmer les timers ; dans cet article, nous allons évoquer l’utilisation des timers comme compteur de temps, même si ceux-ci sont capables de faire bien d’autres choses.


Les registres de contrôle

Le tableau suivant donne les différents registres de contrôle associés à chaque timer ; nous verrons le rôle de chaque registre tout en nous limitant à ce qui est vraiment à connaître pour réaliser un premier exemple.

Timer 0

Timer 1

Timer 2

Rôle

TCNT0

TCNT1L

TCNT2

Timer (bit 0 à 7)

-

TCNT1H

-

Timer (bit 8 à 15)

TCCR0A

TCCR1A

TCCR2A

Registre de contrôle

TCCR0B

TCCR1B

TCCR2B

Registre de contrôle

-

TCCR1C

-

Registre de contrôle

OCR0A

OCR1AL

OCR2A

Output Compare (bit 0 à 7)

-

OCR1AH

-

Output Compare (bit 8 à 15)

OCR0B

OCR1BL

OCR2B

Output Compare (bit 0 à 7)

-

OCR1BH

-

Output Compare (bit 8 à 15)

-

ICR1L

-

Input Capture (bit 0 à 7)

-

ICR1H

-

Input Capture (bit 8 à 15)

TIMSK0

TIMSK1

TIMSK2

Interrupt Mask

TIFR0

TIFR1

TIFR2

Interrupt Flag

Le timer1 est un timer de 16 bits et est donc constitué de deux registres de 8 bits, l’un donnant les bits 0 à 7, l’autre donnant les bits 8 à 15.


Pour se repérer dans ce tableau, il faut savoir que TCNT signifie Timer/Counter (Register), TCCR Timer/Counter Control Register, OCR Output Compare Register, ICR Input Capture Register, TIMSK Timer/Counter Interrupt Mask Register et TIFR Timer/Counter Interrupt Flag Register. OCR et ICR ont des rôles particuliers dont nous ne parlerons pas dans ce premier article. TIMSK et TIFR servent pour que le timer puisse générer des interruptions [2] comme nous le verrons dans un autre article.


Exemple d’utilisation simple

Nous allons maintenant nous intéresser au timer 2 dans son rôle le plus simple, compter le temps, et aux registres de contrôle qui y sont associés. Ce que nous allons dire reste bien entendu valable pour les timers 0 ou 1. Pour mieux comprendre, nous prendrons comme exemple le clignotement d’une DEL à 1 Hz, ce qui signifie que toutes les 500 ms (demi-période), il faut inverser la DEL, c’est-à-dire l’allumer si elle est éteinte et l’éteindre si elle est allumée.

Ralentir ou accélérer le timer

Nous avons vu qu’il était facile de ralentir un timer : il suffit d’utiliser le prédiviseur pour diviser la fréquence du signal d’horloge. Pour faire cela, il suffit de positionner certains bits du registre TCCR2B. À partir de là, notre compteur comptera de 0 à 255 (cela lui prendra un certain temps) puis passera en débordement, c’est-à-dire qu’il repartira de 0 après avoir positionné le flag TOV2 (Timer/Counter 2 Overflow Flag) à 1. Il s’agit là du bit 0 du registre TIFR2. Chaque fois que le bit 0 du registre TIFR2 passe à 1, cela signifie que notre timer a compté un certain laps de temps (connu) ; il suffit alors de repositionner le flag TOV2 à 0 et d’incrémenter un compteur (une variable) pour continuer ce processus. Lorsque ce compteur arrive à une certaine valeur, c’est que le temps à attendre s’est écoulé.

Pour accélérer un timer, il faut le faire déborder avant qu’il ait compté 256 coups : pour cela, il suffit de partir d’une valeur différente de 0 pour effectuer le comptage puisqu’il est possible d’écrire dans le registre du timer.

On voit maintenant comment il faut opérer pour faire clignoter notre DEL. Le timer part de 0 et s’incrémente jusqu’à 255, puis recommence de 0 vers 255 en ayant positionné le flag TOV2 à 1. Il suffit de surveiller ce flag ; chaque fois qu’il est à 1, on repositionne le flag à 0 et on incrémente un compteur. Lorsque ce compteur arrive à une certaine valeur, on a atteint 500 ms et il faut agir sur la DEL. Pour que le timer ait ce comportement, il doit être utilisé en mode normal ; dans ce mode, le timer compte en incrémentant, et le flag est positionné à 1 chaque fois que le timer repasse par 0.

Pour que le timer soit en mode normal, il faut que les trois bits appelés WGM20 WGM21 et WGM22 soient à 0, ce qui nécessite d’aller écrire dans 2 registres (WGM2 pour Waveform Generation Mode du timer 2). En effet, WGM20 et WGM21 sont les bits 0 et 1 du registre TCCR2A, et WGM22 est le bit 3 du registre TCCR2B. Les bits 0 à 2 de ce même registre sont appelés CS20 CS21 et CS22 et servent à régler le prédiviseur suivant le facteur de division souhaité (CS2 pour Clock Select du timer 2). Tout cela sera plus clair sur un exemple.

Calcul théorique du comptage

Le timer 2 sera utilisé en le faisant compter à la fréquence de 62500 Hz (fréquence d’horloge divisée par 256). Un cycle d’horloge dure donc 16 µs (l’inverse de la fréquence). Pour avoir 500 ms (500000 µS), il faut compter 500 000 µs / 16 µs = 31250 fois. Cette valeur est décomposable en 125 * 250. Le timer doit compter 250 fois pour déborder ; il suffit de le faire partir de la valeur 6. Chaque fois qu’il déborde, une variable compteur est incrémentée ; quand cette variable atteint 125, on a bien 500 ms qui se sont écoulées et on agit sur la DEL.

Initialisation des registres de contrôle

Pour que le timer soit en mode normal, il faut que WGM20 WGM21 et WGM22 soient à 0. On positionne ces bits à 0 dans deux registres TCCR2A et TCCR2B par une instruction adéquate (bitClear dans le langage Arduino). Pour que le prédiviseur divise par 256, il faut que le bit CS22 soit égal à 1, le bit CS21 soit égal à 1 et le bit CS20 soit égal à 0 ; il suffit d’écrire la valeur binaire 0b00000110 dans le registre TCCR2B. Heureusement pour nous, le compilateur d’Arduino connaît les noms des registres et les noms des bits.

La dernière chose à savoir, c’est que pour réinitialiser le flag TOV2 par logiciel, il faut écrire à 1 le bit 0 du registre TIFR2 ; l’instruction est bitSet (et non bitClear comme on aurait pu le croire).

Programme

Voici le programme, écrit avec le langage Arduino :

  1. /*
  2.  * Clignotement d'une DEL (LED en anglais) à 1 Hz par timer 2.
  3.  */
  4. const byte Led = 13; // Pour utiliser la LED du module
  5. #define LedToggle digitalWrite (Led, !digitalRead(Led))
  6.  
  7. void setup ()
  8. {
  9. pinMode (Led, OUTPUT);
  10. bitClear (TCCR2A, WGM20); // WGM20 = 0
  11. bitClear (TCCR2A, WGM21); // WGM21 = 0
  12. TCCR2B = 0b00000110; // Clock / 256 soit 16 micro-s et WGM22 = 0
  13. TIFR2 = 0b00000001; // TOV2
  14. TCNT2 = 256 - 250; // Chargement du timer à 6
  15. }
  16.  
  17. byte varCompteur = 0; // La variable compteur
  18.  
  19. void loop () {
  20. if (bitRead (TIFR2, 0) == 1) { // Flag TOV2 mis à 1 ?
  21.  TCNT2 = 256 - 250; // Rechargement du timer à 6
  22.  bitSet (TIFR2, TOV2); // Remise à zéro du flag TOV2 (voir texte)
  23.  if (varCompteur++ > 125) { // Incrémentation et a atteint 125 ?
  24.   varCompteur = 0; // On recommence un nouveau cycle
  25.   LedToggle; // Inversion de la LED
  26.  }
  27. }
  28. }


Conclusion

Nous avons vu qu’un timer est un registre compteur qui s’incrémente (ou se décrémente) à chaque impulsion d’une horloge qui peut être celle du microcontrôleur divisée par un certain nombre pour la ralentir. Lorsque le timer déborde, un flag est positionné à 1 dans un registre de contrôle. En surveillant ce flag, on peut compter le temps qui s’écoule jusqu’à obtenir une durée déterminée. 


On peut lire la valeur du timer et on peut écrire une valeur dans le timer. Plusieurs registres de contrôle sont associés au timer et permettent de modifier son comportement et de suivre son débordement. La connaissance de ces registres de contrôle et du rôle joué par chacun des bits les composant est indispensable au programmeur qui veut utiliser les timers. 


Pour cela, une lecture assidue de la documentation du constructeur est indispensable. L’intérêt d’un timer pour compter le temps qui s’écoule est que le programme peut réaliser autre chose pendant ce temps. 


Dans l’exemple donné, il suffit de surveiller le flag TOV2 pour connaître l’écoulement du temps ; ceci est possible parce que le programme ne fait rien d’autre que de surveiller. Nous verrons par la suite que le débordement du timer peut engendrer une interruption ; le programme peut alors faire autre chose et être interrompu pour traiter l’événement. 


Nous verrons aussi que les timers peuvent faire d’autres choses grâce aux autres registres de contrôle comme OCR ou ICR. Mais tout ceci est une autre histoire.


Créé avec HelpNDoc Personal Edition: Protégez vos PDF contre les accès non autorisés grâce à ces mesures de sécurité