Nous allons voir dans ce troisième article le rôle des registres OCR (Output Compare Register). Ces registres ont la même taille que le timer auquel ils sont associés et il y en a deux par timers, repérés par les lettres A et B. Pour le timer 2 qui a une taille de 8 bits, nous trouvons donc deux registres de 8 bits appelés OCR2A et OCR2B, et ces deux registres jouent des rôles assez identiques ; nous n’en détaillerons qu’un seul (OCR2A) mais vous trouverez dans la documentation du constructeur comment utiliser OCR2B.
Les Timers
Les Timers (III)
Comparaisons et interruptions
.
Par :
DIFFICULTÉ :★★★
Rôle d’un registre OCR
On peut charger un nombre dans le registre OCR2A ; lorsque le timer 2, en comptant, devient égal à OCR2A (les deux valeurs correspondent, ce qui se dit Match en anglais), cela provoque la mise à 1 d’un flag appelé OCF2A , qui est le bit 1 du registre TIFR2 (Timer/Counter 2 Interrupt Flag Register) et si on a autorisé les interruptions, ce flag va commander une interruption qui sera traitée par la routine d’interruption ISR(TIMER2_COMPA_vect)
. Pour autoriser les interruptions, il faut les autoriser de façon générale (avec sei()
ou bien interrupts()
suivant que vous utilisez le C ou le langage Arduino) et il faut aussi autoriser l’interruption produite par le flag OCF2A . Ceci se réalise en positionnant à 1 le bit OCIE2A (Timer/Counter 2 Output Compare Match A Interrupt Enable). Ce bit OCIE2A est le bit 1 du registre TIMSK2 (Timer/Counter 2 Interrupt Mask Register).
Ce que nous venons de dire doit certainement vous rappeler ce que nous avions développé dans l’article Les Timers (II), et si vous aviez bien compris ce précédent article, vous ne devez pas avoir de problème pour suivre celui-ci.
Le mode CTC
Dans les deux premiers articles, nous avions utilisé notre timer 2 dans un mode normal et il avait été nécessaire de positionner à 0 trois bits appelés WGM2 situés dans les registres TCCR2A et TCCR2B (voir les deux précédents articles Les Timers (I) et Les Timers (II)). Nous allons découvrir aujourd’hui un autre mode appelé CTC (Clear Timer on Compare Match Mode) ; dans ce mode, le timer/counter est remis à zéro quand sa valeur correspond à celle du registre OCR2A. Le timer 2 compte traditionnellement de 00 (appelé BOTTOM) à FF (appelé MAX) ; grâce au mode CTC, nous pouvons le faire compter de BOTTOM à la valeur de OCR2A (appelée TOP).
En effet, dans les deux premiers articles Les Timers (I) et Les Timers (II), nous faisions compter notre timer de 6 à 255 (FF) pour qu’il compte 250 coups, et nous lui faisions faire cela 125 fois pour obtenir un temps total égal à la demi-période de clignotement de notre DEL. Nous allons faire exactement la même chose, avec les mêmes paramètres, pour obtenir le même résultat (clignotement de la DEL du module Uno à la fréquence de 1 Hz), mais cette fois, nous ferons compter le timer de 0 à 250. Pour comprendre ces chiffres, reportez-vous au paragraphe « Calcul théorique du comptage » de l’article Les Timers (I).
Différence entre mode normal et mode CTC
La figure 1 montre comment notre timer 2 comptait en mode normal ; nous avons tracé une droite allant de 00 à FF, mais nous aurions dû plutôt dessiner un escalier puisque le compteur s’incrémente d’une unité à la fois. Pour obtenir 250 coups, nous chargions le timer avec la valeur 6 (256 – 250), dans la routine d’interruption, si bien que le timer comptait de 6 à 255 puis débordait, provoquant l’interruption générée par le flag TOV2 , interruption qui remettait à zéro le timer.
La figure 2 montre notre timer 2 dans le mode CTC ; il compte de 00 à TOP (qui vaut OCR2A soit 250) et lorsqu’il atteint cette valeur, il est remis à zéro et une interruption est générée par le flag OCF2A .
Programme par le mode CTC
Nous avons tout sous la main pour réécrire notre programme de clignotement de la DEL du module Uno (raccordée à la sortie 13). Voyons quels sont les bits à positionner à 1 dans les différents registres de contrôle.
Le prédiviseur doit diviser la fréquence de l’horloge (16 MHz) par 256 ; les bits CS22 , CS21 et CS20 doivent être respectivement égaux à 1, 1, 0. Ce sont les bits 0 à 2 du registre TCCR2B.
Le timer doit être utilisé en mode CTC ; pour cela, il faut que les bits WGM22 , WGM21 et WGM20 soient respectivement égaux à 0, 1, 0. Le bit WGM22 est le bit 3 du registre TCCR2B et les bits WGM21 et WGM20 sont les bits 0 et 1 du registre TCCR2A.
Enfin, il faut autoriser l’interruption générée par le flag OCF2A en positionnant à 1 le bit OCIE2A qui est le bit 1 du registre TIMSK2.
On retrouve dans la documentation du constructeur l’emplacement des différents bits des registres de contrôle et leur utilité. Dans les schémas ci-dessous, on a mis en jaune les bits à positionner à 1.
TCCR2A = 0b00000010
TCCR2B = 0b00000110
TIMSK2 = 0b00000010
// Clignotement d'une LED (mot anglais pour DEL) à 1 Hz par le mode CTC et une
// interruption en provenance du timer 2, flag OCF2A, pendant que le programme
// principal fait clignoter une autre LED (période 10s).
const byte Led = 13; // Pour utiliser la LED du module
const byte Led2 = 5; // Led clignotante du programme principal
#define LedToggle digitalWrite (Led, !digitalRead(Led))
#define Led2Toggle digitalWrite (Led2, !digitalRead(Led2))
void setup () {
pinMode (Led, OUTPUT);
pinMode (Led2, OUTPUT);
cli(); // Désactive l'interruption globale
TCCR2A = 0b00000010;
TCCR2B = 0b00000110; // Clock / 256 soit 16 micro-s et WGM22 = 0
TIMSK2 = 0b00000010; // Interruption locale autorisée par OCIE2A
OCR2A = 250;
sei(); // Active l'interruption globale
}
byte varCompteur = 0; // La variable compteur
// Routine d'interruption
ISR(TIMER2_COMPA_vect) {
if (varCompteur++ > 125) { // 125 * 4 ms = 500 ms (demi-période)
varCompteur = 0;
LedToggle;
}
}
void loop () {
// Mettre ici le programme. Exemple :
Led2Toggle;
delay (5000); // Demi-période de la deuxième LED
}
Ce programme fait exactement la même chose que le programme de l’article Les Timers (II), mais cette fois le timer 2 est commandé par le mode CTC.
Les autres timers
N’allez pas imaginer qu’il suffit de recopier un programme et de changer le numéro du timer et des registres de contrôle pour que cela fonctionne, car vous auriez tout faux. En effet, les bits des registres de contrôle ne s’utilisent pas toujours de la même façon. Comparons par exemple les tableaux des bits permettant de régler le prédiviseur, pour les timers 1 et 2, en examinant la documentation du constructeur. Pour diviser la fréquence de l’horloge par 256 (comme dans notre programme), il faut que les trois premiers bits du registre TCCR2B soient égaux à 1 1 0 pour le timer 2, mais pour le timer 1, les trois premiers bits du registre TCCR1B doivent être égaux à 1 0 0. Ceci est montré par la figure 3.
De même, pour passer le timer en mode CTC, il faut que les bits WGM22 , WGM21 et WGM20 soient égaux à 0 1 0 pour le timer 2 et à 1 0 0 pour le timer 1 (ces bits s’appellent alors WGM12 , WGM11 et WGM10 car c’est pour le timer 1 auxquels se rajoute un autre bit WGM13 ; ces quatre bits sont répartis dans les registre TCCR1A et TCCR1B). Ceci est montré par la figure 4. On voit qu’il est extrêmement important, quand on veut utiliser les timers, de bien étudier la documentation du constructeur, mais cela n’a rien de compliqué si vous avez bien compris les principes décrits dans cette série d’articles.
Timer 1
En guise de conclusion, je vous propose d’utiliser maintenant le timer 1, qui est sur 16 bits, pour faire clignoter notre DEL à la fréquence d’un Hertz. Vous verrez que ce n’est pas plus compliqué, c’est même plus simple car sur 16 bits, le cycle du timer 1 est plus long, et il n’est plus nécessaire de faire appel à la variable varCompteur qui comptait jusqu’à 125.
Nous allons dans un premier temps, diviser notre fréquence horloge par 256 et faire tourner notre timer à la fréquence de 16 MHz / 256 = 62500 Hz, soit une période de 16 µs. Pour faire clignoter notre DEL à 1 Hz, il faut inverser cette LED toutes les 500 ms (demi-période) ; Il faut donc que notre timer compte 500 ms / 16 µs = 500000 / 16 = 31250 fois. C’est cette valeur qu’on chargera dans le registre OCR1A et cela rentre puisque ce registre est également de 16 bits. En faisant référence à la documentation du constructeur, on voit tout de suite les bits qu’il faut positionner à 1, d’une part pour régler le prédiviseur, d’autre part pour faire passer le timer en mode CTC. Dans les schémas ci-dessous, on a mis en jaune les bits à positionner à 1.
TCCR1A = 0b00000000
TCCR1B = 0b00001100
TIMSK1 = 0b00000010
OCR1A = 31250
Voici donc le programme, que vous pouvez comparer au précédent :
// Clignotement de la LED (mot anglais pour DEL) avec une interruption
// générée par le mode CTC du timer 1 - Fréquence 1 Hz
const byte ledPin = 13;
void setup() {
pinMode (ledPin, OUTPUT);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0b00001100;
TIMSK1 = 0b00000010;
TCNT1 = 0;
OCR1A = 31250;
interrupts();
}
ISR(TIMER1_COMPA_vect)
{
digitalWrite (ledPin, !digitalRead (ledPin)); // LED Toggle
}
void loop() {
// Mettre ici le programme principal.
}
Conclusion
Utiliser les timers n’est vraiment pas compliqué ; il suffit seulement de bien comprendre l’importance des bits des registres de contrôle, de manière à les positionner à 0 ou à 1 en fonction de ce que l’on veut faire. Il faut aussi faire attention de ne pas mal positionner des bits qu’on n’utilise pas, comme par exemple les bits ICNC1 ou ICES1 dont nous n’avons pas parlé et qui servent pour les captures en entrée. On peut soit manipuler la valeur du registre, soit manipuler les bits en les mettant à 0 (bitClear
) ou à 1 (bitSet
). Concernant les bits dont nous n’avons pas parlé, il y a là matière pour d’autres articles ; j’espère seulement, avec ces trois premiers articles, avoir éveillé votre curiosité et surtout avoir réussi à démystifier les timers. Les timers servent aussi à fabriquer de la PWM (Pulse Width Modulation ou Modulation par Largeur d’Impulsion), mais ceci est aussi une autre histoire.