Comment fonctionnent les timers d’un microcontrôleur ?
(Mis à jour le 28/05/2023)
Vous avez sûrement déjà entendu parler d”un timer, mais savez-vous vraiment à quoi il sert et comment il fonctionne ? Dans cet article, nous allons découvrir les différents cas d’utilisation d”un timer sur un microcontrôleur , ainsi que son fonctionnement théorique. Ces connaissances vous permettront d’utiliser efficacement les timers dans vos programmes Arduino, MicroPython. Commençons par définir ce qu’est un timer 😊
Le timer, un chronomètre électronique
Un timer est un compteur électronique capable de compter le temps qui s’écoule de manière très précise. En fait, c’est un registre à l’intérieur d’un microcontrôleur qui s’incrémente chaque fois qu’il reçoit une impulsion d’un signal d’horloge générée en interne par le microcontrôleur. On utilisera les timers plutôt que d’utiliser le microprocesseur pour compter, ce que fait la fonction Arduino delay()
qui est bloquante.
Les timers hardware sont des blocs indépendants du CPU. Ils s’occupent de compter en continu pendant que le programme peut exécuter d’autres fonctions.
Note
Les fonctions Arduino millis()
et micros()
utilisent un timer pour fonctionner.
Les timers proposent des fonctionnalités supplémentaires, par exemple déclencher des alarmes quand un certain seuil est atteint. On peut par exemple exécuter des tâches périodiquement avec des interruptions générées par un timer.
À quoi sert un timer sur microcontrôleur : les cas d’utilisations
On peut utiliser le timer pour sa fonction de base : mesurer précisément des durées. Cela peut être intéressant pour connaître le temps d’exécution d’un programme ou connaître l’heure depuis le lancement du programme.
En pratique, on utilise souvent une particularité des timers : générer des interruptions quand le compteur arrive à une certaine valeur seuil. Voici ce que l’on peut faire en tirant parti de cette fonctionnalité :
Faire des tâches planifiées : un timer peut être configuré pour déclencher une interruption à intervalles réguliers, ce qui peut être utilisé pour mettre à jour des affichages, surveiller des entrées de capteur …
Générer des signaux PWM : un timer peut être utilisé pour générer des signaux PWM pour contrôler la vitesse d’un moteur, la luminosité d’une LED, etc.
Synchronisation d’événements : un timer peut être utilisé pour synchroniser des événements en interne ou avec des périphériques externes.
Créer des timeouts : un temps d’attente maximal
Note
Les timers sont employés dans des fonctions Arduino sans qu’on en ait forcément conscience, dans des fonctions de base (millis()
et micros()
), pour le PWM et dans des bibliothèques (Servo
)
Je vous en dis un peu plus sur le fonctionnement interne d’un timer. C’est important de comprendre comme il fonctionne sous le capot pour le configurer correctement par la suite dans vos programmes 😊.
Le fonctionnement interne d’un timer
Les timers ne peuvent pas compter indéfiniment. Ils sont limités par leurs résolutions quantifiées en bits. Par exemple, l’Arduino comporte 2 timers de 8bits et 1 de 16bits. Cela signifie que 2 timers peuvent compter entre 0 et 255, et l’autre entre 0 et 65 535. Sur une carte ESP32, il y a 4 timers de 64bits, ce qui permet de compter entre 0 et 18 446 744 073 709 552 000 (Y a de quoi faire 😉.)
Si le timer dépasse sa valeur maximale, il y a ce qu’on appelle un débordement. Dans ce cas, le compteur est généralement remis à zéro autoreload
et une alarme interne est générée pour avertir le microprocesseur.
Le temps qui s’écoule entre chaque « tick » du timer dépend bien évidemment de la fréquence de l’horloge. À chaque front montant de l’horloge, le compteur est incrémenté avec une fréquence de l’ordre de plusieurs dizaines de MHz, le temps est très court et le compteur va très rapidement déborder.
Heureusement, il est possible de réduire cette fréquence grâce à des composants internes au microcontrôleur, appelés diviseurs ou prescalers
en anglais. Un prescaler permet de diviser la fréquence de l’horloge et d’incrémenter plus lentement. Par exemple, avec un prescaler
de 4, le compteur comptera 4 fois plus lentement que le signal d’horloge.
Note
On configure un timer avec sa période via la valeur du prescaler
et de l’autoreload
.
Calculer la valeur du prescaler
et de l’autoreload
depuis la période du timer voulue
On appelle la période du timer, la durée qui s’écoule entre 2 débordements du compteur. Pour la calculer, on peut utiliser la formule suivante :
Note
Le compteur compte de 0 à autoreload
inclus, soit \((autoreload+1)\) valeurs.
En général, on connaît la période du timer que l’on voudrait avoir : on cherche plutôt les valeurs des paramètres. Il y a plusieurs couples de solutions possibles pour une période donnée. En effet, on peut soit avoir un prescaler faible (fréquence de comptage élevée) avec un débordement grand ou vice-versa. On ne peut pas trouver les 2 valeurs de manières séparées. On doit en choisir une, pour déterminer l’autre.
Prenons une horloge de 80 MHz, on veut avoir une période du timer de 1 seconde. En choisissant un prescaler
de 1, la valeur du débordement est :
La valeur de l’autoreload doit être stockable**** dans le registre du timer, c’est-à-dire avoir une valeur inférieure à \(2^{résolution\space timers}\) bits. Pour l’ESP32 ca passe, mais pas pour les timers de l’Arduino. Il faudrait prendre une valeur de prescaler plus importante.
Note
Parfois ce n’est pas possible de trouver exactement les bonnes valeurs des paramètres. Il faudra donc faire des approximations. On peut minimiser cet effet, en prenant le prescaler le plus petit possible.
Si on veut obtenir une valeur sur 16bits pour un timer de l’Arduino, on peut prendre un prescaler de 8000 et un autoreload à 9999 :
Bien sûr, d’autres valeurs de prescaler sont possibles ! On choisit généralement la valeur du prescaler de manière arbitraire, la plus grande possible, puis on en déduit celle de la valeur seuil.
En pratique, on essaye de prendre une valeur qui simplifie les calculs : par exemple un prescaler de 80 pour l’ESP32 permanent de configurer la période du timer à la microseconde près (la fréquence de l’horloge est de 80 MHz).
Plus simple non 😊?
En plus sur l’ESP32, la soustraction est cachée dans le code des fonctions, donc on pourra encore simplifier le calcul avec un prescaler
de 80 par :