Le respect de l'heure Arduino en utilisant millis () n'est pas précis ou correct?

9

J'utilise l'Arduino pour enregistrer certaines données. Dans mon croquis Arduino, j'ai également utilisé la millis()fonction afin que je puisse garder une trace de l'heure à laquelle chaque valeur que je mesure est prise. Cependant, j'ai remarqué que le timing n'est pas correct. Par exemple, 30 secondes dans la vraie vie ne sortent que comme 10 secondes (exemple composé).

Ai-je raison de dire que la fonction de retard Arduino affecte la durée d'utilisation millis()? En d'autres termes, supposons que j'ai un retard de 50 ms, cela signifie-t-il que la millis()fonction s'arrête également pendant cette durée, puis continue et ainsi de suite pendant la durée de la connexion? Je l'ai remarqué lorsque j'ai essayé de tracer certaines données et de constater que la fréquence des pics dans mes données était trop fréquente compte tenu du temps qui s'était écoulé. Donc, je veux savoir si c'est le raisonnement pour cette inadéquation du timing et si oui, comment puis-je résoudre ce problème afin que je puisse garder l'heure à laquelle chaque échantillon se produit?

Pour donner un peu de contexte, voici mon croquis:

#include <eHealth.h>    

unsigned long time;
// The setup routine runs once when you press reset:
void setup() {
  Serial.begin(9600);  
}

// The loop routine runs over and over again forever:
void loop() {

  float ECG = eHealth.getECG();
  time = millis();
  Serial.print(time);
  Serial.print(" ");
  Serial.print(ECG, 5); 
  Serial.println("");    

  delay(50);
}
user3284376
la source
Utilisez-vous l'une des cartes officielles Uno?
Peter Bloomfield
1
Le timing réel au lieu des valeurs composées (un moniteur série qui horodate les lignes est idéal) aiderait probablement à comprendre ce qui se passe.
Ignacio Vazquez-Abrams
3
Le calcul de millis()est piloté par interruption, delay()il ne devrait donc pas l'affecter.
microtherion
J'ai le même problème mais seulement quand je l'intègre (millis ()) dans du code complexe. Je suppose que la complexité du code affecte sa précision dans la mesure où il va de plus en plus en retard avec la complexité du code. y-a-t-il un moyen d'éviter ça? peut-être en utilisant un module RTC séparé?
Josip7171

Réponses:

10

millis()est piloté par interruption, donc il delay()n'aura pas d'impact, du moins pas sur une carte basée sur ATmega.

Cela ne veut pas dire que millis()c'est tout à fait exact non plus. Chaque tick de la minuterie n'est pas exactement de 1 ms, mais est de 1,024 ms. Cette erreur s'accumule progressivement jusqu'à ce qu'une correction soit effectuée. Cela peut être vu dans l'implémentation du gestionnaire d'interruption TIMER0_OVF (timer 0 overflow).

Une autre source d'inexactitude est l'oscillateur / cristal lui-même, qui n'est pas exactement 16 MHz. Il est assez proche cependant, et tant que la température ne change pas trop, il est relativement stable.

Ce qui précède signifie que vous pourriez être à environ 1 ms pendant l'utilisation millis(). Cela ne ressemble pas à votre problème.

Un autre problème potentiel serait ce qui getECG()se passe - cela pourrait être très lent.

float eHealthClass::getECG(void)
    {
        float analog0;
        // Read from analogic in. 
        analog0=analogRead(0);
        // binary to voltage conversion
        return analog0 = (float)analog0 * 5 / 1023.0;   
    }

analogRead() est lent, mais pas assez lent pour avoir un impact sur une boucle comme celle-ci.

Un autre problème que j'ai vu est que les gens changent la vitesse d'horloge mais ne changent pas correctement boards.txt. Cela signifie que les constantes utilisées dans l' millis()implémentation sont incorrectes et que les temps sont incorrects.

Si vous voulez réellement lire les valeurs toutes les 50 ms, une bien meilleure façon de l'implémenter est de faire ce qui suit

static long lastUpdate;

if (millis() - lastUpdate > 50)
{
    lastUpdate = millis();
    //Do stuff
}

Nous aurions vraiment besoin de voir les horodatages que vous obtenez. Si vous voyez réellement 30s afficher comme 10s, alors il y a autre chose au travail.

