Accueil Forum Emploi
Deve lop p e z .com Rubriques
Arduino
Arduino : Comment gérer le temps ?
Niveau : débutant
Table des matières
I. L’attente active
II. Mesurer le temps qui passe
III. Mesure du temps dans un cas par ticulier
IV. Gestion asynchrone
V. Pour aller plus loin
Vous avez compris comment piloter une broche, mais cela ne présente que peu d'intérêt si l'on ne peut pas définir « précisément » quand ou pour combien
de temps cette broche doit rester dans un état donné : par exemple allumer une LED pendant deux secondes ou effectuer une action si aucune activité n'a
été détectée pendant 30 secondes. La gestion du temps est donc souvent un élément essentiel d'un programme sur microcontrôleur, et bonne nouvelle,
Arduino propose des fonctions pour traiter le temps.
17 commentaires
Ar ticle lu 27865 fois.
L'auteur
Jay M
L'article
Publié le 21 juillet 2021
Version PDF Version hors-ligne
ePub, Azw et Mobi
Liens sociaux
Partager
I. L’attente active▲
La première fonction que l'on utilise s'appelle delay ().
Comme son nom l'indique, cette fonction crée un délai dans le code, c'est-à-dire que le microcontrôleur ne va rien faire pendant un cer tain temps : on arrête
le programme. Pour cela on indique pendant combien de temps attendre en millisecondes, en paramètre de la fonction, sous forme d'un nombre de type
unsigned long .
Par exemple pour faire clignoter une LED il faut l'allumer, attendre un moment dans cet état, l'éteindre, attendre un moment dans cet état puis recommencer.
On pourrait donc coder cela et utiliser la loop() pour boucler.
Connexion Inscription Contacts
Sélectionnez
const byte brocheLed = 3; // le N° de broche de la LED
void setup() {
pinMode(brocheLed, OUTPUT); // on met la broche en sortie
}
void loop()
{
digitalWrite(brocheLed, HIGH); // allume la LED
delay(1000); // bloquer pendant 1000 millisecondes
(1s)
digitalWrite(brocheLed, LOW); // éteint la LED
delay(1000); // bloquer pendant 1000 millisecondes
(1s)
}
La fonction delay () ne vous permet pas d'attendre moins d'une milliseconde. Votre Arduino fonctionne assez vite, avec une fréquence en MHz (16 MHz pour
un UNO) et donc 1 milliseconde, c'est long pour le microcontrôleur : le temps d'effectuer 16 000 instructions de base sur un UNO !
Si vous souhaitez avoir un temps de blocage du code inférieur à la milliseconde, il existe la fonction delayMicroseconds(). Cette fonction a un rôle similaire
et prend en paramètre le temps de blocage en microsecondes (en millièmes de milliseconde, soit en millionièmes de seconde). Le paramètre est de type
unsigned int , mais il ne faut pas dépasser 16 383 µs et il ne faut pas non plus descendre sous 3 µs.
II. Mesurer le temps qui passe▲
Une autre caractéristique du temps, c'est qu'il s'écoule inexorablement. Votre code pourrait avoir à mesurer ce temps qui passe. Par exemple, vous avez une
voiture qui fait des tours de circuit et vous voulez mesurer le temps au tour.
Votre Arduino est configuré pour capturer ce temps qui passe depuis qu'il a été démarré (à froid – première alimentation ou à chaud après un reset).
Cette fonction qui vous dit depuis combien de temps votre programme fonctionne s'appelle millis(). La valeur retournée est de type unsigned long , ça veut
dire que le compteur peut aller jusqu'à 4 294 967 295, ce qui représente 49 jours 17 heures 2 minutes 47 secondes et 295 millisecondes. Si votre Arduino
est donc allumé depuis plus de 50 jours environ, le compteur recommencera à 0.
Pour mesurer combien de temps s'est écoulé entre deux moments, vous pourriez mémoriser dans une variable la valeur de millis () au début puis la valeur
de millis () à la fin, et la différence c'est la durée en millisecondes.
Sélectionnez
unsigned long debut;
unsigned long fin;
unsigned long duree;
void setup() {
[Link](115200); // configuration de la voie
série à 115200 bauds
}
void loop() {
debut = millis();
... // ici vous effectuez un
traitement assez long
fin = millis();
duree = fin - debut; // calcul de la durée
[Link](F("Durée de traitement : "));
[Link](duree);
} Connexion Inscription Contacts
La granularité de la mesure sera de l'ordre de la milliseconde. Si vous souhaitez obtenir une mesure plus précise, il existe la fonction micros() qui vous
retourne le nombre de microsecondes écoulées depuis le lancement du programme. C'est aussi un unsigned long , mais comme on compte en
microsecondes, la valeur revient à 0 mille fois plus vite, au bout de 1 heure, 11 minutes, 34 secondes et 967 millisecondes et 295 microsecondes. Sur les
Arduino à 16 Mhz la résolution de la valeur retournée par micros () est de 4 microsecondes. C'est la durée minimale que vous serez capable de mesurer en
utilisant cette approche.
III. Mesure du temps dans un cas particulier▲
Si le temps que vous souhaitez mesurer est le temps pendant lequel une broche prend une valeur donnée ( HIGH ou LOW ), il existe la fonction pulseIn() qui va
faire cela pour vous. Elle fonctionne bien pour une durée dans un état donné entre 10 microsecondes et 3 minutes. La fonction dispose d'un paramètre
optionnel qui indique au bout de combien de temps abandonner (timeout) si les fronts attendus ne se sont pas produits.
Par exemple, imaginez que vous souhaitiez mesurer le temps pendant lequel un bouton est appuyé, vous pourriez avoir le code suivant :
Sélectionnez
const byte brocheBouton = 2;
unsigned long duree;
void setup() {
[Link](115200); // configuration de la voie série
à 115200 bauds
pinMode(brocheBouton, INPUT_PULLUP); // broche en entrée, activation
du pullup (pin HIGH par défaut)
}
void loop() {
duree = pulseIn(brocheBouton, LOW); // mesure de la durée LOW, après
une transition HIGH -> LOW
[Link](duree); // affichage du résultat
[Link](F(" µs"));
}
IV. Gestion asynchrone▲
Comme nous l'avons dit précédemment, lorsque vous utilisez la fonction delay () ou delayMicroseconds () tout le programme s’arrête. Ce n'est pas toujours
un souci, si le programme n'a rien d'autre à effectuer, mais dans cer tains cas c'est gênant par exemple si vous souhaitez continuer à surveiller une entrée et
répondre rapidement.
Par exemple, nous voulons faire clignoter une LED à une cer taine fréquence, sauf si un bouton est appuyé. On pourrait modifier le programme précédent de
clignotement ainsi :
Connexion Inscription Contacts
Sélectionnez
const byte brocheLed = 3; // le N° de broche de la LED
const byte brocheBouton = 2; // le N° de broche du bouton
void setup() {
pinMode(brocheLed, OUTPUT); // on met la broche en sortie
pinMode(brocheBouton, INPUT_PULLUP); // broche en entrée, activation
du pullup (pin HIGH par défaut)
}
void loop()
{
if (digitalRead(brocheBouton) == HIGH) { // si le bouton n'est pas
appuyé
digitalWrite(brocheLed, HIGH); // allume la LED
delay(1000); // bloquer pendant 1000
millisecondes (1s)
digitalWrite(brocheLed, LOW); // éteint la LED
delay(1000); // bloquer pendant 1000
millisecondes (1s)
}
}
Le souci cependant c'est que si on appuie sur le bouton juste après avoir allumé la LED, rien ne va se passer et si on ne le tient pas appuyé plus de 2
secondes, le code ne verra pas la broche à LOW lors du prochain tour de boucle : notre programme n'est pas réactif, l'expérience utilisateur est mauvaise.
Pour corriger cela, il faut écrire un programme qui ne va pas bloquer le microcontrôleur.
On va donc commencer par écrire un clignotement qui ne bloque pas, sans delay (). Il suffit de mémoriser le moment de dernier changement d'état de la LED
(debut) et laisser la LED dans cet état jusqu'à ce qu'une cer taine durée se soit écoulée. À ce moment, on inverse à nouveau l'état de la LED, on note ce
nouveau moment et on recommence.
Connexion Inscription Contacts
Sélectionnez
const byte brocheLed = 3; // le N° de broche de la LED
unsigned long debut;
void inverserLed() {
// on inverse l'état de la broche
if (digitalRead(brocheLed) == HIGH) {
digitalWrite(brocheLed, LOW); // éteint la LED
} else {
digitalWrite(brocheLed, HIGH); // allume la LED
}
// on mémorise le moment de changement d'état
debut = millis();
}
void setup() {
pinMode(brocheLed, OUTPUT); // on met la broche en sortie
inverserLed(); // on active la LED
}
void loop()
{
unsigned long maintenant = millis();
if (maintenant - debut >= 1000) { // si plus d'une seconde s'est
écoulée depuis le début du changement d'état
inverserLed(); // alors on inverse l'état
}
}
Tout le secret est donc dans le test :
Sélectionnez
if (maintenant - debut >= 1000)
que l'on répète dans la boucle. Le calcul dit : « si la durée écoulée entre le début et maintenant dépasse 1000 millisecondes alors… ».
Vous remarquez qu'il n'y a pas de else . Donc si le temps n'est pas écoulé, on ne fait rien et la boucle va juste reboucler et on va continuer à tester si c'est le
bon moment. Ça ne change donc pas trop du code précédent, on clignote, mais la grande différence, c'est que l'on peut donc effectuer d'autres traitements
si le temps n'est pas écoulé.
Par exemple, on pourrait dire « si le bouton est appuyé, éteindre la LED et ne plus clignoter ».
Sélectionnez
Connexion Inscription Contacts
const byte brocheLed = 13; // le N° de broche de la LED
const byte brocheBouton = 2; // le N° de broche du bouton
unsigned long debut;
void inverserLed() {
// on inverse l'état de la broche
if (digitalRead(brocheLed) == HIGH) {
digitalWrite(brocheLed, LOW); // éteint la LED
} else {
digitalWrite(brocheLed, HIGH); // allume la LED
}
// on mémorise le moment de changement d'état
debut = millis();
}
void setup() {
pinMode(brocheLed, OUTPUT); // on met la broche en sortie
pinMode(brocheBouton, INPUT_PULLUP); // broche en entrée, activation
du pullup (pin HIGH par défaut)
inverserLed(); // on active la LED
}
void loop()
{
if (digitalRead(brocheBouton) == HIGH) { // si le bouton n'est pas
appuyé on regarde s'il faut clignoter
unsigned long maintenant = millis();
if (maintenant - debut >= 1000) { // si plus d'une seconde s'est
écoulée depuis le début du changement d'état
inverserLed(); // alors on inverse l'état
}
} else { // le bouton est appuyé
digitalWrite(brocheLed, LOW); // on s'assure que la LED est
éteinte
}
}
Dans ce code, on va regarder si c'est le moment de clignoter seulement si le bouton n'est pas appuyé, sinon on éteint la LED. Comme nous n'avons plus de
delay (), le code ne bloque jamais et donc notre programme sera maintenant très réactif : dès que vous pressez le bouton, la LED s'éteint et dès que vous le
relâchez, elle se remet à clignoter.
Une erreur fréquente est de prendre un int ou unsigned int comme type de variable pour représenter le temps (debut, maintenant). Sur un UNO ou MEGA un
int ne peut pas aller au-delà de 32 767 et un unsigned int au-delà de 65 535 – soit environ 33 ou 66 secondes. Donc si vous testez votre code pendant
quelques secondes, vous n'allez rien déceler, mais en production, très vite votre code aura un compor tement anormal. Il est donc impor tant de toujours
prendre un unsigned long (que l'on note aussi uint32_t pour unsigned int sur 32 bits) pour représenter le temps.
Vous remarquerez que le test sur la durée est écrit sous la forme maintenant - debut >= 1000 . On voit souvent des codes avec maintenant >= debut + 1000 et
c'est mathématiquement similaire dans le cas général. Cependant, il faut retenir la première formulation et bannir la seconde si votre code doit tourner
longtemps. En effet, quand vous approcherez des ~50 jours fatidiques, le unsigned long qui permet de stocker debut va s'approcher de sa valeur maximale.
Rajouter 1000 à cette valeur va donc faire déborder ce qui est représentable et on se retrouvera avec une valeur proche de zéro.
La valeur de millis () (maintenant) sera elle très grande (proche des 50 jours) et donc le test maintenant >= debut + 1000 sera tout de suite vrai, sans avoir
attendu les 1000 ms. En prenant l'approche par soustraction, même lorsque maintenant a débordé et retourné à 0 et que debut est proche de 50 jours,
maintenant - debut représentera bien la bonne durée, et le test sera valable.
V. Pour aller plus loin▲
Nous avons décrit la gestion du temps au niveau du microcontrôleur, mais pas forcément ce que vous dit votre montre : « Samedi 13 mars 2021 à 20h30 ».
Votre Arduino n'a pas de notion du jour et de l'heure, il sait juste depuis quand il a été allumé.
Si vous avez besoin de gérer cela, il vous faudra un composant externe, une horloge, appelée RTC (Real Time Clock). Celles que l'on trouve le plus
fréquemment sont les DS1307 et DS3231/DS3232. Si vous avez le choix, laissez de côté la DS1307 (elle dérive trop vite et ne sera pas précise sur plusieurs
semaines/mois) et préférez une DS3231 (ou DS3232).
Il existe des alternatives : si votre Arduino dispose d'une connexion à Internet, vous pouvez interroger un serveur de temps (serveur NTP) ou si un GPS est
connecté sur votre montage, il est généralement possible d'obtenir l'heure grâce aux satellites GPS.
Et si vous avez encore des questions, n’hésitez pas à ouvrir une discussion dans le forum Arduino.
Vous avez aimé ce tutoriel ? Alors par tagez-le en cliquant sur les boutons suivants : Partager
En complément sur [Link]
Les Cahiers Pratiques Arduino, une série de tutoriels pour débuter
Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une
œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2021 Team Developpez. Aucune reproduction, même par tielle, ne peut être faite de ce site
ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans
de prison et jusqu'à 300 000 € de dommages et intérêts.
© 2000-2024 - [Link]
Par tenaire : Hébergement Web
Connexion Inscription Contacts