Go Down

Topic: Décalage horloge interne millis() (Read 5 times) previous topic - next topic

Bubule

Bonsoir

Ayant réalisé un petit programme de gestion d'oscillateurs à base de servomoteurs : http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1293371084

J'effectue le changement de paramètres (vitesses, courses, ...) en fonction de l'heure.
Pour ce faire, j'ai créé une horloge interne à base de l'instruction millis().
Mais un problème de taille me pose problème. La valeur millis n'évolue pas au rythme souhaité. Normalement, un décalage de 1000 correspond à 1 seconde, et dans mon cas, cela va trop vite. J'ai donc recalé le comptage par une simple règle de trois, mais je me rends compte que le décalage est aléatoire.
parfois cela avance, parfois cela retarde.
J'ai tout d'abord pensé que cela provenait de la taille du programme et que l'horloge s'apparentait plus à un compteur de cycle où la longueur du programme faisait varier le rythme de la variable interne millis.
J'ai donc créé un tout petit programme allumant la LED avec une impulsion toutes les minutes pour vérifier ma théorie.
Effectivement la valeur est légèrement différente pour clignoter toutes les minutes, mais toujours pas dans la norme (je devrais en théorie avoir 600 pour incrémenter toutes les minutes).

Voici le bout de code :
Code: [Select]
 
// Déclaration des variables et constantes horloge
 long compteur_top=0;        // Initialisation des tops horloge
 long ancien_millis=0;       // Mémoire de l'horloge millis
 int minute = 5;            // Minutes de l'horloge
 int heure = 19;             // Heures de l'horloge (avec inscription "19h" comme heure initiale)
 
 
  // Sortie LED
 const int led_int = 13;
 int etat_led = LOW;



void setup() {

 
 // Adressage des sorties
 pinMode (led_int, OUTPUT);

 // Initialise la liaison série
 Serial.begin(19200);
  delay(50);

}



void loop() {
 
// Création d'une impulsion temporisée (top) pour déplacements oscillateurs sur la base de l'horloge interne millis()
 int top;
     if (millis() >= ancien_millis) { // Vérifie que le temps millis() est bien supérieur à l'ancienne valeur mémorisée
                                      // >> risque à l'init et au retour à zéro de l'horloge interne.
     if (millis() - ancien_millis >= 100){ top = 1;  // création d'un top toutes les 100ms
             ancien_millis = millis();               // réinitialisation ancien_millis
         compteur_top = compteur_top + 1; }          // Incrémente le compteur de tops
                     
                     else {delay(100-(millis()-ancien_millis));}
     }
       
// HORLOGE
   if (compteur_top >= 498) {minute = minute + 1; //Incrémentation des minutes  
        // (498 au lieu de 600 dans ce cas pour avoir une incrémentation toutes les minutes)
   
       compteur_top = 0;  
 etat_led = HIGH;      
   }
   else etat_led = LOW;
   
   if (minute >= 60) {heure = heure + 1; //Incrémentation des heures
       minute = 0;}
   if (heure >=24) {heure = 0;} // réinitialise l'horloge toutes les 24h
 
   
   
// LIAISON SERIE POUR TESTS

if (top = 1){
   
     Serial.print(heure);
     Serial.print("     ");  
     Serial.print(minute);
   
     Serial.println("   ");
   
     Serial.println("   ");
 
     Serial.println("     ");
     
}

// Remise à zéro impulsion "Top"
 top = 0;
 

 //    Serial.print("   ");
     Serial.println("  ");
     Serial.println("   ");
     Serial.println("   ");
     

     
         
       digitalWrite(led_int, etat_led);
     
     
}


Ma carte aurait-elle un problème, ou la valeur millis est naturellement inexploitable ?

Merci pour votre aide
Franck

Snootlab

Bonjour,

ça me semble un gros offset tout de même,
Je viens de refaire le montage DS1307 avec le code suivant (trèèèès fortement issu du lien ci dessus)

Code: [Select]
//ChronoDot test [ds1307]
// Based on http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1279385586

#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h> // written by  mattt on the Arduino forum and modified by D. Sjunnesson

#define INT_PIN 3  //DS1307 sqw wire

extern volatile unsigned long timer0_millis;
volatile unsigned long t, now, prev;

void setup() {
 //init wire & serial
 Wire.begin();
 Serial.begin(9600);
 Wire.beginTransmission(104); // transmit to device #104, the ds 1307
 Wire.send(0x07);  //select control register
 Wire.send(0x10);  //set sqw at specified Hz of  0=1Hz
 Wire.endTransmission();

 //init interrupt
 pinMode(INT_PIN, INPUT);
 attachInterrupt(0, OneHzInterrupt, FALLING);  //0 = digital pin 3
}


