Go Down

Topic: reset des millis() Wertes (Read 2164 times) previous topic - next topic

mkl0815

Nach einigen (mehr oder weniger) sinnvollen Kommentaren, will ich nun auch mal eine Frage in die Runde werfen.

Es geht um den Null-Durchlauf des "millis()" Wertes. Da ich in einem Projekt mit einem Unix Timestamp arbeite, kann ich recht einfach mit dem Wert den millis() liefert rechnen. In regelmäßigen Abständen (vermutlich einmal pro Tag) wird der aktuelle Timestamp per NTP geholt. Dabei will ich dann den Wert für millis() auch gleich auf Null setzen, damit mir nicht der unweigerlich irgendwann auftretende Nulldruchlauf mitten am Tag die Zeitrechnung durcheinander bringt.

Den Wert selbst, sowie die Funktion habe ich in der Datei "wiring.c" gefunden :
Code: [Select]

volatile unsigned long timer0_millis = 0

unsigned long millis()
{
        unsigned long m;
        uint8_t oldSREG = SREG;

        // disable interrupts while we read timer0_millis or we might get an
        // inconsistent value (e.g. in the middle of a write to timer0_millis)
        cli();
        m = timer0_millis;
        SREG = oldSREG;

        return m;
}

Somit sollte eine Funktion

Code: [Select]

void resetmillis()
{
        uint8_t oldSREG = SREG;

        // disable interrupts while we read timer0_millis or we might get an
        // inconsistent value (e.g. in the middle of a write to timer0_millis)
        cli();
        timer0_millis =0;
        SREG = oldSREG;
}


kein Problem sein. Ein kurzes "find libraries -type f -exec grep 'millis() {} \; -print" liefert mir alle Stellen in den Libs, die auch mit dieser Funktion arbeiten. Das scheint aber nur in irgendwelchen Funktionen der Fall zu sein, daher denke ich, das ich auf der sicheren Seite bin, wenn ich den Wert von Hand auf 0 setze. (Zumal das ja irgendwann eh passiert)

Trotzdem die Frage, ob ich etwas übersehe? Fällt jemanden etwas ein, was mir auf die Füße fallen könnte?

Grüße,
Mario.

uwefed

Meines Wissens kann man Millis() nicht zurücksetzen;
Ungeachtet dem fehlt mir in Deinem Kode das sei() um interrupts wieder zuzulassen.
Ist es notwendig micros() auch resettieren?
Grüße Uwe

mkl0815

Ich vermute das der Aufruf "SREG = oldSREG;" durch das Setzen die Interrupts wieder erlaubt, denn die originale "millis()" Funktion macht auch kein "sei()" (siehe Code in wiring.c).
Die Variable  "volatile unsigned long timer0_millis" in der wiring.c ist halt auch nur eine ganz normale Variable, die ich somit innerhalb meines Programms auch setzen kann. In meinem Text habe ich nur "millis()" als Begriff verwendet, damit klar ist was ich meine.
Der Wert von micros() ist (soweit ich das beurteilen kann) unabhängig von millis(), daher muss der nicht genullt werden.

uwefed

"SREG = oldSREG;" ist eigentlich nur eine Zuweisung 2er Variablen und setzt kein sei().
Grüße Uwe

MaFu

Wozu millis() resetten?

Code: [Select]
vergangeneZeit = millis() - previousMillis;
Die Variablen müssen als unsigned long deklariert werden, Überlauf spielt dann keine Rolle.
Einzige Voraussetzung: previousMillis muss mindestens einmal innerhalb eines Überlaufs neu gesetzt werden.
_______
Manfred

mkl0815

Ich denke nicht das SREG "nur" eine Variable ist. Ich vermute hier wird direkt ein Register angesprochen, genau wie bei den IO-Ports (PORTA, PORTB, etc...). Hier schreibt der AVR Compiler wohl beim Schlüsselwort "SREG" direkt auf das Register.
Zumindest werden bei der originalen millis() Funktion auch per cli() auch die Interrupts abgeschaltet und danach nicht per sei() wieder an, sondern durch schreiben des alten Registerwertes wieder aktiviert.

Laut http://www.cse.unsw.edu.au/~cs1721/05s2/lectures/slide48.html ist im Statusregister (SREG) auf das Interrupt enable/disable Bit (Bit 7) enthalten, damit werden also durch zurückschreiben des originalen Wertes (vor dem Ausschalten der Interrupts) in das Register auch die Interrupts wieder aktiviert.

