LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

mardi 12 décembre 2017

46 visiteurs en ce moment

Comment gérer le temps dans un programme ?

et comment faire plusieurs tâches simultanément

. Par : Dominique, Guillaume, Jean-Luc

Dès vos premières applications, vous avez pu constater que la gestion du temps revient très fréquemment dans la programmation des diverses applications qui nous concernent. Cela va de choses simples comme les animations lumineuses à base de DEL   comme dans « Un chenillard de DEL » ou « Enseigne de magasin » ou bien l’envoi d’une impulsion de durée déterminée à un moteur d’aiguillage électromagnétique, par exemple, à des choses plus complexes comme la détection du début et de la fin du passage d’un train avec un capteur infrarouge en distinguant les intervalles entre les wagons qui génèrent des impulsions courtes et la fin du passage qui génère une impulsion d’une longueur qui dépend du moment où un autre train va se présenter.

Nous avons déjà vu dans l’exemple de programme à la fin de « La programmation, qu’est ce que c’est » le clignotement d’une DEL  . Pour rappel, la fonction loop() intiale était comme ceci :

  1. void loop() {
  2. digitalWrite(13, HIGH);
  3. digitalWrite(13, LOW);
  4. }

La broche 13 correspondant à la DEL qui se trouve sur toutes les cartes Arduino. Dans ce cas, l’exécution est si rapide que nos yeux ne voient pas le clignotement de la DEL [1].

Supposons une animation de travaux avec un feu qui flashe toutes les secondes. Comment incorporer ce laps de temps dans le programme précédent ?.

En première approche, nous cherchons à faire des choses simples et nous allons naturellement nous tourner vers la fonction delay(...).

La fonction delay(...)

Cette première fonction est simple. Comme son nom l’indique, elle ajoute un délai pendant lequel la carte arduino marque une pause dans l’exécution du code. Entre parenthèses, unique argument de la fonction, se place le temps de la pause dont l’unité est la milliseconde. Ce qui ajouté au programme donne :

  1. void loop() {
  2. digitalWrite(13, HIGH);
  3. delay(20);
  4. digitalWrite(13, LOW);
  5. delay(980);
  6. }

et ainsi la DEL émet un flash de 20ms toutes les secondes.

Si nous voulons une pause plus précise que la milliseconde, il existe la fonction delayMicroseconds(...). Entre parenthèses se place le temps en microsecondes.

L’inconvénient majeur de ces deux fonctions est que pendant le temps spécifié, la carte est bloquée, elle ne peut pas continuer l’exécution du programme. Or, par exemple, de nombreuses tâches nécessitent une lecture fréquente des broches programmées en entrée : détection de train, lecture d’un bouton poussoir.

Nous allons prendre un exemple de la vie de tous les jours pour illustrer le propos. Imaginez que vous avez à mener de front deux tâches : faire cuire un œuf à la coque et guetter le facteur pour lui remettre une lettre qui ne vous est pas destiné. Si vous restez planté devant la pendule pour minuter l’œuf à la coque, vous raterez le facteur. C’est ce que l’on fait en utilisant delay(...), on attend devant la pendule et on ne fait rien d’autre. Si vous restez planté devant la fenêtre à guetter le facteur, votre œuf sera trop cuit.

L’autre solution consiste à jeter régulièrement un œil à la pendule tout en guettant le facteur. Si le temps de cuisson de l’œuf est atteint, on le sort de l’eau. De cette manière il est possible de faire deux choses presque en même temps.

Nous allons donc voir maintenant cette autre fonction qui permet de jeter un œil à la pendule.

La fonction millis()

La fonction millis() renvoie une date [2] qui est le nombre de millisecondes qui se sont écoulées depuis la mise sous tension ou le dernier reset. Contrairement à delay(...), cette fonction n’est pas bloquante [3], elle permet juste de jeter un œil à la pendule. Il faut ensuite utiliser la date pour décider de ce que le programme doit faire.

Reprenons l’exemple simple de la DEL clignotante. Utilisons tout d’abord une variable pour mémoriser l’état de la DEL et une variable pour mémoriser la date du dernier changement d’état de la DEL. Comme millis() renvoie une valeur de type unsigned long [4], cette date est un unsigned long initialisé à 0 :

  1. byte etatDEL = HIGH;
  2. unsigned long dateDernierChangement = 0;