Cybergibbons
la source
2
Veuillez noter que, pour l'Uno, l'horloge n'est pas à cristal, mais utilise simplement un résonateur en céramique qui est moins précis qu'un cristal.
jfpoilpret
@jfpoilpret Ah bon à savoir. En regardant le schéma , ce serait le dispositif CSTCE16M0V53-R0 Murata CERALOCK .
Chris O
Les résonateurs ont une tolérance initiale médiocre (souvent 0,5-2%) et une stabilité de température médiocre, mais si vous étalonnez des boucles de synchronisation lors de leur utilisation, elles peuvent être correctes tant que la température ne bouge pas.
Cybergibbons
2
Millis () fonctionne toujours sur un temporisateur qui se déclenche toutes les 1.024 ms, mais ils ont ajouté une compensation d'erreur, sous forme d'incrémentation chaque fois qu'une variable de compteur d'erreur devient trop élevée. Je pense que c'est l'algorithme de Roman Black en fait. Le timing devrait donc être beaucoup plus proche de 1 ms exactement. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Pour ceux qui sont toujours intéressés, voir le commentaire que j'ai posté sur la réponse de JRobert, je ne voulais pas répondre avec ma propre réponse car je n'en ai pas, je viens de reformuler le problème.
user3284376
2

Si les interruptions sont désactivées pour une eHealth.getECG()durée d'appel fractionnelle importante , millis()le nombre de 'pourrait prendre du retard. Sinon, cela millis()devrait renvoyer un temps beaucoup plus précis que les erreurs 3x que vous avez décrites.

Vous avez dit que votre signal échantillonné apparaît en fréquence plus élevée que ce à quoi vous vous attendiez, ce qui pourrait se produire si votre fréquence d'échantillonnage est inférieure à ce que vous vouliez. Vous supposez une fréquence d'échantillonnage de 20 Hz? Votre boucle pourrait prendre un peu plus de 50 ms, ce que vous verriez dans les temps imprimés, mais ceux-ci devraient toujours suivre l'heure de l'horloge. Si vous ne teniez pas compte de cela mais supposiez 50 ms / échantillon, vous verriez une accélération apparente des données.

Si ce n'est pas le problème, alors la prochaine étape serait de basculer une sortie pendant que vous êtes loop()dedans, et de mesurer la fréquence de l'onde carrée résultante avec un fréquencemètre (certains DVM peu coûteux peuvent le faire) ou une 'portée'. Faites la même chose avec un vide loop(). La première expérience sera votre taux ou intervalle d'échantillonnage réel; la seconde vous dira si millis()(c.-à-d. la fréquence du timer0) est ce que vous attendiez.

JRobert
la source
1
J'ai joué avec elle plus loin et j'ai réalisé que le problème n'est pas avec l'Arduino, la fonction millis () fonctionne très bien pour la plupart, certaines valeurs ne sont pas exactement à 8 ms (délai), mais de ce que vous avez dit qu'il fallait s'y attendre. L'erreur 3x que j'ai décrite est vraie du côté Python des choses que j'utilise pour recevoir les données. Toute idée de ce que cela pourrait être le résultat, j'utilise le physique de Python et c'est lent comme l'enfer.
user3284376
Je ne connais pas assez votre implémentation pour vous donner plus qu'une 1/2 @ deviner, mais le côté Python est-il assez lent pour supprimer des échantillons?
JRobert
0

Pareil ici. Je peux ajouter que si les interruptions sont désactivées, le temps mesuré est "temps réel". Quoi qu'il en soit, je ne comprends pas pourquoi ce retard, car si la boucle prend trop de temps, de toute façon les millis () devraient retourner des valeurs en temps réel (seulement avec plus de distance entre chaque valeur)

user48711
la source
1
À quoi se réfère "même ici"? Les réponses devraient être autonomes, car StackExchange peut réorganiser les choses (contrairement à un forum). Donc "même ici" pourrait signifier n'importe quoi selon la réponse / question à laquelle votre réponse apparaît en dessous.
Nick Gammon
Ce message serait plus approprié en tant que commentaire, bien que (certes) vous manquiez de réputation suffisante.
Greenonline
Désolé, je pense que lorsque vous répondez à quelque chose, il est évident que cela fait référence au message principal, sinon ce serait un commentaire
user48711