Arduino Zeitmessung mit millis () ist nicht genau oder korrekt?

9

Ich habe das Arduino verwendet, um einige Daten aufzuzeichnen. In meiner Arduino-Skizze habe ich auch die millis()Funktion verwendet, um den Zeitpunkt zu verfolgen, zu dem jeder von mir gemessene Wert gemessen wird. Mir ist jedoch aufgefallen, dass das Timing nicht korrekt ist. Zum Beispiel werden 30 Sekunden im wirklichen Leben nur als 10 Sekunden ausgegeben (erfundenes Beispiel).

Habe ich Recht, wenn ich sage, dass die Arduino-Verzögerungsfunktion die Zeiteinsparung beeinflusst millis()? Mit anderen Worten, ich habe eine Verzögerung von 50 ms. Bedeutet das, dass die millis()Funktion auch für diese Dauer stoppt und dann für die Dauer der Verbindung fortgesetzt wird? Ich bemerkte dies, als ich versuchte, einige Daten zu zeichnen und feststellte, dass die Häufigkeit der Peaks in meinen Daten angesichts der verstrichenen Zeit zu häufig war. Ich möchte also wissen, ob dies der Grund für diese Nichtübereinstimmung des Timings ist, und wenn ja, wie kann ich dies beheben, damit ich die Zeit behalten kann, zu der jede Probe auftritt?

Um hier einen Kontext zu geben, ist meine Skizze:

#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
quelle
Verwenden Sie eines der offiziellen Uno-Boards?
Peter Bloomfield
1
Das tatsächliche Timing anstelle von erfundenen Werten (ein serieller Monitor, der die Zeilen mit einem Zeitstempel versehen kann, ist ideal) würde wahrscheinlich helfen, herauszufinden, was los ist.
Ignacio Vazquez-Abrams
3
Die Berechnung von millis()ist Interrupt-gesteuert, delay()sollte sich also nicht darauf auswirken.
Microtherion
Ich habe das gleiche Problem, aber nur, wenn ich es (millis ()) in komplexen Code integriere. Ich denke, die Komplexität des Codes beeinflusst seine Genauigkeit, da er sich immer mehr mit der Komplexität des Codes verzögert. Gibt es eine Möglichkeit, dies zu vermeiden? Vielleicht mit getrenntem RTC-Modul?
Josip7171

Antworten:

10

millis()ist Interrupt-gesteuert, hat also delay()keine Auswirkungen, zumindest nicht auf einem ATmega-basierten Board.

Das heißt nicht, dass millis()das auch völlig richtig ist. Jeder Tick des Timers ist nicht genau 1 ms, sondern 1,024 ms. Dieser Fehler sammelt sich allmählich an, bis eine Korrektur vorgenommen wird. Dies ist in der Implementierung des Interrupt-Handlers TIMER0_OVF (Timer 0 Overflow) zu sehen.

Eine weitere Ursache für Ungenauigkeiten ist der Oszillator / Kristall selbst, der nicht genau 16 MHz beträgt. Es ist jedoch ziemlich nahe und solange die Temperatur nicht zu stark ändert, ist es relativ stabil.

Das oben Gesagte bedeutet, dass Sie bei der Verwendung möglicherweise etwa 1 ms entfernt sind millis(). Das klingt nicht nach Ihrem Problem.

Ein weiteres potenzielles Problem wäre, was getECG()getan wird - es könnte sehr langsam sein.

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() ist langsam, aber nicht so langsam, dass eine solche Schleife beeinflusst wird.

Ein weiteres Problem, das ich gesehen habe, ist, wenn Leute die Taktrate ändern, aber board.txt nicht richtig ändern. Dies bedeutet, dass die in der millis()Implementierung verwendeten Konstanten falsch und die Zeiten falsch sind.

Wenn Sie tatsächlich alle 50 ms Werte lesen möchten, können Sie dies viel besser implementieren, indem Sie die folgenden Schritte ausführen

static long lastUpdate;

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

Wir müssten wirklich die Zeitstempel sehen, die Sie erhalten. Wenn Sie tatsächlich 30er als 10er sehen, ist noch etwas anderes am Werk.

