Zu geringe Genauigkeit von millis()

Hallo,

ich habe eine ältere und geschlossene Diskussion zum Thema gefunden, die leider meine Fragen nicht beantwortet. Deshalb dieser Beitrag und am Ende meine Fragen.

Ich nutze einen MEGA 2560 mit RTC unter Anderem zur zeitlichen Integration eines Wertes. Dieser kann sich von Zeit zu Zeit sprungweise ändern.
Beim "ersten" Sprung merke ich mir Zeit (millis()) und Wert. Beim nächsten Sprung dann wieder die beiden neuen Daten. Dann nehme ich den ersten Wert und multipliziere ihn mit der Differenz der beiden Werte aus millis().
Da immer Minutenwerte gewünscht sind, wird am Minutenende (von RTC-Zeit her) für die Restdauer seit der letzten Werteänderung genau so gehandelt. Die neue Minutenperiode startet dann mit genau diesem Wert aus millis().

Für die zeitliche Integration werden also immer die Werte aus millis() verwendet. Die RTC triggert nur, dass die Minute abzuschließen ist. Der Zeitwert aus der RTC selber geht nicht in die Integration ein.

Bei einem Test hatte ich den zu integrierenden Wert exakt konstant gehalten. Somit war der zu erwartende Integralwert einer Minute einfach zu berechnen. Hier fiel mir eine Abweichung auf. Alle aufeinander folgenden Minutenintegralwerte waren ca. 0,6 % zu niedrig. Nach viel Sucherei blieb eigentlich nur noch eine Abweichung der Werte aus millis() übrig.

Ein weiterer Test sah so aus. Bei jedem Minutenwechsel der RTC wurde ein Wert aus millis() ermittelt und über Serial ausgegeben. Benachbarte Werte hätten im Mittel 60.000 ms Abstand haben müssen. Nur im Mittel deshalb, weil der Minutenwechsel der RTC über Polling über die Bibliotheksfunktionen der RTC erkannt wird und in der Schleife noch einige andere Dinge erledigt werden. Da kann es jedes mal zu unterschiedlichen Verzögerungen zwischen dem tatsächlichen Minutenwechsel in der RTC und dem Aufruf von millis() kommen. Das muss sich aber über mehrere Minuten auf 60.000 ms ausmitteln.

Hier fand ich wieder die bekannte Abweichung von ca. -0,6 %.

Ich nutze im Programm keine Interrupts. Es laufen nur die "unter der Motorhaube" vom Arduino.
Der Mega hat, soweit sichtbar, einen Schwingquartz.
Die RTC läuft deutlich genauer, so mit einigen Sekunden Abweichung pro Tag.

-0,6 % wäre eine Abweichung von 6.000 ppm, viel zu viel für einen Schwingquartz. Selbst für einen einfachen Resonator wäre das recht viel.

Hat jemand eine Idee, woher diese Abweichung noch kommen könnte?
Hat jemand ähnliche Erfahrungen?

Nachtrag:
Hatte gerade mit einem Funkempfänger nach einem Signal bei 16 MHz (Taktfrequenz) gesucht. Fündig wurde ich bei 15,895 MHz. Das wäre genau die fragliche Abweichung von -0,6 %. Diese Abweichung ist ja schon heftig. Was haben die da nur verbaut?

Verwende doch micros() .
Oder was schmeckt dir daran nicht?

Mit deiner RTC solltest du ausmessen könne, wieviel micros eine Minute hat

Vermutlich einen Keramik Resonator!
Nachschauen kannst nur du. Denn das Ding liegt auf deinem Tisch.

Alternativ:
Wer es genauer möchte, macht sich einen Uhrenquarz dran.

Das kann auch der für USB sein.

Dann ist das eine schlechte RTC. Welche?

Gruß Tommy

Hallo,