Ensuite, dans loop(), nous allons jeter un œil à la pendule en appelant la fonction millis() et stocker son résultat dans une variable dateCourante. Ensuite, nous allons calculer l’intervalle de temps en soustrayant dateDernierChangement à dateCourante puis, selon l’état de la DEL et l’intervalle de temps, décider si on doit l’allumer ou l’éteindre. Enfin, si un changement d’état de la DEL a eu lieu, il faut mémoriser dans dateDernierChangement la date de l’événement de manière à pouvoir calculer le prochain intervalle :

  1. void loop()
  2. {
  3. unsigned long dateCourante = millis();
  4. unsigned long intervalle = dateCourante - dateDernierChangement;
  5.  
  6. if (etatDEL == HIGH && intervalle > 20) {
  7. // extinction de la DEL car elle est allumee (HIGH) et 20ms se sont écoulées
  8. etatDEL = LOW;
  9. digitalWrite(13, etatDEL);
  10. dateDernierChangement = dateCourante;
  11. }
  12. else if (etatDEL == LOW && intervalle > 980) {
  13. // allumage de la DEL car elle est éteinte (LOW) et 980ms se sont écoulées
  14. etatDEL = HIGH;
  15. digitalWrite(13, etatDEL);
  16. dateDernierChangement = dateCourante;
  17. }
  18. }

Télécharger

Pour l’instant, le progrès n’est pas spectaculaire. On a écrit plus de code mais le programme fait exactement la même chose. Ajoutons maintenant une seconde DEL avec un cycle d’allumage différent. On va gérer simultanément les deux DEL. On appelle cela du multiplexage. Cette seconde DEL est connectée sur la broche 12 et s’allume lorsque cette broche est HIGH. Elle va être allumée pendant 100ms et éteinte pendant 200ms. Pour savoir comment raccorder une DEL, reportez vous à « Fonctionnement et pilotage d’une DEL ».

Nous allons avoir besoin de deux fois plus de variables puisque nous avons maintenant deux DEL. Voici les déclarations de ces variables dont les noms sont suffixés par le numéro de broche de la DEL concernée.

  1. byte etatDEL13 = HIGH;
  2. byte etatDEL12 = HIGH;
  3. unsigned long dateDernierChangement13 = 0;
  4. unsigned long dateDernierChangement12 = 0;

Pour clarifier le programme, nous allons créer deux fonctions. La première va gérer la DEL de la broche 13 et la seconde va gérer la DEL de la broche 12.

Voici pour commencer la fonction qui gère la DEL de la broche 13, c’est un simple copier-coller du code qui est dans loop() ci-dessus :

  1. void flashDEL13()
  2. {
  3. unsigned long dateCourante = millis();
  4. unsigned long intervalle = dateCourante - dateDernierChangement13;
  5.  
  6. if (etatDEL13 == HIGH && intervalle > 20) {
  7. // extinction de la DEL car elle est allumée (HIGH) et 20ms se sont écoulées
  8. etatDEL13 = LOW;
  9. digitalWrite(13, etatDEL13);
  10. dateDernierChangement13 = dateCourante;
  11. }
  12. else if (etatDEL13 == LOW && intervalle > 980) {
  13. // allumage de la DEL car elle est éteinte (LOW) et 980ms se sont écoulées
  14. etatDEL13 = HIGH;
  15. digitalWrite(13, etatDEL13);
  16. dateDernierChangement13 = dateCourante;
  17. }
  18. }

La seconde fonction, flashDEL12() est similaire. Seules changent les intervalles, les variables de mémorisation de l’état de la DEL et de la date du dernier changement et, bien sûr, la broche :

  1. void flashDEL12()
  2. {
  3. unsigned long dateCourante = millis();
  4. unsigned long intervalle = dateCourante - dateDernierChangement12;
  5.  
  6. if (etatDEL12 == HIGH && intervalle > 100) {
  7. // extinction de la DEL car elle est allumée (HIGH) et 100ms se sont écoulées
  8. etatDEL12 = LOW;
  9. digitalWrite(12, etatDEL12);
  10. dateDernierChangement12 = dateCourante;
  11. }
  12. else if (etatDEL12 == LOW && intervalle > 200) {
  13. // allumage de la DEL car elle est éteinte (LOW) et 200ms se sont écoulées
  14. etatDEL12 = HIGH;
  15. digitalWrite(12, etatDEL12);
  16. dateDernierChangement12 = dateCourante;
  17. }
  18. }

