Forum: Mikrocontroller und Digitale Elektronik Sekunden,Minuten zählen mit der Funktion Millis()


von Sebastian (Gast)


Lesenswert?

Hallo ich bin mich gerade mit der Funktion Millis am beschäftigen. Mein 
Plan ist es eine Steuerung mit Touchscreen zu bauen wo ich Zeiten 
Temperaturen einstellen kann und dann das Programm eine Heizplatte 
steuert. Da ich bis jetzt nur sehr Mini Projekte gemacht habe und vorher 
immer Bascom benutzt habe kenne ich mich mit der IDE noch nicht gut aus. 
Ich wollte jetzt stück für Stück einzelne Programmteile erstellen und 
Testen (Üben) um später alles zu einen großen ganzen zusammenzusetzten. 
Ich habe jetzt schon einiges gelesen und hoffe das ich jetzt soweit 
alles verstanden habe.

1. Millis startet bei Strom bzw. Reset und läuft knapp 50 Tage
2. Ich speichere die Startzeit in einer unsigned long Variable
3. Im loop lese ich erneut Millis aus Rechne die Startzeit ab und 
erhalte die Differenz.

Daraus kann ich jetzt natürlich die Zeit Berechnen. Was ich jetzt aber 
noch nicht ganz genau verstehe ist wie ich damit genau 1 Sek immer 
erreiche. Weil ich weiß ja nicht wann das Programm die Differenz 
berechnet. Jetzt mal total übertrieben wenn meine schleife sagen wir mal 
2 Sekunden brauchen würde um durchzulaufen könnte ich ja nie jede 
Sekunde meinen Timer runter zählen. Es wäre nett wenn mir das Prinzip 
mal jemand erklären könnte. Den Code finde ich schon selbst heraus mir 
reicht allein das Prinzip

von Nick M. (Gast)


Lesenswert?

Nachdem du so viel Text geschrieben hast ohne dass wirklich was dabei 
rauskommt, vermute ich, dass du in Cobol programmierst. Rechner ist wohl 
eine IBM 700.

Richtig?

von Christian S. (roehrenvorheizer)


Lesenswert?

Mit welchem Arduino kämpfst Du denn?

"Häufige Fehlerquelle: Da millis() relativ schnell über den Wertebereich 
int hinauslaufen, sollte man hier den Variablentyp long bevorzugen.

Der Return ist vom Dateityp unsigned long. Nach ca. 50 Tagen springt 
millis() zurück auf 0."


mfg

von Einhart P. (einhart)


Lesenswert?

Wenn du eine schnelle Reaktion brauchst, dann darf die Haupschleife halt 
keine 2 s laufen. 2s sind schon eine Ewigkeit.

von Christian S. (roehrenvorheizer)


Lesenswert?

IBM701?

von Rolf M. (rmagnus)


Lesenswert?

Einhart P. schrieb:
> Wenn du eine schnelle Reaktion brauchst, dann darf die Haupschleife halt
> keine 2 s laufen. 2s sind schon eine Ewigkeit.

Und "Ewigkeit" ist hier noch untertrieben. Wenn die mehr als ein paar 
Millisekunden braucht, ist das schon heftig viel.

von au weia (Gast)


Lesenswert?

Christian S. schrieb:
> sollte man hier den Variablentyp long bevorzugen.

Wer lesen kann ist klar im Vorteil.
Wer verstehen kann, noch mehr.

Sebastian schrieb:
> Ich speichere die Startzeit in einer unsigned long Variable

von Sebastian (Gast)


Lesenswert?

Leute das mit 2 Sekunden war nur ein Beispiel auch wenn die Schleife 
sagen wir mal 255ms braucht komme ich ja nach 4 schleifen auf 1020ms 
raus und mir würde dann schon 20ms fehlen im meine Variable Sek 1 zu 
veringern. Bei 1 Stunde wäre das dann eine große Differenz

von Teo D. (teoderix)


Lesenswert?

Sebastian schrieb:
> wie ich damit genau 1 Sek immer
> erreiche.

Wer erklärt Ihm das mit den Bienen und Blümchen?!

Was du braucht nennt sich Timer-Interrupt.

von (prx) A. K. (prx)


Lesenswert?

Teo D. schrieb:
> Wer erklärt Ihm das mit den Bienen und Blümchen?!

Wer immer alt genug ist, um sich noch an die 701 zu erinnern.

von Sebastian (Gast)


Lesenswert?

Ja das kenne ich noch von BAscom aber warum nutzen dann alle die 
Millis???

von au weia (Gast)


Lesenswert?

Sebastian schrieb:
> warum nutzen dann alle die Millis???

Weil das für Lieschen Müller einfacher zu verstehen ist.

von Thomas W. (Gast)


Lesenswert?

Moin, -

Du willst ja nur das Prinzip:

https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay

Nach dem das Interval verstrichen ist, kannst Du Sekunden,
Minuten und Stunden Millesekunden genau zaehlen.

Das ist (glaube ich) die Antwort auf Deine Frage.

Gruesse

Th.

P.S.: Google mal nach "Blink without Delay", eine Standard
Programmieruebung fuer Arduino.

P.P.S:
https://forum.arduino.cc/index.php?topic=423688.0

von Keiner (Gast)


Lesenswert?

Ich glaube Ihr versteht mich nicht richtig ich weiß wie ich mit Millis 
2-3 Leutdioden oder was auch immer im Sekundentakt oder auch 2 Sekunden 
leuchten lassen kann. Aber je nach dem was im loop abgearbeitet werden 
muss dauert der unterschiedlich lang. Wenn ich jetzt die Funktion millis 
() benutze und den Interval auf 1000ms setzte und dann daraufhin eine 
Variable Sek um 1 hochzähle kann es doch sein das erst Millis bei 1010ms 
die If Schleife durchläuft. Dann fehlen mir doch schon 10ms

von pnp (Gast)


Lesenswert?

Keiner schrieb:
> ich weiß wie ich mit Millis
> 2-3 Leutdioden oder was auch immer im Sekundentakt oder auch 2 Sekunden
> leuchten lassen kann. Aber je nach dem was im loop abgearbeitet werden
> muss dauert der unterschiedlich lang.

Ich glaube nicht, daß du weißt wie es geht. Das merkt man an deinen 
Bemerkungen. Die Intervalle sind NICHT unterschiedlich lang, wenn man es 
richtig macht...

Schau dir bitte die Links an.
Dann wirst du merken, daß du nicht weißt wie es geht.

von Thomas W. (Gast)


Lesenswert?

Doch ich habe Dich verstanden:

Beitrag "Re: Sekunden,Minuten zählen mit der Funktion Millis()"

Th.

P.S.: Du weisst aber schon, dass millis() interrupt-gesteuert
hochgezaehlt wird, oder?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Sebastian schrieb:
> wie ich damit genau 1 Sek immer erreiche.
Wie "genau" musst du die 1 Sekunde "erreichen"?
Welchen Jitter beim Sekundenwechsel kannst du dir erlauben.

Keiner schrieb:
> Aber je nach dem was im loop abgearbeitet werden muss dauert der
> unterschiedlich lang.
Es darf aber eben niemals länger als 1 Sekunde dauern, wenn du jede 
Sekunde sicher "erreichen" willst.
Und wenn du jeden Sekundenwechsel auf 0,1s genau "erreichen" willst, 
dann darf deine Mainloop niemals länger als 100ms deauern.

BTW: bitte nur 1 Name pro Thread!
So steht es in den Nutzungsbedingungen

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Die millis Funktion basiert auf dem CPU Takt, der für derart lange 
Zeiträume sicher nicht zufriedenstellend genau ist. Wenn du da in 50 
Tagen auf 5 Minuten Abweichung kommst ist das schon gut, 15 Minuten 
würde ich auch noch für normal halten.

Verwendet besser eine RTC ( z. Beispiel mit dem DS3231 Chip), die läuft 
erheblich genauer und liefert dir praktischerweise auch gleich den 
ganzen Kalender mit ohne dass du großartig rechnen musst.

von Manfred (Gast)


Lesenswert?

Sebastian schrieb:
> 2. Ich speichere die Startzeit in einer unsigned long Variable
> 3. Im loop lese ich erneut Millis aus Rechne die Startzeit ab und
> erhalte die Differenz.

Ich mache das andersherum: Ich rechne die Zielzeit aus und prüfe, ob 
millis() größer als diese ist. Dann kneift mich ein evtl. Überlauf 
nicht.
Simples Beispiel:
(Es wird nicht direkt compilieren, den Fehler findest Du selbst)
1
int LEDInfo = 10;
2
unsigned long int FlashLEDTime = 0;
3
4
void loop() {  // Hauptschleife
5
if (millis() > FlashLEDTime == HIGH) {  // LED blinken lassen
6
  digitalWrite (LEDInfo, !digitalRead(LEDInfo));
7
  FlashLEDTime = (millis() + 450);
8
}

Teo D. schrieb:
> Was du braucht nennt sich Timer-Interrupt.

Warum? Der Arduino kann in der Hauptschleife rotieren und ein paar 
Zeitstempel prüfen. Man muss natürlich aufpassen, das nicht durch 
blockierende (delay) Unterroutinen zu versauen.

von Falk B. (falk)


Lesenswert?

Keiner schrieb:
> Ich glaube Ihr versteht mich nicht richtig

Oder du das Problem bzw. wie millis() wirklich funktioniert.

> ich weiß wie ich mit Millis
> 2-3 Leutdioden oder was auch immer im Sekundentakt oder auch 2 Sekunden
> leuchten lassen kann. Aber je nach dem was im loop abgearbeitet werden
> muss dauert der unterschiedlich lang.

Ja.

> Wenn ich jetzt die Funktion millis
> () benutze und den Interval auf 1000ms setzte

Wie machst du dass denn genau?

Millis liefert eine Zahl, die Millisekunden seit dem Reset. Diese Zahl 
wird in einem 1kHz [8Interrupt]] vom Arduino-Framework hochgezählt, 
unabhängi davon, was deine loop macht, denn der Interrupt hat Priorität 
und kann die loop() und sonstige Funktionen oder Methoden in Klassen 
IMMER unterbrechen. Damit ist die Zahl immer korrekt.

> und dann daraufhin eine
> Variable Sek um 1 hochzähle
> kann es doch sein das erst Millis bei 1010ms
> die If Schleife durchläuft. Dann fehlen mir doch schon 10ms

Richtig. So macht man es auch nicht. Du liest IMMER über millis() die 
altuelle "Zeit" aus und rechnest die IMMER in HH:MM:SS um. Und schon 
kann es NIE Abweichungen oder Zählfehler geben. Du brauchst keine extra 
Zählung der Sekunden, das ist im Rückgabewert von millis() schon drin. 
/1000 ist einfach, der Rest auch.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dem TO geht es um die Abweichung des millis Vergleiches. Der sich im 
dümmsten Fall aufaddiert.

Man hat doch immer noch eine zweite Variable lastMillis oder ähnlich 
benannt mit der man die verstrichene Zeit vergleicht. Wenn man diese 
nach dem Ereignis gewöhnlich mit millis auf den aktuellen Stand bringt, 
dann addieren sich die Laufzeitfehler auf. Bedingt durch die Laufzeit 
von loop. Wir nehmen einmal an loop benötigt extrem lange.
Setzt man jedoch nach dem Ereignis/Zeitvergleich lastMillis nicht auf 
den aktuellen millis Wert, sondern addiert zu lastMillis den gewünschten 
Intervallwert (1000ms), dann kann sich kein Abweichungsfehler addieren.
Auch wenn die loop länger als 1ms benötigt, bleibt das Ereignis im 
1000ms Fenster. Eine Abweichung kann es dennoch geben, aber wie gesagt, 
addiert sich nicht auf.