ich glaube du verwechselst paar Dinge bzw. Zusammenhänge. Die üblichen Arduino Boards haben einen einfachen Resonator drauf, keinen Quarz. Der Takt ist damit weder von Haus perfekt stabil noch perfekt genau. Mit Quarz wäre er zwar stabiler aber immer noch unbekannt genau ohne Abgleich.
Die Qualität deiner RTC ist unbekannt. Selbst eine schon genaue RTC wie die DS3231N läuft zwar sehr stabil aber nicht unbedingt genau. Kann man aber per Register hinreichend genau abgleichen.
Und jetzt kommt das größte Problem noch obendrauf. Du kombinierst alle Taktquellen für eine Zeitenberechnung. Logisch das es untereinander Abweichungen gibt. Du kommst besser wenn du nur eine gemeinsame Taktquelle verwendest. Du kannst auch die RTC als 32kHz Taktquelle verwenden und damit einen Timer speisen und erstellst dir selbst einen ms Zähler ...
Egal wie du es machst, mit einer Taktquelle taktet alles syncron egal wie schief die Taktquelle läuft.

Vermutlich

Die auf dem Ada Loggershield, Typ müsste ich noch raussuchen.
Ich könnte die Drift über einen Parameter kompensieren, habe da aber wenig Arbeit reingesteckt. Die jetzige Genauigkeit (aktuell - 2 s / Tag) reicht für meine Anwendung völlig.

Da ist wohl etwas falsch rübergekommen. Ich kombiniere nicht die Taktquellen für EINE Zeitberechnung, zumindest im zweiten Test, ich vergleiche die Zeitabläufe der beiden Taktquellen. Die Integration selber beruht nur auf der internen Taktquelle und liefert hier einen zu niedrigen Wert. Durch das Triggern der Minutenintervalle durch die RTC ist mir der Fehler erst aufgefallen, zum Glück!

Das ist nicht mein Problem. Die Integration selber arbeitet nur mit einer Taktquelle (Systemtakt -> millis()). Die RTC triggert nur, dass eine Integrationsperiode abzuschließen ist. Der Abschluss selber nutzt wieder millis().
Störend ist, dass das Integrationsergebnis diesen Fehler von -0,6 % hat. Hätte ich nur mit der internen Taktquelle gearbeitet, wäre alles synchron verlaufen, hätte ich die Abweichung nicht bemerkt, sie wäre aber da gewesen und hätte mein Ergebnis verfälscht.

PCF8523 - Das wird nix

Gebe dir Recht laut INet ist der schlimmer als DS1307

Hallo,
wenn Deine millis() konstant 6% falsch laufen , dann kannst Du das doch einfach berücksichtigen und die 6% dazu rechnen, und einen "intergalaktischen Faktor" erfinden.

Bau Dir was, das mit millis() 60 Minuten läuft auf eine LED und messe das mit einer Stoppuhr. Das ist dann schon ziemlich genau. Mit einer Stoppuhr kannst Du 1/10s genau stoppen. Das gibt einen Fehler von 2/10s bei einer Gesamtzeit von 3600s.
Heinz

Hallo,

naja das meinte ich doch. :wink: Du vergleichst 2 Taktquellen miteinander. Jede Taktquelle hat bedingt eine Abweichung zur anderen. Man müßte beide exakt abgleichen können. Im Falle des µC Board Resonators nicht möglich.
Du musst den Fehler entweder rausrechnen oder dir einen "Arduino" suchen der mit Quarz betrieben wird. Dann stehen die Chancen zumindestens besser. Oder wie gesagt selbst einen ms Zähler mit RTC Taktquelle programmieren.

Zum PCF8523.
Der IC alleine kann nichts für falsche Taktung. Er benötigt ja eine externe Taktquelle wovon alles abhängt. Damit ist jeder Vergleich mit anderen RTCs die interne Taktquellen haben erstmal nicht vergleichbar. Ich gehe erstmal davon aus das er genauer taktet wie der µC Board Resonator. Wenn man sich nun das Datenblatt anschaut sieht man das auch er ein Register "Offset" hat. Damit kann man das Ding noch feintunen. Dazu nimmt man die Atomuhrzeit und beobachtet die Abweichungen über paar Wochen, korrigiert und beobachtet wieder.