Cybergibbons
quelle
2
Bitte beachten Sie, dass beim Uno die Uhr nicht kristallgetrieben ist, sondern nur einen Keramikresonator verwendet, der weniger genau ist als ein Kristall.
Jfpoilpret
@jfpoilpret Ah gut zu wissen. Mit Blick auf den Schaltplan wäre dies das CSTCE16M0V53-R0 Murata CERALOCK-Gerät .
Chris O
Resonatoren haben eine schlechte Anfangstoleranz (oft 0,5 bis 2%) und eine schlechte Temperaturstabilität. Wenn Sie jedoch Zeitschleifen bei der Verwendung kalibrieren, können sie in Ordnung sein, solange sich die Temperatur nicht bewegt.
Cybergibbons
2
Millis () arbeitet immer noch mit einem Timer, der alle 1,024 ms tickt, aber es wurde eine Fehlerkompensation in Form einer Inkrementierung hinzugefügt, wenn eine Fehlermessgröße zu hoch wird. Ich denke, es ist tatsächlich der Algorithmus von Roman Black. Das Timing sollte also genau näher an 1 ms liegen. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Für diejenigen, die noch interessiert sind, siehe den Kommentar, den ich zu JRoberts Antwort gepostet habe. Ich wollte nicht mit meiner eigenen Antwort antworten, da ich keine habe. Ich habe das Problem nur umformuliert.
user3284376
2

Wenn Interrupts für eine signifikante Bruchdauer des eHealth.getECG()Anrufs deaktiviert werden, kann millis()die Anzahl zurückfallen. Andernfalls millis()sollte die Zeit viel genauer zurückgegeben werden als die von Ihnen beschriebenen 3x-Fehler.

Sie sagten, dass Ihr abgetastetes Signal eine höhere Frequenz aufweist als erwartet. Dies kann passieren, wenn Ihre Abtastrate niedriger ist als beabsichtigt. Gehen Sie von einer Abtastrate von 20 Hz aus? Ihre Schleife könnte etwas länger als 50 ms dauern, was Sie in den gedruckten Zeiten sehen würden, aber diese sollten immer noch die Uhrzeit verfolgen. Wenn Sie dies nicht berücksichtigen, aber 50 ms / Probe annehmen, sehen Sie eine offensichtliche Beschleunigung der Daten.

Wenn dies nicht der Fall ist, besteht der nächste Schritt darin, einen Ausgang loop()umzuschalten, während Sie sich gerade befinden , und die Frequenz der resultierenden Rechteckwelle mit einem Frequenzmesser (einige kostengünstige DVMs können dies tun) oder einem Oszilloskop zu messen. Mach dasselbe mit einem leeren loop(). Das erste Experiment wird Ihre tatsächliche Abtastrate oder Ihr Intervall bestimmen. Die zweite zeigt Ihnen, ob millis()(dh die Timer0-Frequenz) Ihren Erwartungen entspricht.

JRobert
quelle
1
Ich habe weiter damit herumgespielt und festgestellt, dass das Problem nicht beim Arduino liegt, die millis () -Funktion funktioniert größtenteils sehr gut, einige der Werte sind nicht genau 8 ms (Verzögerung) voneinander entfernt, aber von was Sie haben gesagt, das ist zu erwarten. Der von mir beschriebene 3x-Fehler trifft auf die Python-Seite der Dinge zu, mit denen ich die Daten empfange. Jede Idee, woraus das resultieren könnte, ich verwende Pythons Pyserial und es ist höllisch langsam.
user3284376
Ich weiß nicht genug über Ihre Implementierung, um Ihnen mehr als eine 1/2 @ Vermutung zu geben, aber ist die Python-Seite langsam genug, um Samples abzulegen?
JRobert
0

Hier gilt das gleiche. Ich kann hinzufügen, dass bei ausgeschalteten Unterbrechungen die gemessene Zeit "Echtzeit" ist. Wie auch immer, ich verstehe nicht, warum diese Verzögerung, denn wenn die Schleife zu lange dauert, sollte millis () sowieso Echtzeitwerte zurückgeben (nur mit mehr Abstand zwischen jedem Wert)

user48711
quelle
1
Worauf bezieht sich "hier gleich"? Die Antworten sollten für sich allein stehen, da StackExchange die Dinge nachbestellen kann (im Gegensatz zu einem Forum). "Das Gleiche hier" kann also alles bedeuten, je nachdem, unter welcher Antwort / Frage Ihre Antwort darunter erscheint.
Nick Gammon
Dieser Beitrag wäre als Kommentar besser geeignet, obwohl Sie (zugegebenermaßen) keinen ausreichenden Ruf haben.
Greenonline
Entschuldigung, ich denke, wenn Sie etwas beantworten, ist es offensichtlich, dass es sich auf den Hauptbeitrag bezieht, sonst wäre es ein Kommentar
user48711