Im ersten Fall kann lastMillis folgende Werte annehmen.
0, 1001, 2000, 3009, 4023 usw.
Im zweiten Fall sind es immer Vielfache vom Intervall 1000.
0, 1000, 2000, 3000, 4000 usw.
Der Vergleich kann im zweiten Fall dennoch z.Bsp. bei 2003 abgearbeitet 
werden, aber das Zeitfenster kann sich nicht verschieben.

von Falk B. (falk)


Lesenswert?

Veit D. schrieb:

> dem TO geht es um die Abweichung des millis Vergleiches. Der sich im
> dümmsten Fall aufaddiert.

Js, sber . . .

>
> Man hat doch immer noch eine zweite Variable lastMillis oder ähnlich
> benannt mit der man die verstrichene Zeit vergleicht. Wenn man diese
> nach dem Ereignis gewöhnlich mit millis auf den aktuellen Stand bringt,
> dann addieren sich die Laufzeitfehler auf. Bedingt durch die Laufzeit
> von loop.

Nur dann, wenn man es naiv macht.

> den aktuellen millis Wert, sondern addiert zu lastMillis den gewünschten
> Intervallwert (1000ms), dann kann sich kein Abweichungsfehler addieren.

Was falsch ist. Wunschdenken war noch nie sonderlich zielführend.

von Teo D. (teoderix)


Lesenswert?

Manfred schrieb:
> Teo D. schrieb:
>> Was du braucht nennt sich Timer-Interrupt.
>
> Warum? Der Arduino kann in der Hauptschleife rotieren und ein paar
> Zeitstempel prüfen. Man muss natürlich aufpassen, das nicht durch
> blockierende (delay) Unterroutinen zu versauen.

Weil er es Punktgenau, ohne Jitter haben will. Unabhängig davon, was den 
das Programm grade macht. Warum wie so, ist hierbei auch erst mal schei. 
egal.

von Falk B. (falk)


Lesenswert?

Teo D. schrieb:
> Weil er es Punktgenau, ohne Jitter haben will.

Wo steht das?

von Manfred (Gast)


Lesenswert?

Veit D. schrieb:
> Setzt man jedoch nach dem Ereignis/Zeitvergleich lastMillis nicht auf
> den aktuellen millis Wert, sondern addiert zu lastMillis den gewünschten
> Intervallwert (1000ms), dann kann sich kein Abweichungsfehler addieren.
> Auch wenn die loop länger als 1ms benötigt, bleibt das Ereignis im
> 1000ms Fenster. Eine Abweichung kann es dennoch geben, aber wie gesagt,
> addiert sich nicht auf.

Das ist schlüssig, merke ich mir für zukünftige Projekte!

Je nach Auslastung / Aktivität der Hauptschleife kann es etwas Jitter 
geben, aber die Langzeit wird nicht verschoben.

von Veit D. (devil-elec)


Lesenswert?

Falk B. schrieb:
> Veit D. schrieb:

>> den aktuellen millis Wert, sondern addiert zu lastMillis den gewünschten
>> Intervallwert (1000ms), dann kann sich kein Abweichungsfehler addieren.
>
> Was falsch ist. Wunschdenken war noch nie sonderlich zielführend.

Das ist kein Wunschdenken. Das funktioniert genauso ohne 
Aufaddierfehler.
Was soll daran falsch sein? Dein hh:mm:ss Vergleich funktioniert in dem 
Fall übrigens genauso, weil grob auf eine Sekunde aufgelöst.

Manfred hat das schon korrekt wiedergegeben.

von Wolfgang (Gast)


Lesenswert?

Sebastian schrieb:
> 1. Millis startet bei Strom bzw. Reset und läuft knapp 50 Tage
Gleich die erste Annahme ist falsch. Millis() läuft, bis du dem µC 
wieder den Strom abklemmst oder einen Reset auslöst.


Teo D. schrieb:
> Was du braucht nennt sich Timer-Interrupt.

Genau das macht millis() und zwar mit Timer0.

von Nick M. (Gast)


Lesenswert?

Wolfgang schrieb:
> Sebastian schrieb:
>> 1. Millis startet bei Strom bzw. Reset und läuft knapp 50 Tage
> Gleich die erste Annahme ist falsch. Millis() läuft, bis du dem µC
> wieder den Strom abklemmst oder einen Reset auslöst.

Könntest du uns dann mal deine Definition von "Anfang" und "Ende" 
mitteilen?

von Falk B. (falk)


Lesenswert?

Veit D. schrieb:
> Das ist kein Wunschdenken. Das funktioniert genauso ohne
> Aufaddierfehler.

OK, ich hab da wohl ein Zitat verhauen. Ich meinte den Abschnitt vorher. 
Das mit dem Aufaddieren ist korrekt.

von Wolfgang (Gast)


Lesenswert?

Nick M. schrieb:
> Könntest du uns dann mal deine Definition von "Anfang" und "Ende"
> mitteilen?

Was hat diese Frage mit dem Thema des TO zu tun?

von S. R. (svenska)


Lesenswert?

Sebastian schrieb:
> Daraus kann ich jetzt natürlich die Zeit Berechnen. Was ich
> jetzt aber noch nicht ganz genau verstehe ist wie ich damit
> genau 1 Sek immer erreiche.

Garnicht. Millis() ist eine Uhr, was du suchst ist ein Wecker. Eine Uhr 
ist ein wesentlicher Bestandteil eines Weckers, den Rest musst du selbst 
dazubasteln.

von Nick M. (Gast)


Lesenswert?

Wolfgang schrieb:
> Was hat diese Frage mit dem Thema des TO zu tun?

Die Antwort würde helfen deine ansonsten unsinnige Antwort verständlich 
zu machen. Also wäre der TO nicht noch zusätzlich verwirrt wenn er dein 
Posting liest.
Ausser dir ging es nur darum einfach mal sinnfrei zu widersprechen.

von Wolfgang (Gast)


Lesenswert?

Lothar M. schrieb:
> Und wenn du jeden Sekundenwechsel auf 0,1s genau "erreichen" willst,
> dann darf deine Mainloop niemals länger als 100ms deauern.

Sag nie "niemals" ;-)
Wenn man sich ungeschickt genug anstellt und mit Polling in loop() 
arbeitet, hast du natürlich Recht.

Damit irgendwelche Abläufe in loop() das Auslesen nicht verzögern, ist 
es besser, millis() unabhängig von loop() aufzurufen und zwar mit 
höherer Priorität.

Der einfachste Weg dazu ist, den sowieso für die Zeitfunktion laufenden 
Timer0 zu nutzen und den Aufruf von millis() per Timer Compare Interrupt 
zu veranlassen. Damit reduziert sich der Jitter auf wenige µC-Takte.

von Wolfgang (Gast)


Lesenswert?

Nick M. schrieb:
> Die Antwort würde helfen deine ansonsten unsinnige Antwort verständlich
> zu machen.

Du scheinst millis() nicht verstanden zu haben. Das ist eine Funktion, 
die einen 32 Bit Zähler ausliest, der etwa im 1ms-Takt hochgezählt wird, 
sobald der µC läuft - und wenn er hundert Jahre läuft. Mit Integer 
Arithmetik sollte man allerdings schon klar kommen, um den zu nutzen.

Kannst du dein Unverständnis bitte mal in Form einer klaren Frage 
formulieren?

von Nick M. (Gast)


Lesenswert?

Wolfgang schrieb:
> Kannst du dein Unverständnis bitte mal in Form einer klaren Frage
> formulieren?

Ich hab keine Frage, ich versteh das schon. Selbst wenn du versuchst mir 
was unterzujubeln.

Es ging um den Unsinn:

Wolfgang schrieb:
> Sebastian schrieb:
>> 1. Millis startet bei Strom bzw. Reset und läuft knapp 50 Tage
> Gleich die erste Annahme ist falsch. Millis() läuft, bis du dem µC
> wieder den Strom abklemmst oder einen Reset auslöst.

Die Aussage von Sebastian ist richtig. Dein Widerspruch ist unsinnig, 
die Begründung dazu um so mehr.
Du hast einen Widerspruch gebastelt, indem du dich auf das Ende 
beziehst, Sebastian aber vom Anfang sprach.

von Wolfgang (Gast)


Lesenswert?

Nick M. schrieb:
> Die Aussage von Sebastian ist richtig. Dein Widerspruch ist unsinnig,
> die Begründung dazu um so mehr.

Wo bitte hört millis() nach 50 Tagen auf zu laufen, mal ganz davon 
abgesehen, dass millis() nur eine Funktion ist, um einen Zähler zu 
lesen?

von Falk B. (falk)


Lesenswert?

Wolfgang schrieb:
> Wo bitte hört millis() nach 50 Tagen auf zu laufen,

Der Zähler läuft über und fängt bei 0 an. Je nach Auswertung kann das zu 
Problemen führen.
1
#define DELTA_T 1000 //ms
2
unsigned long now = millis();
3
4
// Dieser Vergleich schlägt beim Überlauf fehl!!!
5
If ((now > (before + DELTA_T)) {
6
  // DELTA_T Zeitdifferenz erreicht
7
  foo();
8
  before = now;
9
}
10
11
// Dieser Vergleich funktioniert auch beim Überlauf
12
If ((now - before) > DELTA_T) {
13
  // DELTA_T Zeitdifferenz erreicht
14
  foo();
15
  before = now;
16
}

von Wolfgang (Gast)


Lesenswert?

Falk B. schrieb:
> Der Zähler läuft über und fängt bei 0 an. Je nach Auswertung kann das zu
> Problemen führen.

Aach - so ist das nun mal mit einem Zähler mit endlicher Breite - 
irgendwann läuft der über. Wenn 32 Bit nicht reichen, muss man den 
Überlauf auswerten und entsprechend agieren oder die Rechnung 
überlaufsicher formulieren.

Wie gesagt:

Wolfgang schrieb:
> Mit Integer Arithmetik sollte man allerdings schon klar kommen, um den
> zu nutzen.

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

Hey Sebastian,

also wenn du in der loop() wirklich sooo ewig brauchen solltest,
dann hast du recht - das macht dann mit millis() keinen Sinn.

Du brauchst einen Timer mit Interrupt, in dem du dir dann die Zeit 
basteln kannst wie du magst.

Entweder du initialisierst dir einen eigenen Timer (außer Timer0, der 
wird vom Arduino für die millis verwendet).

Oder du manipulierst die Arduino Sourcen, find es eh voll daneben, dass 
es da standardmäßig keine Callback gibt die man nutzen könnte.

Da könntest du wie folgt vorgehen:
Pfad ist Arduino Install-Ordner, z.B.:
"C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino"

1)
Die Datei "Arduino.h" um folgende Zeile erweitern:
1
bool setTimer0Callback(void (*)(void));

2)
Die Datei "wiring.c" um folgendes erweitern:
1
static void (*timer0_callback)(void);
2
3
bool setTimer0Callback(void (*cb)(void))
4
{
5
  if (cb == NULL)
6
  {
7
    return false;
8
  }
9
  
10
  timer0_callback = cb;
11
  
12
  return true;
13
}
14
15
/**
16
* Weiterhin musst du die vorhandenen ISR um den Callback erweitern.
17
*/
18
#if defined(TIM0_OVF_vect)
19
ISR(TIM0_OVF_vect)
20
#else
21
ISR(TIMER0_OVF_vect)
22
#endif
23
{
24
  // copy these to local variables so they can be stored in registers
25
  // (volatile variables must be read from memory on every access)
26
  unsigned long m = timer0_millis;
27
  unsigned char f = timer0_fract;
28
29
  m += MILLIS_INC;
30
  f += FRACT_INC;
31
  if (f >= FRACT_MAX) {
32
    f -= FRACT_MAX;
33
    m += 1;
34
  }
35
36
  timer0_fract = f;
37
  timer0_millis = m;
38
  timer0_overflow_count++;
39
  
40
    /* Dies ist dein Callback-Aufruf, falls initialisiert. */
41
  if (timer0_callback != NULL)
42
  {
43
    timer0_callback();
44
  }
45
}