Ich sehe die fehlende Temperaturkompensation noch als Faktor.
Andererseits hat eine DS3231 das alles.
32Khz und programmierbarer SQW-Abgang.

@user221210 bist Du auf das data-logger-shield festgenagelt oder könntest auch noch in eine verünftige Uhr investieren?

Hallo,

Ich sehe die fehlende Temperaturkompensation noch als Faktor.

oh ja das stimmt.

Die Frage die sich der TO stellen muss ist. Welche Taktquelle legt er als seine Referenz fest. Hätte es überhaupt ein Problem gegeben wenn die Abweichung nicht festgestellt wurden wäre? Man kann sich nämlich auch sehr schnell in einem Detail verlieren was ab einem bestimmten Detailgrad irrelevant ist. Wenn die Genauigkeit des PCF ausreichend ist kann man davon alles ableiten.

Ein Resonator wie zB auf dem orginalen UNO oder MEGA 2560 hat eine Genauigkeit von 0,5%. Darum hat millis() wie auch micros(), delay(), pulseIn() usw diese Ungenauigkeit gegenüber einer genaueren Taktquelle wie zb ein Quarz auf einer RTC. Die Quarze haben typischerweise ohne Kompensation eine Genauigkeit von 100ppm oder in % 0,01%. Durch Kalibrierung und Temperaturkompensation in einem begrenzten Temperaturbereich wie zB in einer DS3231 erreicht man eine Genauigkeit von 2 ppm.

Außerdem setzt sich die Ungenauigkeit aus verschiedenen Teilen zusammen. Einige Teile sind quantisierbar und kompensierbar, andere sind nicht vorhersehbar und darum nicht kompensierbar. Daraus ergibt sich, daß eine 100% Genauigkeit nicht möglich ist. Ein Ausmessen des Fehlers in einem bestimmten Moment sagt nicht, daß dieser Fehler zeitlich konstant bleibt und somit die Ungenauigkeit 0 sein wird.

Grüße Uwe

Quarzofen

Quarzofen bringt schon einiges, verhindert aber nicht die Alterung.

Ich glaube die konstanteste Frequenz bekommt man von Oszylatoren die mit der Trägerfrequenz des DFC77 synchronisiert sind (PLL Schaltungen).
Weiß nicht ob sowas auch mit den GPS Satelliten möglich ist.

oK man kann sich auch eine private Cäsiumuhr zulegen ...

Grüße Uwe

Ja nur dann spielt man bestimmt nicht mit Arduino :wink:

mich würde da ja mal ein Sketch vom TO interessieren der das Verhalten zeigt, mit einem konkreten Beispiel "um wie viel" die Millis() Minute von der Referenz abweicht.

Das Schlimme ist ja, dass hier meist nur Teilbeschreibungen nach dem Motto "so genau wie es technisch geht" aufschlagen, ohne dass man sich Gedanken über das (geheime) Gesamtsystem machen kann. Oft ist die geforderte Genauigkeit nicht wirklich erforderlich oder eine Änderung am Messverfahren eliminiert die Abweichungen.

Gruß Tommy

1 Like

MEGA (clone) mit DS3231

#include "RTClib.h"

RTC_DS3231 rtc;
DateTime now;

void timeinfo() {
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
 
    Serial.print(" @: ");
    Serial.print(rtc.getTemperature());
    Serial.println(" C");
}

