Sylvain Mahé Le site Web Retour à l'accueil Principes Partager des idées et des projets. Contact 06.45.49.96.98
contact@sylvainmahe.site
Écriture de la page : Sylvain Mahé
La gestion du temps avec la classe Timer La plupart des projets requièrent la dimension temporelle pour pouvoir fonctionner, c'est pourquoi la classe Timer apporte la temporisation des événements et des actions à effectuer dans un programme. Contrairement à d'autres plates-formes de développement pour microcontrôleurs à registres 8 bits, la classe Timer utilise un compteur qui fonctionne à une fréquence de 2MHz sur une largeur mémoire de 64 bits, de sorte que le temps peut être compté à partir de zéro toutes les microsecondes pendant bien plus longtemps que la durée de vie du microcontrôleur lui-même (plusieurs centaines de milliers d'années), en effet une largeur mémoire de seulement 32 bits imposerait un retour à zéro (overflow) au bout d'un peu plus d'une demi-heure tout au plus, ce qui serait très limitant pour bons nombres de projets ! Les compteurs (ou timers) que vous déclarez avec la classe Timer se présentent sous la forme d'objets (c'est le principe de beaucoup de mes classes), il est donc possible de créer plusieurs compteurs ayant tous des fonctions indépendantes dans votre programme. Exemple d'utilisation de la classe Timer : #include <Timer.h> int main() { Timer myTimer = Timer(); myTimer.start (0); while (true) { myTimer.state(); //myTimer.s est le temps écoulé en secondes //myTimer.ms est le temps écoulé en millisecondes //myTimer.us est le temps écoulé en microsecondes } return 0; } Dans cet exemple, un objet myTimer de type Timer est déclaré, puis via cet objet, la fonction start est appelée prenant en paramètre 0, le temps de départ indiqué en millisecondes : Dans la plupart des cas nous souhaitons qu'un compteur démarre à 0 (comme l'exemple) lorsque la fonction start est appelée, mais si vous le souhaitez vous pouvez le faire partir à une autre valeur. Plus loin cet objet myTimer appelle la fonction state (état) dans une boucle ce qui permet de mettre à jour les variables s (secondes), ms (millisecondes), et us (microsecondes) que vous pourrez utiliser dans vos conditions logiques et divers calculs. La fonction pause : Malgré que l'utilisation première de la classe Timer permet la création d'objets instanciés indépendants, il est néanmoins parfois utile de pouvoir bénéficier de pauses bloquantes appelées de façon statique sans objet, directement à partir de la classe concernée. La fonction pause remplie cette fonctionnalité. Exemple de clignotement d'une del avec la classe Timer : #include <GpioWrite.h> #include <Timer.h> int main() { GpioWrite myLed = GpioWrite (1); while (true) { myLed.toggle(); Timer::pause (1000); } return 0; } Contrairement au premier exemple et l'utilisation d'un objet, ici la fonction statique pause est directement appelée à partir de la classe Timer. Cette fonction prend en paramètre 1000, la durée de la pause en millisecondes. Dans l'exemple ci-dessus, si une del était branchée sur le port GPIO 1 de l'automate programmable, elle clignoterait toutes les secondes (1000 millisecondes), mais dans ces conditions le programme ne pourrait pas faire autre chose que gérer la commutation du port GPIO numéro 1, la pause est dite bloquante car rien d'autre ne peut être effectué en parallèle. L'exemple suivant résout ce problème. Exemple de clignotement d'une del sans pause bloquante avec la classe Timer : #include <GpioWrite.h> #include <Timer.h> int main() { GpioWrite myLed = GpioWrite (1); Timer myTimer = Timer(); myTimer.start (0); while (true) { myTimer.state(); //tâche à effectuer toutes les 1000ms : if (myTimer.ms >= 1000) { myLed.toggle(); myTimer.start (0); } //autres tâches à effectuer en parallèle... } return 0; } L'exemple ci-dessus montre bien que lors d'un tour de boucle, si à partir du démarrage du compteur le temps écoulé est supérieur ou égal à 1000 millisecondes, l'exécution rentre dans la condition logique, effectue la commutation de la del, redémarre le compteur à zéro, et à la suite exécute toutes les autres tâches du programme sans passer par un état bloquant comme c'était le cas précédemment. Dans une problématique comme celle-ci, plusieurs solutions sont souvent possibles, c'est ce que montre l'exemple suivant avec l'utilisation d'une variable qui est incrémentée pour suivre l'avancée du temps imposé par le compteur (ce qui évite un cumul d'erreurs dans le temps), ainsi ce dernier n'a pas besoin d'être redémarré à zéro à chaque fois. Autre exemple de clignotement d'une del sans pause bloquante avec la classe Timer : #include <GpioWrite.h> #include <Timer.h> int main() { GpioWrite myLed = GpioWrite (1); Timer myTimer = Timer(); unsigned long long timePrevious = 0; myTimer.start (0); while (true) { myTimer.state(); //tâche à effectuer toutes les 1000ms : if (myTimer.ms - timePrevious >= 1000) { myLed.toggle(); timePrevious += 1000; } //autres tâches à effectuer en parallèle... } return 0; } Cet exemple-ci est plus précis que les trois précédents car il met en jeu la variable timePrevious qui au passage dans la condition logique n'est pas initialisée sur le temps écoulé du compteur comme on pourrait être tenté de le programmer, mais plutôt bêtement incrémentée de 1000, ce qui compense et ne tient pas compte du temps d'exécution de la condition logique elle-même, de la commutation de la del, et du reste du programme. Pour parfaire l'utilisation de la classe Timer, une fonction stop existe et permet d'arrêter l'écoulement du temps, dans cette situation les variables s (secondes), ms (millisecondes), et us (microsecondes) resteront à leurs dernières valeurs mises à jour lors de l'appel de la fonction state, ceci avant l'appel à la fonction stop. Références : Récapitulatif des fonctions et variables de cette classe : unsigned long long s = 0; unsigned long long ms = 0; unsigned long long us = 0; Timer(); void start (const unsigned long long TIME_START); void state(); void stop(); static void pause (const unsigned long long DURATION);