3)
Dann kannst es wie folgt nutzen:
1
/* Oder alles schön als struct verpackt, wie du magst */
2
volatile uint16_t ms;
3
volatile uint8_t sec;
4
volatile uint8_t min;
5
6
void mein_timer_callback(void)
7
{
8
  ms = ++ms % 1000;
9
10
  if (!ms)
11
  {
12
    sec = ++sec % 60;
13
    
14
    if (!sec)
15
    {
16
      min = ++min % 60;
17
    }
18
  }
19
}
20
21
void setup()
22
{
23
   setTimer0Callback(mein_timer_callback);
24
}
25
26
void loop()
27
{
28
}

Hab dir die Dateien mal angehängt.

Oder du erweiterst die "wiring.c" direkt um eine Zeitstruktur die
in der ISR inkrementiert wird und baust zusätzlich eine
getTime() Funktion zu den millis() - dann sparst dir das mit dem 
Callback.

Gibt mehrere Möglichkeiten.

Man könnte die Callback auch als "weak" definieren, dann würde man sich 
die set-Funktion sparen - aber naja, wie gesagt, 1000 Wege führen zum 
Ziel.

: Bearbeitet durch User
von Zeno (Gast)


Lesenswert?

Keiner schrieb:
> Wenn ich jetzt die Funktion millis
> () benutze und den Interval auf 1000ms setzte und dann daraufhin eine
> Variable Sek um 1 hochzähle kann es doch sein das erst Millis bei 1010ms
> die If Schleife durchläuft. Dann fehlen mir doch schon 10ms

Wenn Du es so machst wie Du schreibst, dann kannst Du nicht einfach die 
Sekunden hochzählen. Du mußt aus der Zeitdifferenz die Sekunden 
berechnen und einer Variablen zuweisen.
1
long Sekunden;
2
long aktmillis=0;
3
long startzeit=millis();
4
5
for (;;){
6
//irgendwelcher Code
7
aktmillis=millis();
8
Sekunden=(aktmillis-startzeit)/1000;
9
}

Schön ist natürlich anders. Man macht das eigentlich, wie schon mehrfach 
beschrieben mit einem Timerinterrupt.

von Einer K. (Gast)


Lesenswert?

Adam P. schrieb:
> Du brauchst einen Timer mit Interrupt, in dem du dir dann die Zeit
> basteln kannst wie du magst.
>
> Entweder du initialisierst dir einen eigenen Timer (außer Timer0, der
> wird vom Arduino für die millis verwendet).
>
> Oder du manipulierst die Arduino Sourcen, find es eh voll daneben, dass
> es da standardmäßig keine Callback gibt die man nutzen könnte.

Ist doch gar nicht nötig.

Arduino nutzt den Timer0 Overflow. bei ca 0,98 kHz
Der Timer0 hat 2 weitere Compare Interruptquellen.
Nutzt man diese, hat man mindestens einen Takt in der gleichen Frequenz.
Wen man beide geschickt nutzt kommt man auf die doppelte.

Dafür muss man nicht in den originalen Quellen rumbasteln.
Zudem sind Callbacks an der Stelle recht ineffektiv, führen zu langen 
push pop Orgien.

Alles in allem: Du hast recht, es ist ein Weg.
Aber, es ist ein ungeschickter.

von Carl D. (jcw2)


Lesenswert?

Falk B. schrieb:
> Wolfgang schrieb:
>> Wo bitte hört millis() nach 50 Tagen auf zu laufen,
>
> Der Zähler läuft über und fängt bei 0 an. Je nach Auswertung kann das zu
> Problemen führen.

Und die richtige Auswertung ist (wie Falk schreibt):
>
1
> #define DELTA_T 1000 //ms
2
> unsigned long now = millis();
3
> 
4
> // Dieser Vergleich funktioniert auch beim Überlauf
5
> If ((now - before) > DELTA_T) {
6
>   // DELTA_T Zeitdifferenz erreicht
7
>   foo();
8
>   before = now;
9
> }
10
>

Denn die Differenz zweier unsigned long hängt (solange sie unter 2^32 
bleibt; das sind >49 Tage) nicht von eventuellem Überlauf ab.

Wenn man jetzt noch eine Kleinigkeit verbessert, dann hat man sogar 
einen mittleren Abstand von 1000ms (in Rahmen der Taktgenauigkeit).
1
#define DELTA_T 1000UL //ms
2
unsigned long now = millis();
3
4
// Dieser Vergleich funktioniert auch beim Überlauf
5
If ((now - before) > DELTA_T) {
6
  // DELTA_T Zeitdifferenz erreicht
7
  foo();
8
  // wann hätte der aktuelle Lauf sein sollen, auch wenn die
9
  // restliche loop() mal wieder länger dauert (<1000ms)
10
  bevor += DELTA_T; // auch hier funktioniert der Überlauf
11
}
12
13
Wenn man meint, die loop() wäre zu schnell, kann man natürlich noch ein bisschen 32-Bit Multiplikation/Division einflechten.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Generell muss man sagen, daß der Ansatz mit dem millis() und Vergleich 
zum alten Wert eher so lala ist. Kann man machen, geht für viele 
einfache bis mittlere Ansprüche. Wenn es aber besser, genauer und vor 
allem jitterarm sein soll, will man einen direkten Aufruf im EXAKTEN 
Zeitraster. Wie das geht, sieht man u.a. hier, das geht auch auf dem 
Arduino mit einem Timer1 oder Timer2.

https://www.mikrocontroller.net/articles/Multitasking#Verbesserter_Ansatz_mit_Timer

Das kann man sogar noch sehr leicht um eine Stromsparfunktion mittels 
Sleep Mode erweitern, einfach am Anfang der while(1) Schleife den 
Sleep Mode aktivieren. Die CPU wacht dann beim Interrupt wieder auf und 
rattert alles einmal durch.

https://www.mikrocontroller.net/articles/Sleep_Mode#UART_Terminal

von Nick M. (Gast)


Lesenswert?

Wolfgang schrieb:
> Wo bitte hört millis() nach 50 Tagen auf zu laufen,

Das nennt sich Überlauf. Der TO hat das etwas schlampig beschrieben aber 
dennoch verwertbar. Denn nach 50 Tagen ist die Zahl nicht mehr sinnvoll 
auswertbar weil sie wieder bei Null beginnt.

Ich mach uptime Zähler (denn das ist die bessere Bezeichnung) immer als 
uint_64t in einer Auflösung von 1/100 Sekunden.
Diese Plattform traut sich wohl selbst nur eine uptime von 50 Tagen zu, 
was ja auch schon recht bezeichnend ist. Scheinbar gibt es sogar paar, 
die dann auch tatsächlich nicht so weit denken können.

> mal ganz davon
> abgesehen, dass millis() nur eine Funktion ist, um einen Zähler zu
> lesen?

Was soll das billige Ablenkungsmanöver? Bleib mal bei einem Punkt, du 
verlierst die Übersicht schon so schnell genug.

von Donner (Gast)


Lesenswert?

Zeno schrieb:
> Keiner schrieb:
>> Wenn ich jetzt die Funktion millis
>> () benutze und den Interval auf 1000ms setzte und dann daraufhin eine
>> Variable Sek um 1 hochzähle kann es doch sein das erst Millis bei 1010ms
>> die If Schleife durchläuft. Dann fehlen mir doch schon 10ms
>
> Wenn Du es so machst wie Du schreibst, dann kannst Du nicht einfach die
> Sekunden hochzählen. Du mußt aus der Zeitdifferenz die Sekunden
> berechnen und einer Variablen zuweisen.
>
1
> long Sekunden;
2
> long aktmillis=0;
3
> long startzeit=millis();
4
> 
5
> for (;;){
6
> //irgendwelcher Code
7
> aktmillis=millis();
8
> Sekunden=(aktmillis-startzeit)/1000;
9
> }
10
>
>
> Schön ist natürlich anders. Man macht das eigentlich, wie schon mehrfach
> beschrieben mit einem Timerinterrupt.

Genau so würde ich es auch machen. Millis() nutzt ja schon einen 
Timerinterrupt, weshalb braucht man da noch einen weiteren. Und mit der 
Differenz 'millis() - startzeit' kann man ja eine Zeitspanne bis zu 50 
Tagen abdecken.

Sebastian schrieb:
> Hallo ich bin mich gerade mit der Funktion Millis am beschäftigen. Mein
> Plan ist es eine Steuerung mit Touchscreen zu bauen wo ich Zeiten
> Temperaturen einstellen kann und dann das Programm eine Heizplatte
> steuert. Da ich bis jetzt nur sehr Mini Projekte gemacht habe und vorher

Ich denke eine Heizplatte betreibt man maximal einige Stunden (Minuten 
oder Sekunden), und dies kann man mit der Millis()differenz problemlos 
abdecken. Da die Differenz direkt in Sekunden/Minuten/Stunden 
umgerechnet werden kann, ist ein zusätzlicher Sekundentimer unnötig.

Manfred schrieb:
> Ich mache das andersherum: Ich rechne die Zielzeit aus und prüfe, ob
> millis() größer als diese ist. Dann kneift mich ein evtl. Überlauf
> nicht.

