FYI: millis() - provozierter Überlauf ohne 49 Tage warten

Mit fünf aktiven Codezeilen in die Zukunft - Erklärung im Anschluss

extern volatile unsigned long timer0_millis;       // Dieser Einzeiler macht den internen Zähler zugänglich

  uebergabewert=4294967040UL;                      // setzt auf 4294967040 millisekunden 

  cli();                                           // deaktiviert Interrupts
  timer0_millis = uebergabewert;                   // setzt den Zähler auf definierten Übergabewert
  sei();                                           // Interrupts wieder aktivieren

Der Code ist so natürlich nicht ausführbar. Das sind aber die Zeilen, die einen Überlauf von millis() nach 256 millisekunden provozieren.

In den letzten Tagen traten wieder vermehrt Fragen zu millis(), dem Einsatz und dem Überlauf auf. Im Thread Alternative zu delay hat Tommy56 in #14 bereits auf sein Beispiel verwiesen, was passiert wenn ein Überlauf mit bytes erreicht wird.

Es gibt eine schöne deutschsprachige Dokumentation als .pdf, in der der Überlauf von millis() erklärt wird. Trotzdem will, und manchmal kann, man erst am “lebenden Objekt” feststellen, ob das theoretische auch praktisch funktioniert.

Somit wäre schön wenn es möglich wäre den Zustand der in 49 Tagen eintritt beim Start des AVR einzustellen?
Dann machen wir das!

millis() ist eigentlich nur eine Funktion, die auf einen Zähler im AVR zugreift und dessen Wert zurückgibt.

Dieser Zähler ist gebunden an den internen Timer0 und wird gestartet mit Anlegen der Versorgungsspannung. Mittels eines internen Interrupt wird unabhängig vom Bootloader oder sonstigem Code aufgezählt.
Will man ohne millis() diesen Zähler nutzen, benötigt man direkten Zugriff.
Das passiert mit dem Einzeiler:
extern volatile unsigned long timer0_millis;

Dann muss, um den Zähler mit einem eigenen definierten Wert zu versehen, der Interrupt angehalten werden.
cli();
Im Anschluß den von Dir gewünschten Anfangswert des Zählers eintragen und die Interrupts wieder einschalten.
sei();
Ab jetzt zählt der interne Zähler weiter, beginnend mit dem übergebenen Wert.

Und wie bekomme ich nun heraus, wann der Überlauf erfolgt ist?
Einfach warten. Und sehen, wie sich millis() verändern.

Ausgehend vom obigen Ausschnitt der folgende Code als lauffähiger Sketch, der im seriellen Monitor zeigt wann und wie der Überlauf erfolgt.
Dort bitte einstellen: Zeitstempel anzeigen

extern volatile unsigned long timer0_millis;       // Dieser Einzeiler macht den internen Zähler zugänglich

void setMillis(unsigned long uebergabewert)        // Diese Funktion stellt den Zähler auf den übergebenen Wert
  {                                                // und könnte jederzeit ausgeführt werden
  cli();                                           // deaktiviert Interrupts
  timer0_millis = uebergabewert;                   // setzt den Zähler mit Übergabewert
  sei();                                           // Interrupts wieder aktivieren
  }

void setup()                                       // wird einmal ausgeführt
  {
  Serial.begin(115200);                            // Für "Werkzeuge/Serieller Monitor" in der ARDUINO-IDE
  setMillis(4294967040UL);                         // setzt auf 4294967040 millisekunden
  }

void loop()                                        // wird als "Schleife" immer wieder ausgeführt
  {                                                // wenn nicht irgendetwas dazwischen kommt
  Serial.println(millis());                        // schreibt ein paar Zeilen - damit lässt sich z.B. Laufzeit von loop() darstellen
  if ((millis() > 30) && (millis() < 1000000))     //  /* hier kommt was dazwischen */
    {                                              //  /* Wenn millis() > 30 und < 1000000 */ 
    while (1);                                     //  /* hier Endlosschleife */
    }                                              //  /* hält die Ausgabe an */

// hier könnte ausführbarer Code stehen

  }

Wer jetzt seinen Code an der richtigen Stelle einfügt oder aber loop() durch seinem Code mit den richtigen seriellen Ausgaben ersetzt, bekommt damit einen Eindruck, was eigentlich erst in 49 Tagen +x passiert.

Und noch ein Hinweis:
Auch wenn die Funktion setMillis() jederzeit ausgeführt werden könnte, macht das niemals im loop(), wenn ihr nicht wisst, was ihr tut.

Bitte keine Corona-Partys.
Es gibt genug zu tun.

Bleibt gesund!

Hi

Wenn man als Übergabewert eine negative Zahl übergibt, dürfte Das zwar eine Warnung werfen, klappt aber auch.
-100 ist für ein unsigned nichts Anderes, als '100 vor 0'.
Wenn man also 12 Sekunden Zeit haben möchte, bevor der Überlauf statt findet - als Set-Wert -12000 und das gebannte Warten kann beginnen.

MfG

Schon mehrfach verlinkt: Das geht auch mit Byte.

Gruß Tommy

Wenn man als Übergabewert eine negative Zahl übergibt, dürfte Das zwar eine Warnung werfen

Nein macht es nicht.
UL ist ja eigentlich nur eine Umrechnung. Und da hier direkt auf den Zähler zugegriffen wird, ist eine Umrechnung nicht notwendig.
Da millis() aber UL sind, wollte ich das nicht unnötig komplizieren.

Schon mehrfach verlinkt: Das geht auch mit Byte.

Und extra darauf hingewiesen

Im Thread Alternative zu delay hat Tommy56 in #14 bereits auf sein Beispiel verwiesen, was passiert wenn ein Überlauf mit bytes erreicht wird.

Hi

Ich meinte damit, wenn Du einer unsigned Variable einen negativen Wert zuweist, meckert der Kompiler.
Er übersetzt z.B. '-1' in 'alle Bits gesetzt' - was ziemlich genau -1 entspricht ;).
(oder 1 tiefer als 0, was mit den Unterläufen wieder 'alle Bits 1' ergibt)

Klar, wenn dieser Wert erst während der Laufzeit von irgendwoher kommt, kann der Kompiler dagegen nicht viel machen.
Aber auch hier ist -1 wieder nichts Anderes, als alle Bits gesetzt - was bei einem Signed, durch das gesetzte MSB, eine negative Zahl darstellt.
Wegen der anderen Bedeutung des MSB hat man auch nur noch 'die halbe Zahlenmenge' zur Verfügung - dafür aber über und unter Null.

MfG

Ich meinte damit, wenn Du einer unsigned Variable einen negativen Wert zuweist, meckert der Kompiler.

Macht er in diesem Fall (lauffähiger Sketch - da oben) und Austausch von 4294967040 gegen -120

setMillis(-120);

nicht.
Im Übrigen gehe ich mit Dir absolut konform.
Es ist aber nur ein FYI weil immer wieder die Frage aufkommt, wie man das ggfls. nachstellen kann.