void loop() {
Serial.println(t);
 delay(1000);
}


void OneHzInterrupt(void) {
now = timer0_millis;
 t = now - prev;
 prev = now;
}


et j'ai ce genre de résultat :


Code: [Select]
1000
1000
999
1001
1000
1000
1000
1000
1000
999
1001
999
1001
1000
1000
1000
1000
1000
999
1001
1000
1000
1000
1000
1000
999
1001
1000
1000
1000
1000
1000
1000
1000
1000
1000
1000
1000
1000
1000
1000
1001
999
1000
1000
1000
1000
1000
1001
999
1000
1000
1000
1000
1000
1001
999
1001
999
1000
1000
1000
1001
999
1001
999
1000
1000
1000
1001
999


Donc c'est de l'ordre de moins de 1ms par seconde de dérive, ce n'est pas du même ordre que ce que tu constates, doit y avoir un souci dans le code, n'étant pas vraiment une star du code, je ne sais pas dire ou est l'erreur dans le tien

Par contre, il existe la librairie Time pour simplifier l'usage de l'heure dans les sketches, réglable par le terminal série par exemple
http://www.arduino.cc/playground/Code/Time

Ceci dit, concernant de la gestion horaire, je pense que tu devrais regarder tu coté des horloges RTC (Real Time Clock)

Les plus célèbres sont basées sur le DS1307 ou le Chronodot (DS1321 pour plus de précision)
Elles sont autonomes (batterie lithium) donc ne craignant aucunement la coupure secteur.

Leur utilisation est bien documentée, elles utilisent le bus I2c (entrées Analogiques 4 & 5) et c'est quand même bien plus simple de jouer avec le temps dans ce cas.

Bon courage,

Lionel



- Distributeur officiel Arduino - Conception de shields pour les bricoleurs
- Distributeur officiel Arduino - France

Jean-François

#2
Jan 18, 2011, 12:12 pm Last Edit: Jan 18, 2011, 12:13 pm by jfs Reason: 1
Ce que je ne comprend pas c'est la raison pour laquelle tu utilises millis() avec un delay si millis() n'est pas à la valeur que tu attends :

Code: [Select]
else {delay(100-(millis()-ancien_millis));}


Essaye simplement de supprimer ce delay() :

Code: [Select]
else {;;} // ou sans les ;;


Ton offset viens peut-être de là.


Ensuite, incrémente tes top ainsi :

Code: [Select]
compteur_top ++; }    

MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Bubule

Bonjour

Merci à tous les deux pour votre aide !  :)

Lionel
J'avais vu cette option "librairie time", mais je n'ai pas compris comment la télécharger (si vous pouviez m'aider, je suis preneur  :) )

Pour l'utilisation d'une horloge externe RTC, je pensais pouvoir m'en passer au regard du besoin de précision relativement faible dont j'ai besoin. Mais là, c'est énorme !

Jean François
Tout le développement que j'avais réalisé était sans le else (delay). Et le décalage était présent.
Comme j'ai supposé que l'horloge se décalait à cause du temps de scrutation, je m'étais dit qu'une pause ne ferait pas de mal au processeur. Et ai donc rajouté cette instruction qui repose le proc. en attendant le temps nécessaire à l'obtention du top 100ms. Cela n'a rien changé dans le décalage, mais permet de scruter moins souvent  ;).
Là n'est donc pas le problème

Oui, pour l'incrémentation, je vais modifier par "compteur_top ++; "



Si vous entrez la totalité du code donné en tête de post, je constate ce décalage. Constatez vous le même ?

Franck

Jean-François

J'ai effectivement un décalage d'une ou deux secondes au bout de quinze minutes.
MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Snootlab