>
1
int LEDInfo = 10;
2
> unsigned long int FlashLEDTime = 0;
3
> 
4
> void loop() {  // Hauptschleife
5
> if (millis() > FlashLEDTime == HIGH) {  // LED blinken lassen
6
>   digitalWrite (LEDInfo, !digitalRead(LEDInfo));
7
>   FlashLEDTime = (millis() + 450);
8
> }
9
>

Und genau so macht man es nicht!!!

Wenn hier der millis() Zähler nach etwa 52 Tagen kurz vor dem Überlauf 
steht (2^32 - 449) dann wird 'FlashLEDTime = (millis() + 450)' in einen 
Wert von kleiner 450 resultieren, und die Abfrage schlägt 450 msec lang 
jede Millisekunde zu. Das ist bei einer LED sicher unkritisch, die 
leuchtet hier leuchtet dann eine halbe Sekunde mit halber Helligkeit, 
aber für andere Aktionen kann das schon sehr kritisch werden. Und so 
einen Fehĺer zu finden, der nur alle 52 Tage auftritt, ist schon sehr 
sehr schwierig.

von Egon D. (Gast)


Lesenswert?

Wolfgang schrieb:

> Sebastian schrieb:
>> 1. Millis startet bei Strom bzw. Reset und läuft
>> knapp 50 Tage
>
> Gleich die erste Annahme ist falsch. Millis() läuft,
> bis du dem µC wieder den Strom abklemmst oder einen
> Reset auslöst.

Nun ja, es gibt immer zwei Möglichkeiten.

Die eine ist, eine... ähh... arg grenzwertige (für die
Schweiz: falsche) Aussage SO richtigzustellen, dass
jeder Leser weiss, was gemeint ist. Also etwa in der
Form: "Nein, der Zähler, den millis() abfragt, läuft
ewig und drei Tage, sofern der Controller so lange Saft
hat -- richtig ist aber, dass der Zähler nach 50 Tage
überläuft , also wieder von Null anfängt mit zählen."

Die andere Möglichkeit besteht darin, in bewährter
Mathematiker-Manier "FALSCH!!!" zu brüllen, was ebenso
korrekt wie nutzlos ist.


Aber natürlich, die zweite Möglichkeit eignet sich besser,
einer dahindümpelnden Diskussion endlich mal eine gewisse
Lebhaftigkeit zu verleihen...

von Wolfgang (Gast)


Lesenswert?

Nick M. schrieb:
> Das nennt sich Überlauf. Der TO hat das etwas schlampig beschrieben aber
> dennoch verwertbar.

"Aufhören zu laufen" und "Überlauf" sind verschiedene Dinge. Wenn du das 
im Rahmen von "schlampig beschrieben" als gleichwertig betrachtest, ist 
das dein Problem.

von Nick M. (Gast)


Lesenswert?

Wolfgang schrieb:
> Wenn du das
> im Rahmen von "schlampig beschrieben" als gleichwertig betrachtest, ist
> das dein Problem.

Lies das da:
Beitrag "Re: Sekunden,Minuten zählen mit der Funktion Millis()"

und schau dir bitte nochmal deine Minuspunkte an die du im Laufe deines 
Geschwätzes dafür kassiert hast.
Und fang mal an zu denken, dein präpupertäres Fußaufstampfen hilft dir 
hier nicht weiter. Das klappt nur bei Mutti.

von Egon D. (Gast)


Lesenswert?

Nick M. schrieb:

> Wolfgang schrieb:
>> Wo bitte hört millis() nach 50 Tagen auf zu laufen,
>
> Das nennt sich Überlauf. Der TO hat das etwas schlampig
> beschrieben aber dennoch verwertbar. Denn nach 50 Tagen
> ist die Zahl nicht mehr sinnvoll auswertbar weil sie
> wieder bei Null beginnt.

... was natürlich Unsinn ist. Das ist jedoch noch lange
kein Grund, es nicht mit kühner Stirne, stozer Brust zu
behaupten.
Merke: Sachkunde ist einer lebhaften Diskussion durchaus
abträglich.

von Falk B. (falk)


Lesenswert?

Donner schrieb:
> Und so
> einen Fehĺer zu finden, der nur alle 52 Tage auftritt, ist schon sehr
> sehr schwierig.

Good point!

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> ... was natürlich Unsinn ist. Das ist jedoch noch lange
> kein Grund, es nicht mit kühner Stirne, stozer Brust zu
> behaupten.

Dann schau dir doch mal die Verrenkungen an wenn du dir selbst einen 
Zeitpunkt für ein Ereignis in der Zukunft setzen willst. Z.B. Daten an 
einen Server melden in 3 Stunden. Und das, wenn der Überlauf dazwischen 
liegt.

Eine interne Zeit die überläuft ist ein prinzipieller Designfehler.
Genauso ist es ein prinzipieller Designfehler dafür eine Zeit zu 
verwenden die von aussen verstellt werden kann oder die Lokalzeit (die 
auch eine Sommerzeit hat) zu verwenden.

Solche üblen Fehler zu erkennen trau ich dir nicht zu, zu erkennen. Da 
fehlts dir an Weitblick, 50 Tage scheinen bei dir schon eine komplette 
Überforderung zu sein.

> Merke: Sachkunde ist einer lebhaften Diskussion durchaus
> abträglich.

Dann halt dich bitte auch dran.

von Wolfgang (Gast)


Lesenswert?

Nick M. schrieb:
> Dann schau dir doch mal die Verrenkungen an wenn du dir selbst einen
> Zeitpunkt für ein Ereignis in der Zukunft setzen willst. Z.B. Daten an
> einen Server melden in 3 Stunden. Und das, wenn der Überlauf dazwischen
> liegt.

Die "Verrenkungen" hat Falk bereits vorgeführt. Sorry, wenn dich das 
überfordert.

Falk B. schrieb:
> If ((now - before) > DELTA_T)
> {
> ...
> }

von Egon D. (Gast)


Lesenswert?

Nick M. schrieb:

> Egon D. schrieb:
>> ... was natürlich Unsinn ist. Das ist jedoch noch lange
>> kein Grund, es nicht mit kühner Stirne, stozer Brust zu
>> behaupten.
>
> Dann schau dir doch mal die Verrenkungen an [...]

Jaja, schon gut.

Du weisst natürlich alles besser -- nicht nur als ich,
sondern auch als alle anderen. In Ordnung. Nur weiter
so.

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> Jaja, schon gut.
>
> Du weisst natürlich alles besser -- nicht nur als ich,
> sondern auch als alle anderen. In Ordnung. Nur weiter
> so.

Dann erklär mal auf die Schnelle was du machst, wenn das nächste 
Ereignis in 2 Monaten sein soll.
Kannst du so weit denken? Nein. Aber klug daherreden. Das kannst du und 
Wolfgang.

von Rolf M. (rmagnus)


Lesenswert?

Falk B. schrieb:
> Donner schrieb:
>> Und so
>> einen Fehĺer zu finden, der nur alle 52 Tage auftritt, ist schon sehr
>> sehr schwierig.
>
> Good point!

Das musste auch Microsoft schon feststellen, die in ihre Win32 API 
damals den gleichen Fehler eingebaut und in Windoww 95 selbst benutzt 
hatten. Wer konnte auch damit rechnen, dass je einer einen Computer 50 
Tage am Stück laufen lassen will? ;-)

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Ach, diese ganzen "Problemchen" lassen sich mit ein bisschen Logik und 
den Grundrechenarten erledigen.

Ich verstehe gar nicht wieso ihr euch dafür so angehen müsst.

von Sebastian (Gast)


Lesenswert?

Vielen dank für die nette Konversation. Ich denke ich werde einfach wie 
früher in Bascom den Timer1 mir einfach so einstellen das er genau bei 1 
Sekunde einen Interrupt hat. Die Aussagen hier sind hier sehr 
unterschiedlich und bringen mich nicht wirklich weiter. Aber ich denke 
persönlich ist die Funktion Millis für die meisten Anwendungen genau 
genug wer es aber ganz genau will nimmt den Timer Interrupt.

Trotzdem allen einen großen Dank für die Hilfe

von Nick M. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Ach, diese ganzen "Problemchen" lassen sich mit ein bisschen Logik und
> den Grundrechenarten erledigen.

Dann führ das doch mal vor.

Nimmt man uint64, dann ist plötzlich alles easy und entspannt:
1
if (now >= triggerTime) {
2
  doIt();
3
  triggerTime = 0;
4
}

Fertig. Man muss lediglich triggerTime löschen (oder DELTA_T). In dem 
von euch so gelobten Beispielen muss man DELTA_T löschen und den Fall 
berücksichtigen, dass DELATA_T 0 ist und dann NICHTS zu tun ist. Die 
zusätzliche Falle entfällt bei mir.

Na ihr Schlauberger? Hab ihr es jetzt kapiert?


OK, die uptime genügt nur für 584 Jahre mit 1/1000 Sekunden. Aber so 
lang wird wohl keine Hardware überleben.

von Egon D. (Gast)


Lesenswert?

Nick M. schrieb:

> Egon D. schrieb:
>> Jaja, schon gut.
>>
>> Du weisst natürlich alles besser -- nicht nur als
>> ich, sondern auch als alle anderen. In Ordnung.
>> Nur weiter so.
>
> Dann erklär mal auf die Schnelle was du machst,
> wenn das nächste Ereignis in 2 Monaten sein soll.

Wie komme ich denn dazu?

Deine Behauptung war, dass sich der Timer-Wert nach
erfolgtem Überlauf nicht mehr sinnvoll auswerten lässt.
Diese Behauptung ist FALSCH . Diskussion beendet.

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> Wie komme ich denn dazu?

Geht nicht, weil du dich dann blamieren würdest. Damit ist alles klar.

von Nick M. (Gast)


Lesenswert?

Nick M. schrieb:
> Fertig. Man muss lediglich triggerTime löschen (oder DELTA_T). In dem
> von euch so gelobten Beispielen muss man DELTA_T löschen und den Fall
> berücksichtigen, dass DELATA_T 0 ist und dann NICHTS zu tun ist. Die
> zusätzliche Falle entfällt bei mir.

MIST!
TriggerTime sollte man auf MAXUINT64 setzen, nicht Null.

von Einer K. (Gast)


Lesenswert?

Nick M. schrieb:
> Dann führ das doch mal vor.
> ....
> Na ihr Schlauberger? Hab ihr es jetzt kapiert?
Nano, du hast ja richtig Schaum vorm Mund...

Haben deine Einlassungen irgendwas mit dem Problem des TO zu tun?

von Nick M. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Haben deine Einlassungen irgendwas mit dem Problem des TO zu tun?

Ja. Sie haben direkt was mit Punkt 1) des Ursprungspostings zu tun.
Ist das für dich zu schwer zu erkennen?

von Einer K. (Gast)


Lesenswert?

Nick M. schrieb:
> Ja. Sie haben direkt was mit Punkt 1) des Ursprungspostings zu tun.
> Ist das für dich zu schwer zu erkennen?
Ja!
Du siehst da offensichtlich Geister.

Hat Punkt 1 überhaupt mit dem Problem des TO zu tun, oder ist das eher 
eine Nebelkerze, mit der er sich selbst täuscht?

Ich sehe nicht, dass er Intervalle größer 50 Tage nutzen möchte.

Bedenke:
Solange die Zeitintervalle kürzer, als die genannten 50 Tage, sind 
kompensieren sich die Überläufe. (wenn man es richtig anstellt)

von Stefan F. (Gast)


Lesenswert?

Donner schrieb:
> So einen Fehler zu finden, der nur alle 52 Tage auftritt,
> ist schon sehr sehr schwierig.

Falk B. schrieb:
> Good point!

Erinnert mich an meinen Drucker der gerade schon wieder seine 
Konfiguration vergessen hat. Das macht er 2x pro Jahr und das nicht nur 
bei mir. Vom HP Support bekommt man natürlich alle erdenklichen 
Antworten dazu, nur keine hilfreiche.

von Falk B. (falk)


Lesenswert?

Nick M. schrieb:
> OK, die uptime genügt nur für 584 Jahre mit 1/1000 Sekunden. Aber so
> lang wird wohl keine Hardware überleben.

640kB reichen für alle Zeiten ;-)

von Stefan F. (Gast)


Lesenswert?

Falk B. schrieb:
> 640kB reichen für alle Zeiten ;-)

Ich glaube, dass hatte jemand damals nur gesagt, um die Konkurrenz in 
Sicherheit zu wiegen, die gerade an größeren PC arbeitete.

Ganz ähnlich auch die Aussage von angeblich Intel, dass mehr als 333 MHz 
rein physikalisch unmöglich seien. Schon ein halbes Jahr später hatten 
alle Intel-Inside Rechner im Handel bereits 500 MHz.

von Falk B. (falk)


Lesenswert?

Sebastian schrieb:

> früher in Bascom den Timer1 mir einfach so einstellen das er genau bei 1
> Sekunde einen Interrupt hat.

Kann man machen, ist aber meistens unpraktisch. Nimm lieber 10ms oder 
100ms, damit kann man auch andere Dinge erledigen, z.B. Updates von 
Displays, Timerouts etc. 1s ist für einen Mikrocontroller eine EWIGKEIT!

https://www.mikrocontroller.net/articles/Multitasking#Verbesserter_Ansatz

von Nick M. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Hat Punkt 1 überhaupt mit dem Problem des TO zu tun, oder ist das eher
> eine Nebelkerze, mit der er sich selbst täuscht?

Das ist ein Punkt des TO Also ist es ein Problem des TO.

> Ich sehe nicht, dass er Intervalle größer 50 Tage nutzen möchte.

Ich sehe nicht, dass er nur Intervalle gleiner 50 Tg nutzen will.
Der Punkt ist aber, dass hier andauernd behauptet wird, wie elegant das 
Problem doch gelöst werden kann.
Es geht eben nicht. Siehe dein folgendes Zitat:

> Bedenke:
> Solange die Zeitintervalle kürzer, als die genannten 50 Tage, sind
> kompensieren sich die Überläufe. (wenn man es richtig anstellt)

Eben, meine Rede. Aber hier gibt es genügend Leuchten denen das nicht 
einleuchtet.

Ich hab beschrieben wie man das grundlegend richtig macht.

von Egon D. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:

> Hat Punkt 1 überhaupt mit dem Problem des TO
> zu tun,

Nein.


> oder ist das eher eine Nebelkerze,

"Nebelkerze" suggeriert Absicht; das sicher nicht.


> mit der er sich selbst täuscht?

Mir ist nicht klargeworden, was der TO eigentlich
glaubt:

a) Der Timer bleibt nach 50 Tagen stehen.
b) Der Timer-Wert ist in der Nähe des Überlaufes
   nicht auswertbar.

a) ist Unsinn; das wissen wir.

b) ist auch Unsinn; das wissen alle außer Nick Müller.

Lediglich

  c) Mit einem Timer, der 50 Tage Zählumfang hat,
     lassen sich keine Intervalle erzeugen, die
     länger als 50 Tage sind

ist richtig.

(Ich bilde mir zwar ein, dass die Grenze schon beim
halben Zählumfang liegt, bekomme aber die Herleitung
jetzt nicht zusammen. Mag mich also irren.)


> Ich sehe nicht, dass er Intervalle größer 50 Tage
> nutzen möchte.

Korrekt; soweit ich mitbekommen habe, war davon keine
Rede.

von Nick M. (Gast)


Lesenswert?

Falk B. schrieb:
> 640kB reichen für alle Zeiten ;-)

Hehe! :-)  OK, der Punkt geht an dich, aber ich hab schon damit 
gerechnet und daher die Jahre ausgerechnet.

von Sebastian (Gast)


Lesenswert?

Ja hast du recht werde das ganze schneller laufen lassen und das 
Touchdisplay TFT und Zeit in die ISR packen

von Falk B. (falk)


Lesenswert?

Wenn man den Unsinn mal weiter spinnt, der Arduino hat schon einen 64 
Bit Zähler, wenn gleich auf 2 Variablen aufgeteilt. Die kann man atomar 
auslesen und auswerten.

wiring.c
1
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
2
// the overflow handler is called every 256 ticks.
3
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
4
5
// the whole number of milliseconds per timer0 overflow
6
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
7
8
// the fractional number of milliseconds per timer0 overflow. we shift right
9
// by three to fit these numbers into a byte. (for the clock speeds we care
10
// about - 8 and 16 MHz - this doesn't lose precision.)
11
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
12
#define FRACT_MAX (1000 >> 3)
13
14
volatile unsigned long timer0_overflow_count = 0;
15
volatile unsigned long timer0_millis = 0;
16
static unsigned char timer0_fract = 0;
17
18
#if defined(TIM0_OVF_vect)
19
ISR(TIM0_OVF_vect)
20
#else
21
ISR(TIMER0_OVF_vect)
22
#endif
23
{
24
  // copy these to local variables so they can be stored in registers
25
  // (volatile variables must be read from memory on every access)
26
  unsigned long m = timer0_millis;
27
  unsigned char f = timer0_fract;
28
29
  m += MILLIS_INC;
30
  f += FRACT_INC;
31
  if (f >= FRACT_MAX) {
32
    f -= FRACT_MAX;
33
    m += 1;
34
  }
35
36
  timer0_fract = f;
37
  timer0_millis = m;
38
  timer0_overflow_count++;
39
}

von Falk B. (falk)


Lesenswert?

Sebastian schrieb:
> Ja hast du recht werde das ganze schneller laufen lassen und das
> Touchdisplay TFT und Zeit in die ISR packen

Nö. Bestenfalls den Zeitzähler packt man in die ISR. Die Anzeige des 
TFT, Verarbeitung der Eingabe etc. macht man in der Hauptschleife mit 
Flags.

https://www.mikrocontroller.net/articles/Interrupt#Steuersignale_zwischen_ISR_und_Hauptprogramm

von Sebastian (Gast)


Lesenswert?

Und nein mehr als 50 Tage wollte ich nicht und ja ich meinte eigentlich 
nur den Überlauf das der Timer nicht stehen bleibt ist klar

von Sebastian (Gast)


Lesenswert?

Danke für den link

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> a) ist Unsinn; das wissen wir.
>
> b) ist auch Unsinn; das wissen alle außer Nick Müller.
>
> Lediglich
>
>   c) Mit einem Timer, der 50 Tage Zählumfang hat,
>      lassen sich keine Intervalle erzeugen, die
>      länger als 50 Tage sind
>
> ist richtig.

Na also, damit hast du eben selbst zugegeben dass du nicht mit dem 
Überlauf arbeiten kannst. Ausser unter bestimmten Voraussetzungen. Und 
das ist halt der Punkt in dem sich Bastler von Profis unterscheiden.
Bastler können nur bis zu maximal 50 Tage weit denken.

Die Bastler sind namentlich:
Egon B., Arduino Fanboy und Wolfgang der sich zusätzlich durch seine 
laute Klappe "auszeichnet".

von Falk B. (falk)


Lesenswert?

Egon D. schrieb:
> c) Mit einem Timer, der 50 Tage Zählumfang hat,
>      lassen sich keine Intervalle erzeugen, die
>      länger als 50 Tage sind
>
> ist richtig.
>
> (Ich bilde mir zwar ein, dass die Grenze schon beim
> halben Zählumfang liegt, bekomme aber die Herleitung
> jetzt nicht zusammen. Mag mich also irren.)

Einfach mal ein Beispiel aufschreiben, ggf. mit kleineren Zahlen.

8 Bit Zähler, 0-255.
1
// ohne Überlauf
2
now = 255
3
before = 200
4
now - before = 55
5
// mit Überlauf
6
now = 1
7
before = 255
8
now - before = -254 = 2 (Moduloverhalten der Subtraktion)

Das ganze geht auch mit 32 Bit ;-)

von Nick M. (Gast)


Lesenswert?

Könnt ihr großen Meister auch noch die offene Frage beantworten und 
nicht wieder elegant ignorieren:
Was macht ihr, wenn ein einmaliges Ereignis nach DELTA_T abgehandelt 
wurde. Das ist der code dazu von euch der so jedenfalls nicht 
funktioniert:
1
// Dieser Vergleich funktioniert auch beim Überlauf
2
If ((now - before) > DELTA_T) {
3
  // DELTA_T Zeitdifferenz erreicht
4
  foo();
5
  // wann hätte der aktuelle Lauf sein sollen, auch wenn die
6
  // restliche loop() mal wieder länger dauert (<1000ms)
7
  bevor += DELTA_T; // auch hier funktioniert der Überlauf
8
}

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> Wenn man den Unsinn mal weiter spinnt, der Arduino hat schon einen 64
> Bit Zähler, wenn gleich auf 2 Variablen aufgeteilt. Die kann man atomar
> auslesen und auswerten.

Ich vermute das solltest du nochmal überdenken...
Ich sehe da keinen 64 Bit breiten Zähler.

Egal, wie man die die 32 Bit Variablen kombiniert, aber auf einen 
"vernünftigen" 64 Bit ms Zähler kommt man da nicht.
Da muss man sich schon etwas mehr Mühe geben.

Nick M. schrieb:
> Die Bastler sind namentlich:
> Egon B., Arduino Fanboy und Wolfgang der sich zusätzlich durch seine
> laute Klappe "auszeichnet".

Du hast ganz offensichtlich nicht die eingangs geschilderten Sorgen des 
TO nicht verstanden.
Das macht mich zu einem Bastler, wenn du was nicht verstehst?
Das ist so irrational, dass ich mir ein Grinsen nicht verkneifen kann.

von Egon D. (Gast)


Lesenswert?

Nick M. schrieb:

> Na also, damit hast du eben selbst zugegeben
> dass du nicht mit dem Überlauf arbeiten kannst.

Ich glaube, Dir ist da ein kleiner Irrtum unterlaufen.

Ich habe gesagt, dass man mit einem Intervalltimer,
der einen maximalen Umfang von 50 Tagen hat, keine
Intervalle erzeugen kann, die länger als 50 Tage sind.

Von irgend einem Überlauf habe ich kein Sterbenswörtchen
gesagt. DU bist derjenige, der immer davon anfängt.


> Und das ist halt der Punkt in dem sich Bastler von
> Profis unterscheiden.

Sicher.
Der Bastler nimmt einen Intervalltimer für kurze
Intervalle und einen Kalender für die langen.

Der Profi behauptet, ein paar Stunden ließen sich
NUR mittels 64 bit abmessen.

Doch, das kommt so hin.

von Einer K. (Gast)


Lesenswert?

Nick M. schrieb:
> Könnt ihr großen Meister auch noch die offene Frage beantworten
> und
> nicht wieder elegant ignorieren:
> Was macht ihr, wenn ein einmaliges Ereignis nach DELTA_T abgehandelt
> wurde. Das ist der code dazu von euch der so jedenfalls nicht
> funktioniert:
> // Dieser Vergleich funktioniert auch beim Überlauf
> If ((now - before) > DELTA_T) {
>   // DELTA_T Zeitdifferenz erreicht
>   foo();
>   // wann hätte der aktuelle Lauf sein sollen, auch wenn die
>   // restliche loop() mal wieder länger dauert (<1000ms)
>   bevor += DELTA_T; // auch hier funktioniert der Überlauf
> }
Du wirst einen Status halten/einführen müssen.
Der klassische einfache endliche Automat.

von Falk B. (falk)


Lesenswert?

Nick M. schrieb:
> Könnt ihr großen Meister auch noch die offene Frage beantworten und
> nicht wieder elegant ignorieren:
> Was macht ihr, wenn ein einmaliges Ereignis nach DELTA_T abgehandelt
> wurde.

Das war gar nicht die Frage. Aber auch das ist lösbar!
1
#define DATE_TIME 1402
2
uint8_t date_once_en = 1;
3
date_once = DATE_TIME;
4
5
....
6
7
now = millis();
8
9
// Dieser Vergleich funktioniert auch beim Überlauf
10
If (date_once_en && (now - before) > date_once) {
11
  // DELTA_T Zeitdifferenz erreicht
12
  foo();
13
  date_once_en = 0;
14
}

Und jetzt jammer bloß nicht rum, daß man dazu eine extra Variable 
braucht! Deine 64Bit Zähler sind auch nicht kostenlos, dort steckt die 
Variable vier mal drin!

Wer mit minimalen Arduinomitteln eine IT-Revolution starten will, macht 
so oder so was falsch!

von Falk B. (falk)


Lesenswert?

Arduino Fanboy D. schrieb:
>> Wenn man den Unsinn mal weiter spinnt, der Arduino hat schon einen 64
>> Bit Zähler, wenn gleich auf 2 Variablen aufgeteilt. Die kann man atomar
>> auslesen und auswerten.
>
> Ich vermute das solltest du nochmal überdenken...
> Ich sehe da keinen 64 Bit breiten Zähler.

Ich schon.
1
timer0_millis = m;
2
timer0_overflow_count++;

Das Ganze ist ein klassischer Zähler mit Festkommaarithmetik 
basierend auf DDS bzw. Bresenham.

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> Von irgend einem Überlauf habe ich kein Sterbenswörtchen
> gesagt. DU bist derjenige, der immer davon anfängt.

Kurz zitiert:

Egon D. schrieb:
> Die eine ist, eine... ähh... arg grenzwertige (für die
> Schweiz: falsche) Aussage SO richtigzustellen, dass
> jeder Leser weiss, was gemeint ist. Also etwa in der
> Form: "Nein, der Zähler, den millis() abfragt, läuft
> ewig und drei Tage, sofern der Controller so lange Saft
> hat -- richtig ist aber, dass der Zähler nach 50 Tage
> überläuft , also wieder von Null anfängt mit zählen."