void setup () {
  Serial.begin(115200);
  Serial.println(F("millis vs RTC"));
  if (! rtc.begin()) {
    Serial.println(F("Couldn't find RTC"));
    Serial.flush();
    while (1) delay(10);
  }
  if (rtc.lostPower()) {
    Serial.println(F("RTC lost power, let's set the time."));
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop() {
  static int previousMinute = -42;
  static uint32_t previousMillis = 0;
  now = rtc.now();
  if (previousMinute != now.minute()) {
    previousMinute = now.minute();
    uint32_t currentMillis = millis();
    Serial.print(currentMillis - previousMillis);
    Serial.print("\t");
    timeinfo();
    previousMillis = currentMillis;
  }
}

bringt:

15:51:06.244 -> millis vs RTC
15:51:06.244 -> 1	15:51:0 @: 24.00 C
15:52:05.656 -> 58960	15:52:0 @: 24.00 C
15:53:05.690 -> 59506	15:53:0 @: 24.00 C
15:54:05.651 -> 59506	15:54:0 @: 24.00 C
15:55:05.695 -> 59506	15:55:0 @: 24.00 C
15:56:05.647 -> 59505	15:56:0 @: 24.00 C
15:57:05.680 -> 59507	15:57:0 @: 24.00 C
15:58:05.669 -> 59505	15:58:0 @: 24.25 C
15:59:05.682 -> 59506	15:59:0 @: 24.25 C
16:00:05.651 -> 59506	16:0:0 @: 24.25 C
16:01:05.644 -> 59505	16:1:0 @: 24.25 C
16:02:05.654 -> 59506	16:2:0 @: 24.25 C
16:03:05.664 -> 59506	16:3:0 @: 24.25 C

erste Zeile ist klar,
zweite Zeile kann ich noch nicht ganz nachvollziehen, wegschmeissen.
Aber dann habe ich eine Abweichung von +/- 2 ms pro Minute
In welcher Anwendung ist das "zu ungenau"?

mal mit dem Fön den RTC angewärmt (der Mega wird wohl auch etwas wärmer geworden sein...)

16:05:41.733 -> millis vs RTC
16:05:41.733 -> RTC lost power, let's set the time.
16:05:41.771 -> 3	15:51:0 @: 43.50 C
16:06:41.753 -> 59487	15:52:0 @: 54.50 C
16:07:41.740 -> 59491	15:53:0 @: 54.50 C
16:08:41.772 -> 59492	15:54:0 @: 58.25 C
16:09:41.734 -> 59494	15:55:0 @: 49.75 C
16:10:41.769 -> 59495	15:56:0 @: 42.75 C
16:11:41.744 -> 59499	15:57:0 @: 38.50 C
16:12:41.742 -> 59497	15:58:0 @: 34.50 C
16:13:41.739 -> 59500	15:59:0 @: 32.25 C
16:14:41.743 -> 59500	16:0:0 @: 30.50 C
16:15:41.734 -> 59500	16:1:0 @: 28.75 C
16:16:41.754 -> 59501	16:2:0 @: 28.00 C
16:17:41.713 -> 59502	16:3:0 @: 27.50 C
16:18:41.712 -> 59503	16:4:0 @: 27.00 C
16:19:41.730 -> 59502	16:5:0 @: 26.75 C
16:20:41.719 -> 59503	16:6:0 @: 26.50 C
16:21:41.716 -> 59502	16:7:0 @: 26.25 C
16:22:41.709 -> 59504	16:8:0 @: 26.00 C
16:23:41.723 -> 59504	16:9:0 @: 26.00 C
16:24:41.711 -> 59503	16:10:0 @: 25.75 C
16:25:41.719 -> 59504	16:11:0 @: 25.75 C
16:26:41.696 -> 59504	16:12:0 @: 25.50 C
16:27:41.712 -> 59505	16:13:0 @: 25.50 C
16:28:41.716 -> 59505	16:14:0 @: 25.50 C
16:29:41.696 -> 59504	16:15:0 @: 25.50 C
16:30:41.688 -> 59505	16:16:0 @: 25.50 C

Hallo,

2ms/min >> 120ms/h >> 2,88s/d

Deckt sich mit der Aussage vom TO

und scheint sich erledigt zu haben.