Ich messe Spannungen und Ströme, das funktioniert. Die Werte werden alle
10 Sekunden auf eine SD-Karte geschrieben, das funktioniert auch, aber
die Zeit driftet mir ab.
Den wesentlichen Teil als Beispiel:
Das Problem: Wenn die Millisekunde erreicht ist, während gerade
"MacheIrgendwas" begonnen hat, komme ich 25ms zu spät zur Ausführung
(stört mich nicht).
Der neue Zeitstempel "LetzteMessung" liegt nun aber auch nicht mehr
10000ms nach dem letzten, sondern versetzt um 25ms - diese ms addieren
sich über die Zeit.
Wie bekomme ich diesen Versatz weg?
Oliver S. schrieb:> Indem du die vergangene Zeit seit der letzten Messung berücksichtigst.
Dein Hinweis ist etwas arg knapp, kannst Du den möglichen Algorithmus
umschreiben?
Sylvester schrieb:> Vergiss den vorigen Beitrag. Das funktioniert nicht wenn es einen> Overflow bei "LetzteMessung + 10000" gibt.
Ich ueberlege gerade, warum Du Deinen Ansatz widerrufen hast. Mir
schwebt etwas in dieser Richtung vor, den ersten Zeitstempel immer in
10000er-Schritten zu erhöhen, ich bekomme es nur nicht als Ablauf
formuliert.
Ich würde das ganze mit einem Timer + Interrupt lösen.
1.) Timer konfigurieren dass jede Sekunde eine Interrupt Service Routine
aufgerufen wird. (10s ist wahrscheinlich nicht möglich)
2.) Bei jedem 10. Aufruf des Interrupts rufst du dann eine Funktion auf
um die Werte auf die SD Karte zu schreiben. Dabei erhöhst du eine
einfache Zählervariable (int).
Zeit = counter * 10;
Manfred schrieb:> Sylvester schrieb:>> Vergiss den vorigen Beitrag. Das funktioniert nicht wenn es einen>> Overflow bei "LetzteMessung + 10000" gibt.>> Ich ueberlege gerade, warum Du Deinen Ansatz widerrufen hast. Mir> schwebt etwas in dieser Richtung vor, den ersten Zeitstempel immer in> 10000er-Schritten zu erhöhen, ich bekomme es nur nicht als Ablauf> formuliert.
Unsigned Counter die unabhängig in eine Overflow Bedingung laufen können
mit größer oder kleiner zu vergleichen funktioniert nicht. Das kann man
anhand eines kleinen Beispiels zeigen. Damit die Zahlen nicht so
unhandlich werden, nehmen wir an, daß millis() unsigned 8-bit Werte
liefert, LetzteMessung ebenso eine unsigned 8-bit Variable ist, und als
Schrittweite nehmen wir 10.
if (millis() >= LetzteMessung + 10) { // zyklisch Messwerte holen
LetzteMessung += 10;
LetzteMessung = 242 und millis() steht auf 252, dann ist die
if-Bedingung erfüllt, und LetzteMessung wird auf 252 gesetzt. Soweit
noch ok. Aber schon beim nächsten Schleifendurchlauf ist die
if-Bedingung wieder erfüllt, da der millis() Wert nun mit 252+10 = 6
(wegen 8-bit overflow) verglichen wird. Und das ist falsch.
Was bei Unsigned aber trotz Overflow funktioniert ist die
Differenzbildung, d.h. so wie du es ursprünglich hattest ist es richtig.
Du mußt nur "LetzteMessung = millis();" durch "LetzteMessung += 10000"
ersetzen.
Sylvester schrieb:>> Ich ueberlege gerade, warum Du Deinen Ansatz widerrufen hast. Mir>> schwebt etwas in dieser Richtung vor, ...> Unsigned Counter die unabhängig in eine Overflow Bedingung laufen können> mit größer oder kleiner zu vergleichen funktioniert nicht.
Ich weiß. Variablen und deren Typen sind nicht meine Freunde, man lernt,
sich zu respektieren.
> Was bei Unsigned aber trotz Overflow funktioniert ist die> Differenzbildung, d.h. so wie du es ursprünglich hattest ist es richtig.
Jou, in einem bestehenden Gerät funktioniert das mit zweistelligen
Stunden, längere Betriebszeiten treten nicht auf. Rechnerisch sollte es
knapp 50 Tage bis zum Überlauf dauern. Da fiel mir halt der sich
addierende Fehler auf, den ich fuer ein anderes Projekt aber nicht
ertragen mag.
> Du mußt nur "LetzteMessung = millis();" durch "LetzteMessung += 10000" ersetzen.
Genau so habe ich das heute geschrieben und getestet:
LastTimeMess=millis();// nur falls Timing grob aus dem Ruder laeuft
6
Serial.print("LastTimeMess neu gesetzt: ");
7
Serial.println(LastTimeMess);
8
}
9
}
Die Routine kommt beim erstmaligen Aufruf nicht auf die Strümpfe, da
wird dann millis() eingesetzt. Bei weiteren Durchläufen passt das dann,
es sei denn, eine Unterroutine blockiert extrem lange - was aber nicht
passieren wird.
Das bleibt jetzt so, einfacher kann ich es nicht haben!
Uwe C. schrieb:> Was delay() mit dem Prozessor macht ist bekannt ?>> Da nützen die millis() im Aufruf der Unterprogramme gar nix....
Das sind nur sinnbildliche Beispiele!
>LastTimeMess=millis();// nur falls Timing grob aus dem Ruder
7
>laeuft
8
>Serial.print("LastTimeMess neu gesetzt: ");
9
>Serial.println(LastTimeMess);
10
>}
11
>}
Bei dem zweiten Vergleich hast du wieder das Problem bei einem Overflow.
Der erste Vergleich funktioniert ja mit/trotz Overflow auch noch nach 50
Tagen, deshalb solltest du auch den zweiten Vergleich entsprechend
umformulieren,
anstelle: if ((LastTimeMess + MessInterval) < millis()) {
schreibe: if ((millis() - LastTimeMess) > MessInterval) {
Solche Overflow Probleme, die erst nach langer Zeit in Erscheinung
treten, sollte man grundsätzlich vermeiden.
Du kannst auch auf MessInterval/2..x prüfen, wenn diese Korrektur früher
zuschlagen soll.
Um definiert anzufangen würde ich am Ende von setup() "LastTimeMess =
millis() - MessIntervall;" setzen, wenn gleich beim ersten loop()
Durchlauf ein Messergebnis gewünscht ist, oder "LastTimeMess =
millis();", dann wird der erste Messwert erst nach Ablauf von
"MessInterval" geholt.
Sylvester schrieb:> Bei dem zweiten Vergleich hast du wieder das Problem bei einem Overflow.> Der erste Vergleich funktioniert ja mit/trotz Overflow auch noch nach 50> Tagen, deshalb solltest du auch den zweiten Vergleich entsprechend> umformulieren,>> anstelle: if ((LastTimeMess + MessInterval) < millis()) {> schreibe: if ((millis() - LastTimeMess) > MessInterval) {>> Solche Overflow Probleme, die erst nach langer Zeit in Erscheinung> treten, sollte man grundsätzlich vermeiden.
Ein Überlauf nach 50 Tagen ist unwichtig, das Gerät wird nicht länger
als ein paar Stunden laufen. Aber egal, die Korrektur kostet kein Geld
und wird eingearbeitet! Wer weiß, ob ich den Fehler ansonsten in Zukunft
mal in eine Anwendung kopiere, wo er relevant ist.
> Du kannst auch auf MessInterval/2..x prüfen, wenn diese Korrektur früher
zuschlagen soll.
Ich habe hier nur ein sehr kleines Stück dargestellt, bei dem ich den
Knoten im Kopf hatte.
> Um definiert anzufangen würde ich am Ende von setup() "LastTimeMess => millis() - MessIntervall;" setzen, wenn gleich beim ersten loop()> Durchlauf ein Messergebnis gewünscht ist, oder "LastTimeMess => millis();", dann wird der erste Messwert erst nach Ablauf von> "MessInterval" geholt.
Das Gebilde läuft vor sich hin und aktualisiert alle paar Sekunden
Meßwerte im Display. Es gibt Tasten Start und Stop, mit denen die
zyklische Protokollierung zur SD-Karte gestartet wird, da habe ich es
direkt in der Hand, sofort bei Start eine Messung zu triggern.
Das Problem ist eigentlich Kindergarten, ich danke Dir, dass Du Dich
damit so detailliert befasst hast.
--------
Was das wird: Ein Netzteil liefert 5V / 2A, ein INA219 misst den Strom
und errechnet daraus die Millimaperestunden, die in ein Gerät per USB
reingeladen werden. Es sind drei Analogeingänge des AT328 (Nano)
beschaltet, damit der die Betriebsart erkennen kann.
Es kann eine externe Versorgung angeschlossen werden, dann werden mAh
und Spannung am blanken Akku gemessen, das kann z.B. ein Blei bis 12V
sein.
Zum Laden einer LiIon-Einzelzelle ist ein TP4056 einschaltbar.
Alle Werte können auf eine µSD-Karte protokolliert werden, um später am
PC die zugehörige Grafik darstellen zu können.
(Die Daten auf dem Bild sind sinnlos, dienen nur dem Test, die
Formatierung korrekt hinzubiegen. Es ist eine rein private Bastelei.)