Da hast du auch noch völlig normal argumentiert. Nur irgendwann sind bei 
dir die Sicherungen durchgebrannt. Und dann wurde der Unsinn immer 
größer.

Egon D. schrieb:
> Der Profi behauptet, ein paar Stunden ließen sich
> NUR mittels 64 bit abmessen.
>
> Doch, das kommt so hin.

Wo hast du die paar Stunden bloß her? Bist du nicht in der Lage so zu 
programmieren, dass der Code nicht nur unter möglichst vielen 
Randbedingungen funktioniert? Oder bist du halt einfach nur ein Bastler 
der es gewohnt ist nur irgendwas irgendwie zum Laufen zu bringen?
Und dann versucht durch die fadenscheinigsten Argumente rauszureden. 
Schämst du dich dafür nicht?

von Falk B. (falk)


Lesenswert?

Wundert sich jetzt noch einer, warum die Welt so ist, wie sie ist?
Ich nicht . . . 8-(

von Nick M. (Gast)


Lesenswert?

Falk B. schrieb:
> Und jetzt jammer bloß nicht rum, daß man dazu eine extra Variable
> braucht! Deine 64Bit Zähler sind auch nicht kostenlos, dort steckt die
> Variable vier mal drin!

Keine Angst, ich weiß sogar, dass ein 64 Bit Zähler doppelt so viel 
Platz braucht wie ein 32 Bit Zähler!

> Wer mit minimalen Arduinomitteln eine IT-Revolution starten will, macht
> so oder so was falsch!

Neinnein, mir ging es darum, aufzuzeigen dass die vermeintlich so 
einfache Lösung mit dem Überlauf doch nicht ganz so einfach ist. Denn 
jetzt muss man, -wie du selbst aufgezeigt hast- noch ein flag setzen. 
Und bitte nicht vergessen.

Meine Lösung ist jedenfalls einfacher und weitsichtiger und kann auch 
Intervalle größer 50 Tage lösen.

Das hat nichts mit IT-Revolution zu tun. Das hat damit zu tun, dass 
Bastler häuptsächlich Bastlerlösungen liefern und die bis ans blutige 
Messer verteidigen. Und sich dabei für nichts zu blöd sind.

von Veit D. (devil-elec)


Lesenswert?

Carl D. schrieb:

> Wenn man jetzt noch eine Kleinigkeit verbessert, dann hat man sogar
> einen mittleren Abstand von 1000ms (in Rahmen der Taktgenauigkeit).
> ...

Ich hatte das übrigens genauso gemeint. Ich würde es nur etwas anders 
schreiben. Möglichst ohne define und ohne globale Variablen.
1
void foo (const unsigned long interval)
2
{
3
  static unsigned long lastMillis = 0;
4
5
  if (millis() - lastMillis >= interval)
6
  {
7
    lastMillis += interval;
8
    // mach hier sinnvolle Dinge
9
  }
10
}


Falk B. schrieb:
> Wenn es aber besser, genauer und vor
> allem jitterarm sein soll, will man einen direkten Aufruf im EXAKTEN
> Zeitraster. Wie das geht, sieht man u.a. hier, das geht auch auf dem
> Arduino mit einem Timer1 oder Timer2.
>
> 
https://www.mikrocontroller.net/articles/Multitasking#Verbesserter_Ansatz_mit_Timer

Daraus folgender Code:
1
// Hauptschleife
2
while (1)
3
{
4
  if (flag_1ms)
5
  {
6
    flag_1ms = 0;
7
    taste = taste_lesen();
8
    led_blinken(taste);
9
    uart_lesen();
10
    ...
11
    ...
12
  }
13
}

Hier sehe ich keinen Vorteil gegenüber der millis Abfrage. "flag_1ms" 
ist nur ein Flag was irgendwann abgearbeitet wird. Genauso wie der 
millis Vergleich der irgendwann abgearbeitet wird. In beiden Fällen weiß 
niemand genau wann der Vergleich stattfindet, wenn die Hauptschleife aus 
irgendwelchen Gründen mal kürzer oder länger benötigt. Alles unterliegt 
dem gleichen Jitter. Ohne Jitter geht nur, wenn man es direkt im 
Timerinterrupt erledigt.

von Egon D. (Gast)


Lesenswert?

Nick M. schrieb:

> Egon D. schrieb:
>> Von irgend einem Überlauf habe ich kein Sterbenswörtchen
>> gesagt. DU bist derjenige, der immer davon anfängt.
>
> Kurz zitiert:
>
> Egon D. schrieb:
>> Die eine ist, eine... ähh... arg grenzwertige (für die
>> Schweiz: falsche) Aussage SO richtigzustellen, dass
>> jeder Leser weiss, was gemeint ist. Also etwa in der
>> Form: "Nein, der Zähler, den millis() abfragt, läuft
>> ewig und drei Tage, sofern der Controller so lange Saft
>> hat -- richtig ist aber, dass der Zähler nach 50 Tage
>> überläuft , also wieder von Null anfängt mit zählen."
>
> Da hast du auch noch völlig normal argumentiert.

Weil ich da versucht habe, einen Gesprächspartner, der
auch normal argumentiert hat, darauf hinzuweisen, dass
seine Antwort zwar sachlich richtig ist, für den
Gesprächsverlauf aber nicht sonderlich förderlich.


> Nur irgendwann sind bei dir die Sicherungen
> durchgebrannt. Und dann wurde der Unsinn immer
> größer.

Nun ja, dann such' doch mal den ersten Beitrag heraus,
bei dem meine Sicherung (angeblich) durchgebrannt ist,
und ZITIERE diesen hier vollständig . Dann können
wir weiter darüber diskutieren, falls Dir daran liegt.


> Egon D. schrieb:
>> Der Profi behauptet, ein paar Stunden ließen sich
>> NUR mittels 64 bit abmessen.
>>
>> Doch, das kommt so hin.
>
> Wo hast du die paar Stunden bloß her?

Du wirst es kaum glauben: Aus einem Beitrag des TO.


> Bist du nicht in der Lage [...]

Würde es Dir etwas ausmachen, Dich nicht in Spekulationen
über meine Fähigkeiten zu ergehen, sondern auf das zu
antworten, was tatsächlich geschrieben wurde ?

Vielen Dank im Voraus.

von Falk B. (falk)


Lesenswert?

Nick M. schrieb:
> Meine Lösung ist jedenfalls einfacher und weitsichtiger und kann auch
> Intervalle größer 50 Tage lösen.

Ja, kann sie, braucht aber keiner, schon gar nicht der OP. Es muss nicht 
immer die ultimative, endlos skalierbare Lösung sein, es darf auch mal 
deutlich einfacher sein, der verringerte Aufwand rechtfertigt die 
Grenzen.

> Das hat nichts mit IT-Revolution zu tun. Das hat damit zu tun, dass
> Bastler häuptsächlich Bastlerlösungen liefern und die bis ans blutige
> Messer verteidigen. Und sich dabei für nichts zu blöd sind.

Ich bin einer der Letzten, die aoffensichtlichen MURKS gutheißen würden! 
Aber einfache Lösungen sind OK. Und der Vorschlag mit dem RICHTIGEN 
Vergleich ist so eine!

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Ich finde es nur lustig, wie Nick sich aus den "der Zähler läuft alle 50 
Tage über" einen Anwendungsfall wie folgt konstruiert:

"Aber wenn du Ereignisse in 60 Tagen haben willst, na wie machst du das? 
Na? Na? Immernoch nicht? Bist du so doof? Schau mal, ich weiß das, das 
macht man so. Na? Klingelt's jetzt?"

Nick, ich unterstelle dir ja, dass du mit dem Hammer eine Schraube in 
die Gipswand kriegst. Und sogar, dass dir klar ist, dass entweder ein 
Schraubenzieher oder ein Nagel dafür die bessere Lösung ist.

Es wäre aber durchaus hilfreich, wenn du anderen nicht unterstellen 
würdest, dass sie ausschließlich mit Hammer und Schrauben arbeiten. 
Danke.

von Falk B. (falk)


Lesenswert?

Veit D. schrieb:
> Hier sehe ich keinen Vorteil gegenüber der millis Abfrage.

Ich schon, und nicht nur was den Sleep Mode angeht.
U.a., daß man nicht immer mit dem 32 Bit Zähler hantieren muss. Das 
bringt zwar die CPU nicht um, aber auch keine Vorteile.
Die millis() Methode ist nur ein Arduinogewächs.
Außerdem kann man mit dem Flag zur Echtzeit prüfen, ob das Timing noch 
stimmt, siehe Artikel Multitasking.

von Nick M. (Gast)


Lesenswert?

Egon D. schrieb:
> Weil ich da versucht habe, einen Gesprächspartner, der
> auch normal argumentiert hat, darauf hinzuweisen, dass
> seine Antwort zwar sachlich richtig ist, für den
> Gesprächsverlauf aber nicht sonderlich förderlich.

Ich hab mehr davon zitiert:

Egon D. schrieb:
> Wolfgang schrieb:
>
>> Sebastian schrieb:
>>> 1. Millis startet bei Strom bzw. Reset und läuft
>>> knapp 50 Tage
>>
>> Gleich die erste Annahme ist falsch. Millis() läuft,
>> bis du dem µC wieder den Strom abklemmst oder einen
>> Reset auslöst.
>
> Nun ja, es gibt immer zwei Möglichkeiten.

Du wolltest also sagen, dass Wolfgang normal argumentiert hat? Dir ist 
doch selbst aufgefallen, dass er blanken Unsinnschreibt. Wie gesagt, das 
war alles in Ordnung mit dir.

Hier geht es los:
Egon D. schrieb:
>> Das nennt sich Überlauf. Der TO hat das etwas schlampig
>> beschrieben aber dennoch verwertbar. Denn nach 50 Tagen
>> ist die Zahl nicht mehr sinnvoll auswertbar weil sie
>> wieder bei Null beginnt.
>
> ... was natürlich Unsinn ist. Das ist jedoch noch lange
> kein Grund, es nicht mit kühner Stirne, stozer Brust zu
> behaupten.
> Merke: Sachkunde ist einer lebhaften Diskussion durchaus
> abträglich.

Du hast ja inzwischen selbst zugegeben, dass man selbst mit 
Berücksichtigung des Überlaufs nicht richtig arbeiten kann. Und wie 
sich die Verfechter des einfachen Überlaufs selbst bewiesen haben, es 
doch nicht ganz so einfach ist.

Egon D. schrieb:
>> Wo hast du die paar Stunden bloß her?
>
> Du wirst es kaum glauben: Aus einem Beitrag des TO.

Interessant. Hilft aber nichts weiter. Denn ich hab zum wiederholten 
Male gesagt, dass das mit dem Überlauf nicht richtig klappt. Der TO hat 
erst später gesagt er braucht nur Stunden als Intervall. Das ändert aber 
nichts daran, dass die Berücksichtigung des Überlaufs keine 
ordentliche Lösung ist. Sondern wieder nur ein Gebastle für den einen 
Fall.

Egon D. schrieb:
>> Bist du nicht in der Lage [...]
>
> Würde es Dir etwas ausmachen, Dich nicht in Spekulationen
> über meine Fähigkeiten zu ergehen, sondern auf das zu
> antworten, was tatsächlich geschrieben wurde ?

Macht mir nichts aus. Du bist es der Bastlerlösungen verteitigt. Also 
bist du ein Bastler. Nirgend hast du gesagt, dass meine Lösung richtig 
ist und einfach ist. Nein, du hast sie immer wieder angegriffen. -> 
Bastler.

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> Ich schon.
> timer0_millis = m;
> timer0_overflow_count++;
>
> Das Ganze ist ein klassischer Zähler mit Festkommaarithmetik
> basierend auf DDS bzw. Bresenham.

timer0_overflow_count++; zählt die Überläufe, welche mit ca 980Hz 
auftreten.
timer0_millis hält einen korrigierten Wert, die ms

Beide zählen recht synchron hoch.
Daraus baust du keinen 64 Bit Zähler.

Muss ich dir den Beweis liefern?

OK, dann hier, auf einem UNO:
1
#include <Streaming.h>
2
#include <util/atomic.h>
3
#define CriticalSection ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
4
5
extern volatile unsigned long timer0_overflow_count;
6
extern volatile unsigned long timer0_millis;
7
8
void setup() 
9
{
10
  Serial.begin(9600);
11
  Serial << "Start: "<< __FILE__ << endl;
12
}
13
14
void loop() 
15
{
16
  unsigned long temp_timer0_overflow_count;
17
  unsigned long temp_timer0_millis;
18
19
  CriticalSection
20
  {
21
    temp_timer0_overflow_count = timer0_overflow_count;
22
    temp_timer0_millis         = timer0_millis;
23
  }
24
  Serial << "timer0_overflow_count: " << temp_timer0_overflow_count << endl;
25
  Serial << "temp_timer0_millis:    " << temp_timer0_millis         << endl;
26
  delay(1000);
27
}


-----

Ausgabe:
1
Start: E:\Programme\arduino\portable\sketchbook\sketch_dec21a\sketch_dec21a.ino
2
timer0_overflow_count: 16
3
temp_timer0_millis:    16
4
timer0_overflow_count: 1047
5
temp_timer0_millis:    1072
6
timer0_overflow_count: 2025
7
temp_timer0_millis:    2073
8
timer0_overflow_count: 3002
9
temp_timer0_millis:    3074
10
timer0_overflow_count: 3979
11
temp_timer0_millis:    4074
12
... usw ...

von Veit D. (devil-elec)


Lesenswert?

Falk B. schrieb:
> Veit D. schrieb:
>> Hier sehe ich keinen Vorteil gegenüber der millis Abfrage.
>
> Ich schon, und nicht nur was den Sleep Mode angeht.
> U.a., daß man nicht immer mit dem 32 Bit Zähler hantieren muss. Das
> bringt zwar die CPU nicht um, aber auch keine Vorteile.
> Die millis() Methode ist nur ein Arduinogewächs.
> Außerdem kann man mit dem Flag zur Echtzeit prüfen, ob das Timing noch
> stimmt, siehe Artikel Multitasking.

Ja gut, wenn es spezialisiert zu geht gibts immer andere Lösungen.

Der Nachteil von den Timer Flags ist, man benötigt für jedes Intervall 
ein zusätzliches Flag was man sich basteln muss. Mit millis ändert man 
einfach den Wert von "interval".

Ich sage mal so. Jedes Problem bekommt seine eigene Lösung. 
Funktionieren muss es. Mehr wird nicht verlangt.  :-)