Enfin, dans loop(), nous allons appeler les deux fonctions que nous venons de voir.

  1. void loop()
  2. {
  3. flashDEL13();
  4. flashDEL12();
  5. }

Voici une vidéo montrant l’exécution de ce programme

Voici également le programme à télécharger :

multiplex.ino
Programme de multiplexage de deux clignotements simultanés de DEL.

Le programme n’est pas d’une grande élégance. Les deux fonctions ne diffèrent que par les variables utilisées. On pourrait tout à fait ajouter les variables utilisées en argument pour n’avoir qu’une fonction mais une solution encore plus élégante est d’utiliser des objets. Nous verrons ultérieurement comment s’y prendre.

Pour terminer, il existe également une fonction micros() qui donne la date en microsecondes. Cette fonction renvoie également une donnée de type unsigned long.

Il est aussi possible de gérer le temps via les timers. Pour en savoir plus, lisez « Les Timers (I) »

[1Mesuré à l’oscilloscope, le temps qui s’écoule entre la mise à HIGH de la sortie 13 et la mise à LOW est de 3,94µs et le temps qui s’écoule entre la mise à LOW et la mise à HIGH est de 4,56µs.

[2date dans le sens instant précis dans le temps et non date calendaire.

[3L’exécution ne se bloque pas en attendant que le temps passe.

[4La date maximum est donc 4294967295 millisecondes, soit un peu plus de 1193 heures, soit un peu plus de 49 jours.

18 Messages

Réagissez à « Comment gérer le temps dans un programme ? »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Comment gérer le temps dans un programme ?

La programmation, qu’est ce que c’est

Types, constantes et variables

Installation de l’IDE Arduino

Répéter des instructions : les boucles

Les interruptions (1)

Instructions conditionnelles : le if … else

Instructions conditionnelles : le switch … case

Comment gérer l’aléatoire ?

Calculer avec l’Arduino (1)

Calculer avec l’Arduino (2)

Les structures

Systèmes de numération

Les fonctions

Trois façons de déclarer des constantes

Transcription d’un programme simple en programmation objet

Ces tableaux qui peuvent nous simplifier le développement Arduino

Les chaînes de caractères

Trucs, astuces et choses à ne pas faire !

Processing pour nos trains

Arduino : toute première fois !

Démarrer en Processing (1)

Le monde des objets (1)

Le monde des objets (2)

Le monde des objets (3)

Le monde des objets (4)

Les pointeurs (1)

Les pointeurs (2)

Les Timers (I)

Les Timers (II)

Les Timers (III)

Les Timers (IV)

Les Timers (V)

Bien utiliser l’IDE d’Arduino (1)

Bien utiliser l’IDE d’Arduino (2)

Piloter son Arduino avec son navigateur web et Node.js (1)

Piloter son Arduino avec son navigateur web et Node.js (2)

Piloter son Arduino avec son navigateur web et Node.js (3)

Les derniers articles

Ces tableaux qui peuvent nous simplifier le développement Arduino


bobyAndCo

Processing pour nos trains


Pierre59

Piloter son Arduino avec son navigateur web et Node.js (3)


bobyAndCo

Piloter son Arduino avec son navigateur web et Node.js (2)


bobyAndCo

Démarrer en Processing (1)


DDEFF

Piloter son Arduino avec son navigateur web et Node.js (1)


bobyAndCo

Arduino : toute première fois !


Christian

Bien utiliser l’IDE d’Arduino (2)


Thierry

Bien utiliser l’IDE d’Arduino (1)


Christian, Dominique, Jean-Luc, Thierry

Les Timers (V)


Christian

Les articles les plus lus

Les interruptions (1)

Les Timers (I)

Piloter son Arduino avec son navigateur web et Node.js (1)

Les Timers (II)

Comment gérer le temps dans un programme ?

Calculer avec l’Arduino (1)

Les Timers (III)

Les Timers (IV)

Répéter des instructions : les boucles

Les chaînes de caractères