Siehe auch http://www.mikrocontroller.net/articles/AVR-Tutorial:_Interrupts
Da wir in C arbeiten und unsere ISRs in der Regel Funktionen sind, brauchen wir uns um das Sichern der Register nicht zu kümmern, das wird in der Regel automatisch beim Sprung in eine Subroutine (in unserem Fall die ISR) gemacht. Dafür sorgt der Compiler. Trotzdem sollte man sich das merken, denn evtl. ist die ISR ja doch mal bei inline Assembler.
Mario.

mkl0815

@MaFu: Du hast recht, allerdings hilft mir das bei meinem konkreten Problem nicht.

Einmal am Tag wird per NTP der aktuelle Zeitstempel geholt (unsigned long basetime) und der dabei aktuelle Wert von millis() (unsigned long uptime) gesichert.
Jede Minute wird nun eine Aktion ausgeführt, bei der der aktuell Timestamp mittels
Code: [Select]
unsigned timestamp  = ( millis() - uptime) / 1000 + basetime; berechnet wird.
Wenn nun während des Tages der Nulldurchlauf stattfindet, liefert (millis() - uptime) einen negativen Wert, bzw. weil unsigned einfach nur Blödsinn. Und zwar solange, bis das nächste Mal die NTP-Routine den Wert für "uptime" übernimmt. Dann hab ich wieder (ich glaub 53,x Tage) Ruhe.
Genau das will ich vermeiden, indem ich den Wert auf 0 setze.

Natürlich kann ich auch prüfen, ob millis() < uptime ist, aber dann muss ich etvl. zu einer "ungünstigen" Zeit in die NTP-Routine verzweigen. Dann lieber zu einem definierten Zeitpunkt dafür sorgen das am nächsten Tag (oder auch die folgenden Tage, falls mal Netzwerk nicht geht) kein Blödsinn passiert.
Mario.

MaFu

#7
Jan 04, 2012, 02:26 pm Last Edit: Jan 04, 2012, 02:28 pm by MaFu Reason: 1
Doch, das hilft Dir schon.
Dein
Code: [Select]
(millis() - uptime)entspricht ja meinem
Code: [Select]
millis() - previousMillis. Und da ja wegen "unsigned long" keine negativen Werte entstehen passt es auch trotz Überlauf.

Beispiel:
Gehen wir mal davon aus, dass der aktuelle millis Wert 4294967290 entspricht (also kurz vor dem Überlauf).
Sagen wir weiterhin, uptime wurde kurz vorher gesetzt und steht auf 4294967280.

Dann zählen wir millis mal hoch und prüfen das Ergebnis beim Überlauf (Ergebnis = millis() - uptime):
4294967290 - 4294967280 = 10
4294967291 - 4294967280 = 11
4294967292 - 4294967280 = 12
4294967293 - 4294967280 = 13
4294967294 - 4294967280 = 14
4294967295 - 4294967280 = 15 (jetzt kommt der Überlauf)
        0 - 4294967280 = 16
        1 - 4294967280 = 17
        2 - 4294967280 = 18
        3 - 4294967280 = 19


Wie Du siehst, passiert kein "Blödsinn".
_______
Manfred

mkl0815

Ok, das überzeugt mich. Dann habe ich also eine Lösung für ein Problem entwickelt, das es gar nicht gibt. :-)
Ich hatte mich schon gewundert, warum keine sonst dieses Problem zu haben schien. Google hatte nix wirklich sinnvolles gefunden, obwohl ein Haufen Sketche und Libs fleissig mit den Differenzen arbeiten.

Danke nochmal fürs Vorrechnen.

Chris72622

Wieso ergibt "0 - 4294967280" die Zahl 16?

Gruß Chris
https://github.com/jeffThompson/DarkArduinoTheme

sth77

Weil du mit nichtvorzeichenbehafteten Long-Varablen nur von 0 bis 4.294.967.295 zählen kannst. Dis Subtraktion ist etwas schwerer zu überblicken, also rechnen wir einfach mal mit der Umkehrrechenoperation und Addieren zu der 16 einfach 4.294.967.280. Der Taschenrechner spuckt 4.294.967.296 aus, liegt also auf dem nächsten Zahlenwert nach der letzten zulässigen Zahl: Zack, Überlauf, 0. :D
Mein Arduino-Blog: http://www.sth77.de/ - letzte Einträge: Teensy 3.0 - Teensyduino unter Window 7 - Teensyduino unter Windows 8

Go Up