von Nick M. (Gast)


Lesenswert?

S. R. schrieb:
> Ich finde es nur lustig, wie Nick sich aus den "der Zähler läuft alle 50
> Tage über" einen Anwendungsfall wie folgt konstruiert:

Ich finde es weitaus witziger, wie man sich auf so starrsinnige Art und 
Weise einer ordentlichen, weitsichtigen und allgemein gültigen und 
funktionerenden Methode entziehen kann.

Man überlegt sich das einmal richtig, macht es ab dann immer so und 
ist vor Überraschungen gefeit.

Die hier so vehement propagierte Methode ist -wie ihr sogar selbst schon 
festgestellt habt- gefährlich (selbst Microsoft ist schon drauf 
reingefallen).
Mir ist es absolut schleierhaft wie jemand der sich Softwareentwickler 
nennt sich so penetrant einer ordentlichen Lösung verweigert.

Für mich sind solche Leute einfach nur Bastler. Bastler und Pfuscher die 
ihr Zeug nur hinfummeln und froh sind wenns irgendwie läuft.
Liegt vielleicht auch an der Plattform.

von Einer K. (Gast)


Lesenswert?

Nick M. schrieb:
> Für mich sind solche Leute einfach nur Bastler. Bastler und Pfuscher die
> ihr Zeug nur hinfummeln und froh sind wenns irgendwie läuft.
> Liegt vielleicht auch an der Plattform.


Der erfolgreiche Bastler kommt mit 32 Bit aus, wenn die Intervalle unter 
49,x Tage bleiben.

Der geniale Profi nimmt immer 64 Bit. Egal ob das überflüssig ist. Das 
kümmert ihn nicht.

von Nick M. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Der erfolgreiche Bastler kommt mit 32 Bit aus, wenn die Intervalle unter
> 49,x Tage bleiben.

Bis er dann mal aus Versehen ein längeres Intervall braucht und sich 
dann wundert. Oder sich wundert, dass das immer wieder kommt.

Und dann kratzt er sich am Kopf, erinnert sich an diesen Thread und sagt 
dann: Aber die Experten haben doch alle gesagt, dass das richtig 
funktioniert wenn man nur den Überlauf richtig behandelt.

Würd er nur etwas weiter lesen, hätte er schon gemerkt, dass das nur die 
halbe Wahrheit ist. Denn er braucht noch ein zusätzliches Flag das er 
setzen und löschen muss um überhaupt ein Intervall richtig behandeln 
zu können. Wenn er das vergisst, kommt das nämlich spätestens nach 40 
Tagen wieder. Obwohl er doch alles richtig gemacht hat.
Ohne es zu wissen, hat er also ein wiederholendes Ereignis gesetzt.

Merkst du es vieleicht jetzt, was das für Hobby-Lösungen sind die ihr 
hier propagiert?

Es tut mir leid, aber ich muss mich jetzt aus dem thread ausklinken. Ich 
hab es oft genug und ausführlich erklärt.

Ich wünsch euch in eurer Bastler-Blase noch viel Erfolg.

von S. R. (svenska)


Lesenswert?

Nick M. schrieb:
> Man überlegt sich das einmal richtig, macht es ab
> dann immer so und ist vor Überraschungen gefeit.

Das klingt weniger nach ordentlichem Ingeneurswesen, sondern nach 
Zwangsstörungen.

Ich mein, du isst ja auch nicht jeden Tag exakt das gleiche, oder etwa 
doch? Oder gehörst du zu den Menschen, die jeden Tag pünktlich auf die 
Sekunde im Büro erscheinen und dieses pünktlich auf die Sekunde auch 
wieder verlassen?

Mir gefallen einigermaßen an das Problem angepasste Lösungen 
grundsätzlich besser als Universallösungen, denn letztere haben auch 
ihre Schwächen.

Zudem habe ich mal gelernt, dass man diese Schwächen nicht mehr 
wahrnimmt, wenn man angefangen hat, eine gute Lösung als "dat ham'wa 
imma schon so jemacht" anzusehen. Das wird vor allem dann relevant, wenn 
sich die Probleme ändern.

von Falk B. (falk)


Lesenswert?

Arduino Fanboy D. schrieb:
>> Das Ganze ist ein klassischer Zähler mit Festkommaarithmetik
>> basierend auf DDS bzw. Bresenham.
>
> timer0_overflow_count++; zählt die Überläufe, welche mit ca 980Hz
> auftreten.
> timer0_millis hält einen korrigierten Wert, die ms

Ja, mein Fehler, da wahr wohl etwas Wunschdenken im Spiel! 8-0
Ich hab die drei Variablen im Überflug als einen Zähler betrachet, es 
sind aber nur die ersten beiden verknüpft.

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> Ja, mein Fehler, da wahr wohl etwas Wunschdenken im Spiel! 8-0

Eine positive Bewertung von mir, wegen zugeben eines Irrtums.
Wäre eigentlich nicht erwähnenswert.
.. aber in diesem Forum ist einiges "anders" ...

von Gerhard O. (gerhard_)


Lesenswert?

Ich habe leider nur den immer längerer werdenden Thread überflogen und 
es möglich, daß mein Vorschlag Schnee von gestern ist. Ich würde es 
eigentlich anders planen. Zuerst einmal eine Rekapitulierung vom Arduino 
Default Setup:

Das Arduino Framework konfiguriert ja bekanntlich immer automatisch 
TIMER0 als Zeitbasis einschließlich der millis() Funktion. Dieser TIMER0 
wird durch die spezifische Vorteilereinstellung im 976Hz Zyklus 
getaktet. Millis() wird durch clevere Code-Maßnahmen so genau wie 
möglich an 1.000ms angenähert um bei Abfragen als globale Zeitbasis 
dienen zu können. Da TIMER0 auch für PWM Zwecke mitverwendet werden 
soll, mußte man auf CTC verzichten und den TIMER0 im 976Hz Rhythmus frei 
laufen lassen und ist deshalb so nicht direkt im 1.000ms Rhytmus für 
andere Zwecke brauchbar. Die millis() Methode hat jedoch viele Nachteile 
wesegen sie ich lieber nicht für solche Aufgaben verwenden würde.

Stattdessen könnte nun alternativ aber auf millis() ganz verzichten und 
mit TIMER2 eine genau im 1ms Zyklus arbeitende Zeitbasis im CTC Modus 
erstellen, die dann bei jeden CTC Interrupt eine FW Uhr im Linux 
Datetime Format mit exakter T2 1ms Genauigkeit unterhält. Dadurch wird 
die Langzeitgenauigkeit nur von der Quarzgenauigkeit des 16MHz 
Oszillators beeinflußt.

In diesem Interrupt macht man auch die Alarmtests und setzt dann globale 
Flags die dann im nächsten Loop durchlauf einfach abgearbeitet werden 
können und nach Erfüllung der Aufgabe wieder zurückgesetzt werden. Auf 
diese Weise hat man eine unabhängige Softwareuhr die zuverlässig im 
Hintergrund ihre Aufgabe erfüllt. Die durch die Flag und Loopdurchgänge 
auftretenden im us-ms Bereich verursachten Verzögerungen sind für seine 
Zwecke vernachlässigbare Jittereffekte die man bei Zeitauflösung im 
Sekundentakt meist ignorieren kann. Addierende Zeitfehler gibt es mit 
diesem Konzept nicht solange die T2 ISR kurz gehalten ist.

Ich würde es nun so machen:

Eine LONG DATETIME Variable die das LINUX UTC Zeitformat verwendet und 
vom T2
CTC Timer im Sekundentakt hochgezählt wird. Das wird wegen des 
unvermeidlichen Überlaufs bis 2037 funktionieren und das geht auch Linux 
BS so. Helfer-Routinen konvertieren diese Zahl in menschlich praktisches 
HHMMSS YYYYMMD Format. Alle Alarmzeitvergleiche werden nativ 
abgearbeitet um den Overhead der normalen Zeitformate zu vermeiden. Es 
ist ja viel einfacher mit Integerzahlen zu operieren. Umwandlung vom 
Linux Datetimefomat werden mit entsprechenden Umwandlungsroutinen 
gemacht die nur dann aufgerufen werden wenn es zur Eingabe oder Ausgabe 
notwendig ist. Da stört auch deren Komplexität nicht. 
Systemzeitberechnungen laufen alle einfach und schnell im Integerformat. 
Durch die Flagbefehle kann auch nichts vergessen werden.

Die ganze User Interface Seite kann nun nicht-kritisch in jeder 
denkbaren Weise verwirklicht werden. Die Alarmzeiten kann man dann im 
SRAM oder EEPROM speichern.