Quote
J'avais vu cette option "librairie time", mais je n'ai pas compris comment la télécharger (si vous pouviez m'aider, je suis preneur   )


La librairie se trouve en suivant ce lien

http://www.arduino.cc/playground/uploads/Code/Time.zip

En fonction de ton OS il faut l'installer a l'endroit qui convient

Lionel
- Distributeur officiel Arduino - France

Jean-François

J'ai une hypothèse.
la précision est de 1 milliseconde, la résolution est de 4 millisecondes.

Comme on test un chiffre plus grand ou égal à l'écart de temps que l'on veut mesurer, au meilleur des cas on tombe sur le plus petit écart possible, au pire des cas on tombe sur un chiffre plus grand, donc une dérive est obligatoire.

Lors de chaque test if t tu tombes dans la fourchettes de résolution, dans le pire des cas tu as une seconde d'offset au bout de 4 minutes.
MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

#7
Jan 19, 2011, 12:06 am Last Edit: Jan 19, 2011, 08:35 am by jfs Reason: 1
J'ai sué un petit moment sur ton problème....

Je t'ai fait un petit bout de code avec commentaires, essaye le.
Après 30 minute de test, je n'ai pas de dérive flagrante.

Quote


// Déclaration des variables et constantes horloge

unsigned long refTemps=0;              // reference prise dans le flux du temps
                                 // on lance une ancre dans le flux du temps et on compte les noeuds qui défilent sur la corde
unsigned long diff=0;                // difference entre le temps voulu et le temps reel

int jour=18;                // Jour de l'horloge
int heure = 23;             // Heures de l'horloge
int minute = 32;            // Minutes de l'horloge
int seconde = 0;            // Secondes de l'horloge


void setup() {

  // Initialise la liaison série
  Serial.begin(19200);

}


void loop() {

  diff=millis()-refTemps-1000;      // on calcule les millisecondes qui seront perdues dans la comparaison de la boucle "if"
  if(millis()-refTemps>=1000){      // on compare a 1000 (1 seconde) le temps actuel moins la longueur de l'ancre jetee dans le flux du temps

    refTemps=millis();              // on jette a nouveau une ancre dans le flux du temps
    seconde++;                      // on additionne 1 seconde
    
    refTemps -= diff;                 // on enleve les millisecondes de retard a la position de l'ancre, ainsi l'echeance de la seconde arrive plus tot
    
    if(seconde>=60){                // lorsque les secondes arrivent a 60
      minute++;                     // on additionne 1 minute
      seconde=0;                    // et on remet les seconde a zero
      if(minute>=60){               // lorsque les minutes arrivent a 60
        heure++;                    // on additionne 1 heure
        minute=0;                   // et on remet .... etc, etc
        if(heure>=24){
          jour++;
          heure=0;

        }
      }
    }
  }

  // on envoie dans le moniteur serie le jour, l'heure, les minutes et les secondes....
  Serial.print(jour);
  Serial.print("     ");  
  Serial.print(heure);
  Serial.print("     ");  
  Serial.print(minute);
  Serial.print("     ");  
  Serial.println(seconde);

delay(100);

}




Tiens moi au courant  ;)
MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Bubule

Ouhaouuuu !  :o :o  :-X

Je vois qu'il y en a qui occupent leurs soirées  ;D

Merci Jean François
Bien vu la cause très probable du décalage. Je testerais l'intégration de cette astuce ce WE.

Lionel,
J'ai un portable dernière (presque) génération sous win7. Le soft Arduino installé est la version Française, sans remise à jour.
Je ferais également l'installation ce WE. Mais je n'ai pas tout compris dans la manière de procéder. Où dois-je dézipper ? Suffit-il d'écrire les instructions sans chargement préalable ? y'aurais pas un tuto qui traine des fois ?

Franck

Snootlab

#9
Jan 19, 2011, 02:55 pm Last Edit: Jan 19, 2011, 04:07 pm by snootlab Reason: 1
Bonjour,

Pour installer une nouvelle librairie, il faut faire comme suggéré sur la page suivante :
http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.Librairies

Quote
(...)Pour utiliser les librairies "hors référence" fournies par la communauté, il faut télécharger un fichier zip, le décompresser et copier le répertoire obtenu dans le répertoire /arduino-00xx/libraries/. La nouvelle librairie ainsi installée sera insérée dans un programme à l'aide de l'instruction #include.


Ou alors, il y a aussi la possibilités de place la librairie dans ton répertoire sketchbook, il faut la dézipper dans un dossier /libraries/ que tu auras crée dans ton répertoire sketchbook


Ensuite, il faut quitter l'appli et la relancer pour que les nouvelles librairies soient prises en compte lors de la compilation.

Souvent, les librairies intègrent des sketches d'exemple qui seront accessibles (une fois la lib installée) par le menu File/examples du GUI arduino.

Et je ne savais même pas qu'il y avait une version fr de l'arduino, par curiosité tu l'as trouvée ou ?

Par ailleurs, je te conseille plutôt de travailler avec la version à jour du GUI (22 à ce jour) les évolutions du GUI corrigent bien des bugs.

