Une solution pour ne pas avoir de problème avec millis() sur plus de 49 Jours

Portrait de brossden

Lorsque on veut positionner une temporisation dans un programme Arduino il existe deux solutions.

  • La première c'est avec la fonction 'delay(x)' mais le problème c'est qu'avec cette solution le programme attend que le délais 'x' soit passer pour continuer à exécuter le code. Dans ce cas il ne fait plus rien et dès que le temps 'x' est écoulé le programme saute à la ligne suivante jusqu'au prochain 'delay(y)' s'il y en a un. Donc dans beaucoup de cas 'delay(x)' ne convient pas.
  • La seconde c'est d'utiliser millis(). Cette instruction renvoi le temps en milliseconde depuis que le module Arduino a commencé à exécuter le code avec lequel il a été programmé.
    Pour utiliser cette solution il faut attribuer à une variable de type 'unsigned long' la valeur de millis() + la valeur du délais à respecter en milliseconde. Dans un deuxième temps on compare cette variable à 'millis()' et si 'millis()' est supérieure ou égale à celle-ci on exécute le code prévu.

Exemple :  Faire-clignoter-des-leds-des-frequences-differentes-ou-synchro

Comme toute les bonnes choses ont une fin la valeur de millis() fini par atteindre la valeur maximum du compteur 32 bits soit 0xFFFFFFFF ou  4,294,967,295 ce qui correspond à un peu plus de 49 jours. Exactement : 49 jours 17 heures 2 minutes 47 secondes 295 millisecondes, pour être précis !

Pour le programmes qui n'ont pas vocation à s'exécuter aussi longtemps il n'y a pas de soucis, mais pour ceux dont le temps de fonctionnement est supérieur à ce délais... c'est une autre affaire.
Que se passe t'il ? Et bien le compteur une fois arrivé à 4,294,967,295 soit 0xFFFFFFFF en hexa il repasse à 0 avec un flag de débordement. Comme je veux éviter ici de rentrer dans les détails disons que ce n'est pas facile à gérer.
Il serait plus simple de remettre à zéro ce millis() absolument quand on le désire, mais aucune instruction n'existe pour cette opération. 'millis() = 0;' ne fonctionne pas !
En cherchant sur le Net, comme je ne suis pas le seul à avoir rencontré ce problème, j'ai trouvé un code qui initialise ce millis() à volonté !

Je vous livre donc la solution non commentée (car elle n'est pas de mon cru !) dans un petit exemple qui initialise millis() toutes les 10 secondes (à vous de voir pour changer ce délais à votre convenance) c'est juste une façon de voir comment exploiter ce code.

extern volatile unsigned long timer0_overflow_count;
extern volatile unsigned long timer0_millis;
uint8_t old_SREG;

void ResetMillis() {
  old_SREG = SREG;
  cli();
  timer0_millis = 0;
  timer0_overflow_count = 0;
  SREG = old_SREG;
}
void setup()
{
  Serial.begin(230400);
}
void loop()
{
  Serial.println(millis());
  delay(1000);
  if (millis() > 10000)
    ResetMillis();

}

Attention que toutes vos temporisations soient écoulées avant d'utiliser 'ResetMillis() ' !!!!

Bonne journée à tous !

Portrait de Walter

bonjour à tous,

Du coup ca te force à faire des calcules et à mémoriser le nombre de fois que tu as réinitialiser ton compteur de milliseconde ?

Tu pourrais utiliser un "Timer" qui t'incrémente un compteur de minutes au lieu d'utiliser celui des millisecondes, je suppose que dans le cas évoqué on n'a pas besoin d'être précis à la second sur le délais passé?

Portrait de brossden

Bonjour Walter

Je ne vois pas ce que tu veux dire ?

Pourquoi des calculs ?

Comment ferais tu pour faire clignoter une led toutes les 7 secondes et une seconde toute les 13 secondes ?

Cela indéfiniment ??

De plus un compteur qui compterais tous le 49 jours .... pas trop pénible !

Sur un compteur 32 bits cela représente plus de 576 millions d'années, il y aura longtemps que je n'aurai plus mal aux dents !! :o)

Les dix secondes que j'ai prises dans mon exemple c'était pour vérifier la remise à zéro de millis()