Man kann auch eine DS3231 RTC verwenden um periodisch die Systemzeit zu 
synchronisieren. Dadurch könnte auch ein Stromausfall keinen Schaden 
anrichten weil beim Wiederanlauf einfach die Linuxzeitvariable nach 
Daten vom RTC gesetzt wird.

Die NANO Klonen die mir zur Verfügung stehen haben typisch eine 
Laufgenauigkeit unter 1 Minute pro Tag. Deshalb ist eine RTC wie der 
DS3231 empfehlenswert.

Alle zeitaufwendigen Aufgaben die die Loopdurchgangszeit vergrößern 
könnten, mit State machines verwirklichen um die Loopdurchgänge kurz 
gestalten. Durch diese Architektur entkoppelt man zeitkritische Vorgänge 
und der uC kann seine Aufgaben im eigenen Zeitmasstab abarbeiten und 
wird nicht in eine Zwangsjacke gesteckt. Man muß so etwas natürlich 
planen. Am Ende hat man dann aber eine robuste Lösung die durch nichts 
gestört werden kann. Auch Stasis des Hauptloopdurchgangs bringt wegen 
der T2 ISR die Zeitrechnung nicht ausser Tritt. Nur ein uC Reset kann 
das.

Die Programmentscheidungen kann mit so einem Konzept statisch durch 
Arithmetik bestimmen und Fehler können bei korrekten Testausdrücken 
überhaupt nicht entstehen da die Zustandsmaschine nur von Arithmetik 
gesteuert wird und zeitmässig vollkommen entkoppelt ist.

Es hört sich kompliziert an, ist es aber nicht wirklich. Ich mache 
langsame Sachen meist so weil ich dem uC so viel Freizügigkeit erlauben 
möchte wie möglich und Optimierungen sind auf diese Weise fast nie 
notwendig.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

es wird sicherlich unendliche Lösungen für unendlich viele Aufgaben 
geben. Fakt ist jedoch, dass die millis Genauigkeit in 99% der Fälle für 
irgendwelche zyklischen Tasterabfragen und Led Blinkanzeigen völlig 
ausreichend ist. Dafür ist millis sehr einfach und praktikabel nutzbar. 
Wer höhere Genauigkeit benötigt greift sich einen freien Timer oder 
macht sich einen Timer frei. Wie gesagt, alles bekommt seine eigene 
Lösung.

Beitrag #6521338 wurde vom Autor gelöscht.
von Martin V. (oldmax)


Lesenswert?

Hi
Sorry, aber wenn doch "millis" wie eine Variable beeinflußbar ist, dann 
versteh ich das Problem nicht.
If millis > 1000 then
  Inc  Sek_Count
  Millis = millis-1000
end if

Ist jetzt nicht grad eine Sprache, soll ja nur das Prinzip aufzeigen. So 
wie nun Sekunden gezählt werden, können Minuten, Stunden und Tage, 
Wochen und Jahre folgen. Das Problem, das Millis nicht immer bei 1000 
erfaßt werden, erledigt sich, weil ja ein Überlauf wieder auf den 
Anfangswert drauf addiert wird. So dauert halt mal eine Sekunde 1060 
mSek und die nächste dann vielleicht nur 950. Im Mittel paßt das aber.
Wenn das irgendwo bereits geschrieben war, ok, ich hab nicht alles bis 
ins Detail gelesen.
Gruß oldmax

von Falk B. (falk)


Lesenswert?

Martin V. schrieb:
> Hi
> Sorry, aber wenn doch "millis" wie eine Variable beeinflußbar ist,

NEIN! millis() liefert den internen 32 Bit Zähler, welcher die Anzahl 
der Millisekunden seit dem Reset zählt. Der normale Arduinoanwender 
kennt den Namen der Variablen nicht, soll er auch nicht. Nur der 
"Fortgeschrittene"
kennt den und KÖNNTE die Variable auch schreiben. Das ist aber im 
Konzept des Arduino-Frameworks nicht vorgesehen. Es kann sein, daß das 
auch unerwünschte Nebenwirkungen auf andere Softwareteile hat, welche 
auch millis() verwenden.

> versteh ich das Problem nicht.

Das ist dein Problem ;-)

von Martin V. (oldmax)


Lesenswert?

Hi
Falk B. schrieb:
> Das ist dein Problem ;-)

Nein, ich verwende den Arduino nicht und auch nicht "C". Aber trotzdem 
danke für die Aufklärung.
Wenn das so ist, das ich eine Funktion oder was auch immer habe, die mir 
millis liefert, mit denen ich nix anfangen kann, sorry, wozu brauch ich 
das dann? Um erst ein kompliziertes Konzept um diese millis-Funktion zu 
schreiben? Wahnsinn...   Respekt.
Gruß oldmax

von Einer K. (Gast)


Lesenswert?

Martin V. schrieb:
> Wenn das so ist, das ich eine Funktion oder was auch immer habe, die mir
> millis liefert, mit denen ich nix anfangen kann,
Wer sagt das?
millis() erfüllt seinen Job.
Wo ist dein Problem damit?
Mache es konkret!

von Stefan F. (Gast)


Lesenswert?

Martin V. schrieb:
> Wenn das so ist, das ich eine Funktion oder was auch immer habe, die mir
> millis liefert, mit denen ich nix anfangen kann, sorry, wozu brauch ich
> das dann?

Keiner zwingt dich dazu, das Arduino Framework zu benutzen. Wenn du 
damit nichts anfangen kannst, dann lass es halt bleiben. Alles andere 
wäre dumm.

von Sebastian (Gast)


Lesenswert?

Also wenn man immer genau 1 Sek haben will geht das mit Millis nicht 
weil

starttime=0

if (millis()-starttime >=1000)  keiner weiß ob diese Überprüfung bei 
genau 1000ms geschieht
sek++;
starttime=millis(); startwert wird wieder auf null gesetzt deswegen kann 
man so die Gesamtzeit nicht erfassen hier mußte dann vorher

oldstarttime= starttime am anfang stehen so das man später darauf 
zurückgreifen kann und die wirkliche Zeit korrigieren kann

von Falk B. (falk)


Lesenswert?

Martin V. schrieb:
> Nein, ich verwende den Arduino nicht und auch nicht "C". Aber trotzdem
> danke für die Aufklärung.
> Wenn das so ist, das ich eine Funktion oder was auch immer habe, die mir
> millis liefert, mit denen ich nix anfangen kann, sorry, wozu brauch ich
> das dann?

Bist du so dumm oder tust du nur so?

> Um erst ein kompliziertes Konzept um diese millis-Funktion zu
> schreiben?

Schon wieder nix geblickt? Die Funktion millis() gibt es schon, die muss 
keiner programmieren.

> Wahnsinn...   Respekt.
> Gruß oldmax

Jaja, geh mit deinem BASCOM spielen und sei glücklich.

Denn seelig sind, die da geistig arm sind, denn ihrer ist das 
Himmelreich. AMEN!

von Martin V. (oldmax)


Lesenswert?

Hi

Falk B. schrieb:

> Jaja, geh mit deinem BASCOM spielen und sei glücklich.

In keinem meiner Beiträge habe ich jemals erwähnt, das ich mit BASCOM 
programmiere. Diese Sprache hat aber durchaus eine Daseinsberechtigung. 
Ich nutze sie nur nicht.

Falk B. schrieb:
> Schon wieder nix geblickt? Die Funktion millis() gibt es schon, die muss
> keiner programmieren.

Niemand will eine vorhandene Funktion schreiben, aber wenn ich ein 
umfangreiches Programmkonstrukt erstellen muß, um aus dieser Funktion 
ein erwünschtes Zeitraster zu erhalten, taugt diese Funktion dafür 
nicht, oder der Ansatz ist falsch.
Mag sein, das ich von "C" keine Ahnung hab, aber wenn ich es mal 
(wieder) mit einer Pseudo-Sprache (kein BASCOM) verdeutlichen darf:
Liefert mir die Funktion einen Zählerstand, so ist doch die Differenz 
leicht auszuwerten.
Anfangs setze ich einen Zaehler_Alt auf das Ergebnis der Funktion 
Millis. In meiner Programmschleife frage ich dann wie folgt
If Zaehler_Alt+1000<Millis then
   Inc Zeitraster  (Sekunden)
   Zaehler_Alt= Zaehler_Alt-1000
end if
Die Auswertung bei Überlauf von Millis und weitere Zeitraster wie 
Minuten, Stunden etc. überlasse ich euch gern.
Ich glaub, eine gleichwertige "C" Programmierung wird nicht größer sein 
und bekommt ihr auch aus diesem Vorschlag gebastelt.

Falk B. schrieb:
> Bist du so dumm oder tust du nur so?

Nun, deine unhöfliche Entgleisung schreibe ich mal der Belastung durch 
die Pandemie zu. Ich denke schon, das du als Experte einen solchen 
Tonfall nicht standartmäßig draufhast, aber wenn ich mich irre und du 
auch nur einer der zahlreichen Marktschreier bist, dann tu ich dir den 
Gefallen und fühle ich mich halt mal ein wenig beleidigt.
Gruß oldmax

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Sebastian schrieb:
> starttime=millis(); startwert wird wieder auf null gesetzt deswegen kann
> man so die Gesamtzeit nicht erfassen hier mußte dann vorher

Man muss nicht unbedingt den nicht funktionierenden Weg gehen.

Wenn man die Gesamtzeit richtig erfassen möchte, muss man den neuen Wert 
für starttime genau um 1000 gegenüber dem alten erhöhen und nicht noch 
die (unbekannte) Abfrageverzögerung als Fehler einbauen.

von Wolfgang (Gast)


Lesenswert?

Martin V. schrieb:
> If Zaehler_Alt+1000<Millis then

Das funktioniert so nicht, wenn bei Zaehler_Alt+1000 ein Überlauf 
auftritt. Die richtige Lösung wurde oben schon lange gepostet.

von Einer K. (Gast)


Lesenswert?

Martin V. schrieb:
> Die Auswertung bei Überlauf von Millis .....  überlasse ich euch gern.
Danke, für nichts.
Das schöne ist, solange man keine Intervalle größer 49,x Tage braucht, 
muss man nix "Überlauf auswerten".

Es sei denn, man machst es so wie du:
> If Zaehler_Alt+1000<Millis then
1. Dann fällt man halt mal auf die Fresse, beim Überlauf.
2. Oder man backt ihm eine (unnötige) Extrawurst.
Das darf jeder gerne für sich selber entscheiden.
Sind aber beide recht falsch.

von Martin V. (oldmax)


Lesenswert?

Hi
Arduino Fanboy D. schrieb:
> 1. Dann fällt man halt mal auf die Fresse, beim Überlauf.
Noch so ein Corona- Geschädigter. Davor aber noch darauf hinweisen, das 
ich sehrwohl mit einem Überlauf rechne. G'scheiter gehts wirklich nicht.
Leute, laßt mal die Kirche im Dorf. Wenn ich solch eine Aufgabe lösen 
soll, mach ich das garantiert nicht in "C" und auch nicht auf einem 
Arduino. Spielt doch mit was ihr wollt, aber lernt auch  mal etwas 
Nettikette. Soll manchmal auch im realen Leben etwas hilfreich sein.

Gruß oldmax

von Einer K. (Gast)


Lesenswert?

Martin V. schrieb:
> Davor aber noch darauf hinweisen, das
> ich sehrwohl mit einem Überlauf rechne.
Das sieht man aber in deinem Beispiel nicht.
Denn das fällt genau in den Topf.

Und von daher ist es ein ganz schlechtes Beispiel, wenn man millis() 
verteufeln will.

Der hinkende Vergleich:
Es ist eher nicht die Schuld des Brotmessers, oder des Herstellers des 
Brotmessers, wenn du dich zu dumm anstellst, und dich damit in die 
Fingerchen schneidest.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.