Lionel






- Distributeur officiel Arduino - Conception de shields qui s'empilent
- Distributeur officiel Arduino - France

Bubule

#10
Jan 19, 2011, 04:02 pm Last Edit: Jan 19, 2011, 04:02 pm by Bubule Reason: 1
Merci Lionel.

Je tenterais l'opération (et celle de Jean François) ce WE

Pour le soft en Français, b'en t'étais pas loin :
http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.TelechargerArduinoFrancais
(version 018)

Voilà

Le Pnume

Bonjour,

En ce qui concerne les libraries ajoutées, j'avais fait comme conseillé sur le site Arduino mais à la 1ere mise à jour du logiciel Arduino comme j'ai créée un nouveau répertoire pour la nouvelle version et que je n'avais pas copié le dossier "libraries", je ne pouvais plus compiler mes projets. J'ai cherché un moment avant de comprendre pourquoi ça ne marchait plus.  >:(
Maintenant j'ai  un dossier "librairies" dans le dossier sketchbook et j'y installe les librairies et plus de problème pour les mises à jour Arduino.

Serge

Jean-François

#12
Jan 19, 2011, 05:56 pm Last Edit: Jan 19, 2011, 08:36 pm by jfs Reason: 1
Sur dix heures d'essais, j'ai une dérive de 2 secondes.



J'ai une autre solution un peu plus simple, qui devrait fonctionner sans dérive :

Quote

// Déclaration des variables et constantes horloge

unsigned long refTemps;              // echeance placee dans le futur

int jour=19;                // Jour de l'horloge
int heure = 18;             // Heures de l'horloge
int minute = 6;            // Minutes de l'horloge
int seconde = 30;            // Secondes de l'horloge


void setup() {

  // Initialise la liaison série
  Serial.begin(19200);

}


void loop() {

  if (millis()>= refTemps){          // si millis est egal ou plus grand que l'echeance

    refTemps+=1000;                   //la derniere echeance + 1 seconde
    seconde++;                      // on additionne 1 seconde

    if(seconde>=60){                // lorsque les secondes arrivent a 60
      minute++;                     // on additionne 1 minute
      seconde=0;                    // et on remet les seconde a zero
      if(minute>=60){               // lorsque les minutes arrivent a 60
        heure++;                    // on additionne 1 heure
        minute=0;                   // et on remet .... etc, etc
        if(heure>=24){
          jour++;
          heure=0;

        }
      }
    }
  }

  // on envoie dans le moniteur serie le jour, l'heure, les minutes et les secondes....
  Serial.print(jour);
  Serial.print("     ");  
  Serial.print(heure);
  Serial.print("     ");  
  Serial.print(minute);
  Serial.print("     ");  
  Serial.println(seconde);
  delay(100);
}




La précision de l'affichage de la seconde doit être dans le 1/100 de seconde, mais dans la globalité on est précis, car l'erreur ne se cumule pas et on réajuste environ toutes les secondes sur une référence qui est exacte.
MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

#13
Jan 19, 2011, 10:21 pm Last Edit: Jan 20, 2011, 07:05 am by jfs Reason: 1
Après quatre heures d'essai(pour le dernier code), ça marche.... dans une moyenne.

Pendant une minute on retarde et la minute suivante on avance (de l'ordre de la 1/2 seconde) et ainsi de suite, donc en moyenne c'est "précis".  ;)



Edit: Après 13 heures de test j'ai une avance de 3 secondes due à la précision du quartz et du Mcu.
MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Bubule

#14
Jan 20, 2011, 10:17 am Last Edit: Jan 20, 2011, 01:54 pm by Bubule Reason: 1
Bonjour

3" sur 13 heures... Et moi qui pensais que la précision Suisse était indiscutable ;D ;D

Cela devrait me suffire. Car j'ai commandé un capteur photo-résistif. Je recalerais l'horloge au moment de la détection de l'allumage des HQI (éclairage du bac).

Cependant, je me demande comment cela se passera au débordement des variables "refTemps" et millis() puisqu'à aucun moment refTemps n'est réinitialisée.

Edit :
Si débordement de RefTemps avant millis : RefTemps < millis() = Pas de soucis
Si débordement de millis avant refTemps : RefTemps > millis = reste à traiter le cas car l'incrémentation ne se fait plus.

Code: [Select]
if millis() < 1000 {refTemps = 0} ;

En tous cas merci à tous. Je vous tiendrais au courant de l'évolution

Franck

Go Up