Forum: Mikrocontroller und Digitale Elektronik mehrere Millis()


von Markus O. (markusjo)


Lesenswert?

Guten Abend,

momentan verzweifle ich daran mehrer Millis(), nacheinander, zu 
verwenden.
Was ich im Internet dazu gefunden habe war leider nicht hilfreich. Jeder 
Timer hat seinen eigenen Namen und die laufen sogar getrennt 
voneinander. Was mache ich noch falsch? Wenn ich nur einen von beiden 
verwende funktioniert es auch.

Fehlerbild ist folgendes.
Der Zweite Aktor, zum Test eine LED, blinkt nur einmal kurz auf und 
bleibt nicht für die angegebene Zeit an.
Dazu kommt noch dass die Pausenzeit zwischen den beiden nicht 
eingehalten wird, sondern es folgt nach der ersten LED direkt die 
zweite.

Der normale Ablauf sollte wie folgt sein:

1. LED 1 an:
2. TimerAct 500ms // Dauer des HIGHs
3. LED 1 aus
4. TimerSpace // Dauer der Pause zwischen den beiden LED´s
5. LED 2 an
6. TimerAct 500ms // Dauer des HIGHs
7. LED 2 aus
8. TimerSpace // Pause bis zum nächsten Durchlauf

Gruß
1
const byte Actuator_1 = 2;
2
const byte Actuator_2 = 3;
3
4
boolean Act1State;
5
boolean Act2State;
6
7
byte StepCounter = 1;
8
9
void setup() {
10
  Serial.begin(9600);
11
  pinMode(Actuator_1, OUTPUT);
12
  pinMode(Actuator_2, OUTPUT);
13
}
14
15
void loop()
16
{
17
  if(StepCounter == 9)StepCounter = 1;
18
  
19
Steps();
20
21
}
22
void Steps(){
23
switch (StepCounter){
24
25
  case 1: Act1_on();break;
26
  case 2: TimerAct();break;
27
  case 3: Act1_off();break;
28
  case 4: TimerSpace();break;
29
  case 5: Act2_on();break;
30
  case 6: TimerAct();break;
31
  case 7: Act2_off();break;
32
  case 8: TimerSpace();break;
33
  }
34
}
35
  
36
  void Act1_on()  {Act1State = digitalRead(Actuator_1); if(Act1State == LOW){digitalWrite(Actuator_1, HIGH);StepCounter++;}}
37
  void Act1_off() {Act1State = digitalRead(Actuator_1); if(Act1State == HIGH){digitalWrite(Actuator_1, LOW);StepCounter++;}}
38
  void Act2_on()  {Act2State = digitalRead(Actuator_2); if(Act2State == LOW){digitalWrite(Actuator_2, HIGH);StepCounter++;}}
39
  void Act2_off() {Act2State = digitalRead(Actuator_2); if(Act2State == HIGH){digitalWrite(Actuator_2, LOW);StepCounter++;}}
40
41
  boolean TimerAct()
42
  { 
43
    unsigned long currentMillis;
44
    static unsigned long previousMillis;
45
    const int timeout = 500;
46
    
47
    currentMillis = millis();
48
    if (currentMillis - previousMillis >= timeout)
49
    {
50
    previousMillis = currentMillis;
51
    StepCounter++;
52
    }
53
  }
54
55
    boolean TimerSpace()
56
  { 
57
    unsigned long currentMillis2;
58
    static unsigned long previousMillis2;
59
    const int timeout2 = 1000;
60
    
61
    currentMillis2 = millis();
62
    if (currentMillis2 - previousMillis2 >= timeout2)
63
    {
64
    previousMillis2 = currentMillis2;
65
    StepCounter++;
66
    }
67
  }

: Bearbeitet durch User
von LostInMusic (Gast)


Lesenswert?

Du denkst viel zu kompliziert.

Ich würde einfach die drei vorkommenden Dauern definieren...
1
DAUER_LED_1 = 500
2
DAUER_SPACE = 1000
3
DAUER_LED_2 = 500

...und daraus die fünf Schaltzeitpunkte berechnen, an denen etwas 
passiert...
1
T0 = 0
2
T1 = T0 + DAUER_LED_1
3
T2 = T1 + DAUER_SPACE
4
T3 = T2 + DAUER_LED_2
5
T4 = T3 + DAUER_SPACE

...und die dann mit folgendem (Pseudocode-)Programm klarmachen, in dem 
jede Millisekunde eine (weil eine reicht!) Timervariable "t" 
hochgezählt wird:
1
t = 0;
2
3
Loop:
4
5
IF {t==T0} Schalte_LED_1_ein;
6
IF {t==T1} Schalte_LED_1_aus;
7
IF {t==T2} Schalte_LED_2_ein;
8
IF {t==T3} Schalte_LED_2_aus;
9
IF {t==T4} t = -1    // t-Timer inaktivieren
10
11
IF {t>=0} inc(t);    // t-Timer aktiv --> t inkrementieren
12
13
Warte_eine_Millisekunde;
14
15
Goto Loop;

Da kannst Du dann noch eine Taste hinzufügen zum Starten eines neuen 
Ablaufs:
1
Taste_jetzt = (vom Pin einlesen)
2
IF {Taste_vorher = oben} und {Taste_jetzt = unten} (t = 0)
3
Taste_vorher = Taste_jetzt

Vielleicht hilft es Dir ja beim Verständnis, wie man so etwas macht.

von LostInMusic (Gast)


Lesenswert?

>Was mache ich noch falsch?

Ach so, Nachtrag: Ich werde Dein Programm nicht analysieren.

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Guten Abend,
>
> momentan verzweifle ich daran mehrer Millis(), nacheinander, zu
> verwenden.

Tja, du bist ja auch ein Experte für chaotisches Programmieren.

> Was ich im Internet dazu gefunden habe war leider nicht hilfreich.

Im Internet gibt es nur Informationen. Wissen ist was anderes.

> voneinander. Was mache ich noch falsch?

Vieles.

1.) Du machst Chaos^3
2.) Die Bearbeitung der Schrittvariable ist über gefühlt 1 Millionen 
Stellen verstreut. Das MUSS schief gehen.
3.) Ein Statemachine ruft man meistens in einem festen Zeitraster 
auf, welches AUßERHALB erzeugt wird.
4.) Die schlechte Formatierung des Quelltextes bringt noch mehr Chaos.

Etwa so.
1
#define ON 1
2
#define OFF 0
3
4
#define PIN_ACTUATOR1 = 2;
5
#define PIN_ACTUATOR2 = 3;
6
7
#define FSM_PERIOD 100   //ms
8
9
long time_old;
10
11
void setup() {
12
13
  Serial.begin(9600);
14
  pinMode(PIN_ACTUATOR1, OUTPUT);
15
  pinMode(PIN_ACTUATOR2, OUTPUT);
16
  time_old = millis();
17
}
18
19
void loop() {
20
21
  // CPU- und multitaskingfreundliches delay()
22
  if ( (millis() - time_old) >= FSM_PERIOD) {
23
    time_old += FSM_PERIOD;
24
    Steps();
25
  }
26
}
27
28
void Act(uint8_t no, uint8_t state) {
29
  if (no == 1) {
30
    digitalWrite(PIN_ACTUATOR1, state);
31
  }
32
  
33
  if (no == 2) {
34
    digitalWrite(PIN_ACTUATOR2, state);
35
  }
36
}
37
38
void Steps() {
39
  static uint8_t state=0;
40
  static uint8_t timer;
41
42
  switch (state) {
43
    case 0: Act(1, ON); timer = 50; state = 1; break;
44
    case 1:
45
      timer--;
46
      if (timer == 0) state = 2;
47
      break;
48
    case 2: Act(1, OFF); timer = 50; state = 3; break;
49
    case 3: 
50
      timer--;
51
      if (timer == 0) state = 4;
52
      break;
53
    case 4: Act(2, ON);  timer = 50; state = 5; break;
54
    case 5:
55
      timer--;
56
      if (timer == 0) state = 6;
57
      break;
58
    case 6: Act(2, OFF); timer = 50; state = 7; break;
59
    case 7:
60
      timer--;
61
      if (timer == 0) state = 0;
62
      break;
63
  }
64
}

von Schlafloser (Gast)


Lesenswert?

Kurz zu millis(),
Die Funktion gibt die Zeit seit dem letzten Einschalten wieder... So wie 
du es geschrieben hast erwartest du das jeder Aufruf diesen neu 
startet.. Dem ist nicht so..
Aus deinem Code:

currentMillis = millis();
    if (currentMillis - previousMillis >= timeout)
    {
 previousMillis    = currentMillis;
    StepCounter++;
    }
  }

Da perviousMillis nicht beschrieben wird bevor es in der If Abfrage 
angefragt wird, wird es ziemlich sicher 0 sein. 0 ist kleiner als die 
bis dahin Zeit die die millis() zurückgibt..
(was eigentlich mindestens eine Compiler Warnung sein sollte, oder macht 
Arduino da was anderes?  Könnt mich gerne korrigieren)..

In diesem Fall wäre es möglich die previousMillis zum Zeitpunkt der 
Transition (schrittwechsel) zu schreiben. Dannach eben TimerAct oder 
Timer Space Aufrufen bis die Zeit verstrichen ist.

Bin aber mei lostMusic, eine Funktion mit Parametern tuts auch und ist 
schlanker.

Grüße von der Nachtwache

von dummschwaetzer (Gast)


Lesenswert?

kurz gesagt: static unsigned long previousMillis ist jedes mal falsch 
initialisiert.
beim ersten mal(case2) ist es 0, sollte es aber nicht.
beim zweiten mal(case6) ist es in etwa der Ausschaltmoment Act1_off 
sollte es aber nicht.

quick and dirty:
previousMillis global,
case 1: Act1_on();previousMillis=millis();break;
case 5: Act2_on();previousMillis=millis();break;

für TimerSpace() ähnlich

von Johannes S. (Gast)


Lesenswert?

previousMillis2 ist beim ersten Aufruf 0, millis() ist aber schon 500 + 
Laufzeit der ersten beiden states.

> previousMillis ist jedes mal falsch initialisiert.
Nein, ist ja static und wird nur vor main() auf 0 gesetzt.

Die Timerfunktionen sind als bool deklariert, geben aber nix zurück. Das 
gibt eine Warnung, die aber schlauerweise bei Arduino per default aus 
sind.

von dummschwaetzer (Gast)


Lesenswert?

Johannes S. schrieb:
> Nein,

doch, OH!!!
wann wurde previousMillis das letzte mal mit welchem Wert beschrieben? 
Welchern Wert müsste es enthalten um die 500 ms zu Erreichen? ...

von Markus O. (markusjo)


Lesenswert?

Danke.

Dachte ich hätte das jetzt schon relativ sauber gemacht. Kann das aber 
mit den StepCountern, die überall versteut sind, nachvollziehen.

Dein Vorschlag gefällt mir ganz gut und sieht schlank und logisch, wenn 
auch ungewohnt, aus.

Wieso hast du die Timerzeit einmal oben definiert und dann hast du je 
Step noch einmal 50ms angegeben. Das kann ich nicht nochvollziehen.

#define soll ja nicht so gut sein.
Du verwendest es offensichtlich. Was sagst du also zu dieser Thematik 
und ob es nicht besser durch const ersetzt werden sollte...?

Den Space-Timer würde ich gerne flexible durch einen encoder einstellen. 
Sollte kein Problem sein das hier einzubinden, oder?

MfG

von Johannes S. (Gast)


Lesenswert?

PreviousMillis startet mit 0, millis -  previous  ist millis und wird 
immer größer bis es >timeout wird. Absolut ok.

Beitrag #6847196 wurde von einem Moderator gelöscht.
von dummschwaetzer (Gast)


Lesenswert?

>PreviousMillis startet mit 0, millis -  previous  ist millis und wird
>immer größer bis es >timeout wird. Absolut ok.
beim ersten Aufruf.
Dann kommen Weitere Aufrufe im case 2, bis 500ms nach Start rum sind.

beim ersten Aufruf im case 6:
previousMillis ist dann wahrscheinlich 500 (wenn die Inititalisierung in 
< 500ms durch ist)
millis ist dann aber schon >= 1000 (von der TimerSpace())
currentMillis - previousMillis ist dann also im ersten Aufruf schon >= 
timeout, damit wird sein case 6 genau einmal ausgeführt, und nicht wie 
beabsichtigt 500ms lang.

von EAF (Gast)


Lesenswert?

Markus O. schrieb:
> #define soll ja nicht so gut sein.
> Du verwendest es offensichtlich. Was sagst du also zu dieser Thematik
> und ob es nicht besser durch const ersetzt werden sollte...?

#define tuts schon...
Ist aber alter C Stil, und bar jeder Typisierung, nur eine 
Textersetzung.
In modernem C++ kann man meist constexpr an solchen Stellen verwenden.

> #define PIN_ACTUATOR1 = 2;
das ist sowieso unfug.

gemeint ist sicherlich:
#define PIN_ACTUATOR1  2

Wobei auch
#define PIN_ACTUATOR1  2.34567
genau so gut funktionieren würde, auch wenn es unsinnig aussieht

constexpr byte PIN_ACTUATOR1  {2};// modern/Typesicher
constexpr byte PIN_ACTUATOR1  {2.34567}; // wirft Error

Beitrag #6847228 wurde von einem Moderator gelöscht.
von Oliver S. (phetty)


Lesenswert?

Ich empfehle dir die Library Ticker.
https://github.com/sstaub/Ticker

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


Lesenswert?

Markus O. schrieb:
> Der normale Ablauf sollte wie folgt sein:
> 1. LED 1 an:
> 2. TimerAct 500ms // Dauer des HIGHs
> 3. LED 1 aus
> 4. TimerSpace // Dauer der Pause zwischen den beiden LED´s
> 5. LED 2 an
> 6. TimerAct 500ms // Dauer des HIGHs
> 7. LED 2 aus
> 8. TimerSpace // Pause bis zum nächsten Durchlauf
Man könnte also sagen:
1. erzeuge ein Zeitmuster mit einem 500ms-Puls und einer einstellbaren 
Pause
2. schalte währen des Pulses eine LED ein
3. wechsle bei jedem Durchlauf zwischen 2 LEDs

Dann kommt etwa sowas raus:
1
:
2
unsigned long aktzeit, startzeit=0, Pausendauer=1000;
3
int Pulse=1, Led=0;
4
5
mainloop:
6
   :
7
   :
8
   aktzeit = millis();
9
   if (Pulse==1) {
10
      if (aktzeit-startzeit >= 500) {
11
         startzeit = aktzeit;
12
         Pulse = 0;
13
         Led++;
14
      }
15
   }
16
   else { // Pulse == 0 
17
      if (aktzeit-startzeit >= Pausendauer) {
18
         startzeit = aktzeit;
19
         Pulse = 1;
20
      }
21
   }
22
23
   if (Pulse == 1) {
24
      if (Led&1) Schalte_LED1(EIN);
25
      else       Schalte_LED2(EIN); 
26
   }
27
   else {
28
      Schalte_LED1(AUS);
29
      Schalte_LED2(AUS); 
30
   }
31
   :
32
   :

EAF schrieb:
> #define tuts schon...
> Ist aber alter C Stil, und bar jeder Typisierung, nur eine Textersetzung.
> constexpr byte PIN_ACTUATOR1  {2};// modern/Typesicher
Lustigerweise ist es so, dass, wenn jemand diese Problematik kennt, er 
dann auch mit dem #define keinen Unfug macht. Und wenn einer die 
Problematik nicht kennt, dann schafft er es garantiert, auch mit dem 
modernen und typsicheren Stil Unfug zu treiben.

: Bearbeitet durch Moderator
von Johannes S. (Gast)


Lesenswert?

das ist doch ein simples logisches Problem, dafür muss man weder die 
Programmiersprache wechseln noch gleich irgendwelche Libs installieren.

Der SW Timer muss in einem Schritt initialisiert werden, und es gibt 
eine zyklische Prüfung. Dann reicht hier auch ein SW Timer.
1
  unsigned long delayTime;
2
  unsigned long previousMillis;
3
4
  void setTimer(unsigned long t)
5
  { 
6
    previousMillis = millis();
7
    delayTime = t;
8
  }
9
10
  bool isTimerRunning()
11
  {
12
    if (delayTime == 0) {
13
      return false;
14
    }
15
  
16
    unsigned long currentMillis = millis();
17
    if (currentMillis - previousMillis >= delayTime)
18
    {
19
      // delay expired
20
      delayTime = 0;
21
      return false;
22
    } else {
23
       previousMillis = currentMillis;
24
       return true;
25
    }
26
  }

in den Schritten kann dann mit setTimer(500); das delay aktiviert 
werden,
in Step oder in der loop wird isTimerRunning() aufgerufen und der Step 
nur ausgeführt wenn das eine 0 liefert.

von W.S. (Gast)


Lesenswert?

Markus O. schrieb:
> Was mache ich noch falsch?

Nachdem dir einige Leute erklärt haben, was du denn so alles mit dem 
Zeugs, was du bei Arduino und artverwandtem Krempel so vorfindest, 
falsch machen kannst und wie du dich trotzdem da durchschlängeln kannst, 
sollte dir zumindest klar sein, wie du zu deinen richtig blinkenden LED 
kommen kannst.

Aber:
Du machst einen generellen Fehler, wenn du Zeiten im Millisekunden- oder 
gar im Sekundenbereich mittels Trampelschleifen erzeugen willst. Bedenke 
mal, daß dein µC währenddessen Abertausende von Operationen tun 
könnte, wenn du ihn nur lassen würdest und ihn nicht in einer stupiden 
Trampelschleife versauern lassen würdest.

Du solltest deine Programmier-Prämissen erweitern und für diverse Dinge 
(die sehr viel länger dauern als bloß ein paar Mikrosekunden) die 
ereignisorientierte Programmierung für dich entdecken. Das ist nix 
Besonderes, es stülpt lediglich ein paar while's und if's um:
nicht
 while (KonditionNochNichtErreicht) TueNix;
 TueDasAngedachte;
 TueAllesÜbrige;
sondern
 if(KonditionErreicht) TueDasAngedachte;
 TueAllesÜbrige;

Schlußendlich wirst du irgendwann mal dazu kommen, dir eine Systemuhr in 
deiner Firmware einzurichten, mit der du die Millisekunden (und darauf 
fußend auch Sekunden, Stunden etc.) abhandeln kannst. Ein Gleiches gilt 
dann auch für das Einrichten einer Ereignis-Warteschlange (was ein 
simpler Fifo ist) und das Begreifen der Funktionalität von 
DelayedEvent's, was lediglich eine Unterfunktion deiner Systemuhr ist, 
was dazu führt, daß die Systemuhr zu festgelegten Zeiten Events in die 
Ereignis-Warteschlange wirft. Auf die kannst du dann mit deiner Firmware 
reagieren und in der Zwischenzeit etwas anderes erledigen.

Also: bleibe nicht stur bei der blockierenden Geradeaus-Programmierung, 
wo diese störend sich auswirkt.

W.S.

von Johannes S. (Gast)


Lesenswert?

W.S. schrieb:
> Also: bleibe nicht stur bei der blockierenden Geradeaus-Programmierung,
> wo diese störend sich auswirkt.

Anstatt soviel zu schreiben hättest du wenigstens einmal versuchen 
sollen den Code zu verstehen.
Der TO hat das nämlich längst kapiert und eine nicht blockierende loop 
gebaut und auch das delay versauert nicht in einer Funktion. Der Fehler 
war lediglich die fehlende Initialisierung.

von bitte. (Gast)


Lesenswert?

zusätzlich definierst du eine Boolean Funktion ohne return. Besser als 
Viod deklarieren.
1
boolean TimerSpace(){
2
3
  unsigned long currentMillis2;
4
  static unsigned long previousMillis2;
5
  const int timeout2 = 1000;
6
7
  currentMillis2 = millis();
8
  if (currentMillis2 - previousMillis2 >= timeout2){
9
    previousMillis2 = currentMillis2;
10
    StepCounter++;
11
  }
12
}

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
> switch (state) {
>     case 0: Act(1, ON); timer = 50; state = 1; break;

50ms zum nächsten Step + die 100ms des delays?
Also immer die 100ms des delays und dann immer noch pro angegebenen 
Step?
Verstehe ich das richtig?

Falk B. schrieb:
> // CPU- und multitaskingfreundliches delay()
>   if ( (millis() - time_old) >= FSM_PERIOD) {
>     time_old += FSM_PERIOD;
>     Steps();

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Falk B. schrieb:
>> switch (state) {
>>     case 0: Act(1, ON); timer = 50; state = 1; break;
>
> 50ms zum nächsten Step + die 100ms des delays?
> Also immer die 100ms des delays und dann immer noch pro angegebenen
> Step?
> Verstehe ich das richtig?

Nein. Die Funktion Steps() wird alle 100ms aufgerufen und bearbeitet 
immer nur den aktuellen case in der switch() Anweisung. Wenn dort ein 
Timer 50 mal runter gezählt werden muss, um 0 zu erreichen, dauert das 
100ms * 50 = 5000ms.
Siehe Statemachine.

von EAF (Gast)


Lesenswert?

Lothar M. schrieb:
> Und wenn einer die
> Problematik nicht kennt, dann schafft er es garantiert, auch mit dem
> modernen und typsicheren Stil Unfug zu treiben.
Immerhin hat der Compiler so mehr Möglichkeiten/Chancen erschrocken mit 
Warnungen und Errors um sich zu werfen. Auch: Er zeigt dann meist 
genauer auf die Zeile.
Wenn man da die Wahl hat.... zwischen define und const.... dann hat man 
halt die Wahl.
Das war die Frage: "Was ist besser?" const oder define
Da ist constexpr die dritte Variante, die vielleicht noch bessere. 
Benötigt sie doch kein RAM, wie auch define. Bei const ist das nicht 
gewährleistet.

von LostInMusic (Gast)


Lesenswert?

>dauert das 100ms * 50 = 5000ms.

Das ist gut, falls der TO mal irgendwas mit 5000 ms machen will. Bisher 
sind es ja nur 500 ms und 1000 ms.

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
> Markus O. schrieb:
>> Falk B. schrieb:
>>> switch (state) {
>>>     case 0: Act(1, ON); timer = 50; state = 1; break;
>>
>> 50ms zum nächsten Step + die 100ms des delays?
>> Also immer die 100ms des delays und dann immer noch pro angegebenen
>> Step?
>> Verstehe ich das richtig?
>
> Nein. Die Funktion Steps() wird alle 100ms aufgerufen und bearbeitet
> immer nur den aktuellen case in der switch() Anweisung. Wenn dort ein
> Timer 50 mal runter gezählt werden muss, um 0 zu erreichen, dauert das
> 100ms * 50 = 5000ms.
> Siehe Statemachine.

Ah, ok.

Ich hab gerade mal ein wenig mit den Zeiten rumprobiert.
Wenn ich die periodische Wiederholung des FSM auf 10ms stelle, dann 
passt was mit den Zeiten nicht.

Ich hatte in den Cases dann jeweils 10ms, für die Dauer der LED 
angegeben und 500 für die Pause.
Also 100ms LED und 5000ms Pause.
Da war die Pause nur ca 3000ms lang.

Jetzt wo ich die periodische Wiederholung wider auf 100ms und die pause 
auf 50ms sowie die LED auf 1ms gestellt habe, scheint es zu passen.
Es schein also irgendwie zeitkritisch zu sein

Nun die Frage: Sind 100ms ein grober Richtwert? Wann sollte dieser 
erhöht oder verringert werden.

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


Lesenswert?

Markus O. schrieb:
> Es schein also irgendwie zeitkritisch zu sein
Der Schein trügt...

> Nun die Frage: Sind 100ms ein grober Richtwert? Wann sollte dieser
> erhöht oder verringert werden.
Wenn du z.B. 450ms brauchst, dann sind 100ms-Ticks völlig ungeeignet. 
Ticks mit 90ms wären dann das bessere Zeitraster. Oder 150ms. Oder auch 
50ms oder sogar 10ms, dann ist es einfacher zu rechnen.

: Bearbeitet durch Moderator
von Markus O. (markusjo)


Lesenswert?

Lothar M. schrieb:
> Der Schein trügt...

Wie kommts dann dazu, zu den 3000ms?

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


Lesenswert?

Markus O. schrieb:
> Wie kommts dann dazu, zu den 3000ms?
Wie üblich: Programmierfehler.
Mangels zugehörigem Sourcecode kann man nichts genaueres sagen.

Alles im ms Bereich ist für einen µC nicht "zeitkritisch", denn in 
dieser 1ms kann er locker 10000 Rechenschritte (oder mehr) ausführen. 
Wenn jemand es schafft, diese vielen Rechenschritte zu verplempern, dann 
ist da nichts "zeitkritisch", sondern schlicht falsch programmiert.

: Bearbeitet durch Moderator
von Markus O. (markusjo)


Lesenswert?

Lothar M. schrieb:
> Wie üblich: Programmierfehler.
> Mangels passendem Sourcecode kann man nichts genaueres sagen.
1
boolean ON = 1;
2
boolean OFF = 0;
3
const int PIN_ACTUATOR1 = 2;
4
const int PIN_ACTUATOR2 = 3;
5
unsigned int FSM_PERIOD = 10;   //ms
6
long time_old;
7
8
void setup() {
9
10
  Serial.begin(9600);
11
  pinMode(PIN_ACTUATOR1, OUTPUT);
12
  pinMode(PIN_ACTUATOR2, OUTPUT);
13
  time_old = millis();
14
15
}
16
17
void loop() {
18
19
  // CPU- und multitaskingfreundliches delay()
20
21
  if ( (millis() - time_old) >= FSM_PERIOD)
22
  {
23
    time_old += FSM_PERIOD;
24
    Steps();
25
  }
26
}
27
28
void Act(byte no, boolean state) {
29
30
  if (no == 1)
31
  {
32
    digitalWrite(PIN_ACTUATOR1, state);
33
  }
34
35
  if (no == 2)
36
  {
37
    digitalWrite(PIN_ACTUATOR2, state);
38
  }
39
}
40
41
void Steps() {
42
43
  static byte state = 0;
44
  static byte timer;
45
46
  switch (state) {
47
48
    case 0: Act(1, ON); timer = 10; state = 1; break;
49
    
50
    case 1: timer--; if (timer == 0) state = 2;break;
51
    
52
    case 2: Act(1, OFF); timer = 500; state = 3; break;
53
    
54
    case 3: timer--; if (timer == 0) state = 4; break;
55
    
56
    case 4: Act(2, ON);  timer = 10; state = 5; break;
57
    
58
    case 5: timer--; if (timer == 0) state = 6; break;
59
    
60
    case 6: Act(2, OFF); timer = 500; state = 7; break;
61
    
62
    case 7: timer--; if (timer == 0) state = 0;break;
63
64
  }
65
}

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


Lesenswert?

Markus O. schrieb:
> static byte timer;
> timer = 500;
Da ist der Programmierfehler. Wie groß kann so ein "byte" maximal 
werden?

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> static byte timer;
> timer = 500

Siehst du den Fehler jetzt?

von Markus O. (markusjo)


Lesenswert?

oh verdammt. 255.

Danke

von EAF (Gast)


Lesenswert?

Falk B. schrieb:
> // CPU- und multitaskingfreundliches delay()
>   if ( (millis() - time_old) >= FSM_PERIOD) {
>     time_old += FSM_PERIOD;
>     Steps();
>   }

Es ist nicht nötig da ein weiteres Zeitraster einzuführen.
Denn es bringt nur minimale Ersparnis, erhöht aber die Komplexität.

Was aber schon sinnvoll ist, ist die Zeitabhandlung herauszukapseln. Das 
macht sie widerverwendbar, man kann sie in eine Lib auslagern. Das 
verringert die Schreibarbeit und damit auch die Chance auf 
Flüchtigkeitsfehlern.
1
class SimpleTimer
2
{
3
  private:
4
    uint32_t timeStamp;      // Zeitmerker
5
    bool reached;            // default Status: timer abgelaufen
6
7
  public:
8
    SimpleTimer(): timeStamp(0), reached(true) {}
9
10
    void start()
11
    {
12
      timeStamp   = millis();
13
      reached     = false;
14
    }
15
16
    void reset()
17
    {
18
      reached     = true;
19
    }
20
21
    bool operator()(const uint32_t interval)
22
    {
23
      if(!reached) reached = millis() - timeStamp >= interval;
24
      return reached;
25
    }
26
};
27
28
29
30
31
constexpr byte Actuator_1 {2};
32
constexpr byte Actuator_2 {3};
33
34
35
void automat()
36
{
37
  static unsigned step {0};
38
  static SimpleTimer timer;
39
40
  switch (step)
41
  {
42
    
43
    case 0: pinMode(Actuator_1,OUTPUT); // einmalige initialisierung
44
            pinMode(Actuator_2,OUTPUT);
45
            step++;
46
            break;
47
            
48
    case 1: digitalWrite(Actuator_1,HIGH);
49
            timer.start();
50
            step++;
51
            break;
52
            
53
    case 2: if(timer(500)) // wenn abgelaufen
54
            {
55
              digitalWrite(Actuator_1,LOW);
56
              timer.start();
57
              step++;
58
            }
59
            break;
60
            
61
    case 3: if(timer(1000)) // wenn abgelaufen
62
            {
63
              digitalWrite(Actuator_2,HIGH);
64
              timer.start();
65
              step++;
66
            }
67
            break;
68
            
69
    case 4: if(timer(500)) // wenn abgelaufen
70
            {
71
              digitalWrite(Actuator_2,LOW);
72
              timer.start();
73
              step++;
74
            }
75
            break;
76
            
77
    case 5: if(timer(1000)) // wenn abgelaufen
78
            {
79
              step = 1;
80
            }
81
            break;
82
83
  //  default: step = 1;         
84
  }
85
}
86
87
void setup()
88
{
89
}
90
91
void loop()
92
{
93
  automat();
94
}
Aus Faulheit, und weil der Automat sowieso nicht wiederverwendet wird, 
kommen da globale und statische Variablen zum Einsatz.

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


Lesenswert?

Markus O. schrieb:
> oh verdammt. 255.
Bonusfrage: was steht denn nach dieser Zuweisung timer=500 in timer?
Und auch: was wäre nach timer=256 in timer?

von Markus O. (markusjo)


Lesenswert?

Lothar M. schrieb:
> was steht denn nach dieser Zuweisung timer=500

255

Lothar M. schrieb:
> was wäre nach timer=256

0-256 im loop

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Jetzt wo ich die periodische Wiederholung wider auf 100ms und die pause
> auf 50ms sowie die LED auf 1ms gestellt habe, scheint es zu passen.
> Es schein also irgendwie zeitkritisch zu sein

Nö, du hast mal wieder was vermurkst.

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


Lesenswert?

Markus O. schrieb:
> Lothar M. schrieb:
>> was steht denn nach dieser Zuweisung timer=500
> 255
Nein.
500 = 0x1F4 --> timer = 0xF4 = 244

>> was wäre nach timer=256
> 0-256 im loop
256 = 0x100 --> timer = 0x00 = 0

von Wolfgang (Gast)


Lesenswert?

Markus O. schrieb:
> momentan verzweifle ich daran mehrer Millis(), nacheinander, zu
> verwenden.

Die Funktion Millis() gibt es gar nicht. In C ist Groß-/Kleinschreibung 
relevant.

Und die Funktion millis() tut gar nichts, außer einen Wert mit dem 
aktuellen Stand vom Zeit-Tick-Zähler zurück zu geben. Ob du den 
Zählerstand mehrfach nacheinander abrufst, spielt überhaupt keine Rolle. 
Du bekommst halt jedes Mal den aktuellen Wert zurück.

von LostInMusic (Gast)


Lesenswert?

Code-Beitrag meinerseits (real getestet):
1
const byte PIN_LED_1 = 9;
2
const byte PIN_LED_2 = 10;
3
4
const int DAUER_LED_1 = 50;    //  500 ms
5
const int DAUER_SPACE = 200;   // 2000 ms
6
const int DAUER_LED_2 = 50;    //  500 ms
7
8
const int T0 = 0;
9
const int T1 = T0 + DAUER_LED_1;
10
const int T2 = T1 + DAUER_SPACE;
11
const int T3 = T2 + DAUER_LED_2;
12
const int T4 = T3 + DAUER_SPACE;
13
14
long z;  // zählt in Zehnerschritten von 0 bis 2^32-1
15
int t;   // zählt in Einerschritten von 0 bis T4
16
17
void setup() {
18
  // put your setup code here, to run once:
19
  pinMode(PIN_LED_1, OUTPUT);
20
  pinMode(PIN_LED_2, OUTPUT);
21
  t = 0;
22
  z = millis() + 10;
23
}
24
25
void loop() {
26
  // put your main code here, to run repeatedly:
27
  while (millis()<z) {};
28
29
  // Was unterhalb dieser Zeile steht, wird alle 10 ms ausgeführt
30
  // LEDs ein-/ausschalten
31
  digitalWrite(PIN_LED_1, t>=T0 && t<T1);
32
  digitalWrite(PIN_LED_2, t>=T2 && t<T3);
33
  
34
  // falls Sequenz am Ende angekommen Timer deaktivieren
35
  if (t==T4) t = -1;
36
37
  // falls Timer aktiv, t inkrementieren
38
  if (!(t==-1)) t++;  
39
40
  z = z + 10;
41
}

Man kann das natürlich auch viel komplizierter machen.

von EAF (Gast)


Lesenswert?

LostInMusic schrieb:
> auch viel komplizierter
Naja....


LostInMusic schrieb:
> z = z + 10;
Es droht ein signed overflow, welcher nicht spezifiziert ist.
1
E:\Programme\arduino\portable\sketchbook\sketch_oct14f\sketch_oct14f.ino:22:18: warning: comparison of integer expressions of different signedness: 'long unsigned int' and 'long int' [-Wsign-compare]
2
   22 |   while (millis()<z) {};
3
      |          ~~~~~~~~^~
Das heißt: Kann klappen, muss aber nicht.

LostInMusic schrieb:
> while (millis()<z) {};
Auch nicht besser, als ein delay()

von LostInMusic (Gast)


Lesenswert?

>Auch nicht besser, als ein delay()

Aber auch nicht schlechter.

von Johannes S. (Gast)


Lesenswert?

und es ist eine reine Zeitsteuerung. In einer Schrittkette möchte auch 
man auch gerne auf Ereignisse wie z.B. Endschalter warten, das bekommt 
man einfacher in den switch-case integriert. Oder verschiedene 
Ablaufsequenzen haben, dann wird so ein optimierter Code schnell sehr 
unübersichtlich.
Ich habe ja schon mehrfach geschrieben das so etwas auch schön mit 
EventQueues geht, wenn man soetwas denn hat. Aber alle Welt muss es ja 
mit Arduino machen.

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Das heißt: Kann klappen, muss aber nicht.

Insbesondere führt diese Art der Nutzung von millis() nach einem 
Zählerüberlauf zu einer "etwas" längeren Zeit.

Beitrag "Re: Bug durch millis() Überlauf beim Vergleichen vermeiden"

von Wolfgang (Gast)


Lesenswert?

LostInMusic schrieb:
> Code-Beitrag meinerseits (real getestet):

Dann kann das kein vernünftiger Dauertest über den Überlauf von millis() 
hinaus gewesen sein.

von Markus O. (markusjo)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> Nö, du hast mal wieder was vermurkst.

Ich habs jetzt in mein Programm implementiert bekommen.
Allerdings passt der Takt wieder nicht wenn ich den FSM auf 1 oder 10ms 
stelle und nach dem ersten Aufruf blinken die LED´s 1-2 Zyklen recht 
schnell hin- und her. Da ich nicht weiß wie ich hier den Code über 
10Tabs keinkopieren kann, schicke ich die Datei. Wäre toll wenn du oder 
die anderen mal reinschauen könntet.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> void setup()
> {
>    ...
>    time_old = millis();
>    lcd.begin();
>    lcd.backlight();
>    ....
> }

Und später dann:
> void between()
> {
>   const unsigned int FSM_PERIOD = 100;   //ms
>   if ( (millis() - time_old) >= FSM_PERIOD)
>   {
>     time_old += FSM_PERIOD;
>     Step();
>   }
> }

Bis dahin ist die FSM_PERIOD wahrscheinlich schon mehrfach abgelaufen, 
weil deine setup() Prozedur so lange dauert.

Markus O. schrieb:
> Allerdings passt der Takt wieder nicht wenn ich den FSM 1 odedr 10ms
> stelle

FSM 1 kommt in deinem Code nirgends vor. Was heißt "odedr" ?

von Markus O. (markusjo)


Lesenswert?

Stefan ⛄ F. schrieb:
> FSM_PERIOD = 100;

Wenn ich hier statt der 100ms 1ms "oder" 10ms eintrage.

Wie kann ich das Setup effizienter gestalten, ohne jetzt hoch 
wissenschaftlich zu werden? :D

Und wieso kann ich "time_old = millis();" nicht erst in der Funktion 
"between" aufrufen?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Wäre toll wenn du oder
> die anderen mal reinschauen könntet.

Noch mehr Chaos? Meine Güte, was soll das?
In den Dateien gibt es weder Anfang noch Ende. Wo ist denn die 
"Hauptdatei" mit setup() und loop()? Ahhh, in der sinnvollen Datei 
arduforcam.ino.ino. OMG!

Und warum meinst du, ist deine komprimierte Schreibweise besser? Willst 
du Zeichen sparen, um das Weltklima zu retten? Schlechte Idee!

Naja, und der Rest? OMG! Das ist Chaos^3. Du hast weder Plan noch 
Struktur, kein Wunder, daß da alles mögliche klemmt. Deine 
Menubearbeitung ist ein Irrsinn. Deine Tastenentprellung kaum was wert. 
Denn die muss in einem gescheiten Zeitraster erfolgen, sond funktioniert 
sie nicht wirklich.

Ich sage es zum letzten Mal. Lies den Artikel Multitasking. Mehrfach 
und langsam. Dann wird dir hoffentlich klar, wie sowas strukturiert sein 
sollte. Mit einem Timer, bzw,. hier bei Arduino mit der 
Hilfskonstruktion mit dem Intervall und millis().
1
  if ( (millis() - time_old) >= FSM_PERIOD) {
2
    time_old += FSM_PERIOD;
3
    Steps();
4
    // andere Funktionen hier periodisch aufrufen
5
  }

In diesem festen Zeitinterfall werden alle relevanten Funktionen für 
LED-Ablauf, Tastenentprellung und Menudarstellung als einzelne 
Funktionen aufgerufen. Alle Funktionen arbeiten NICHT blockierend! Dann 
klappt das auch.

Und lass den Unsinn mit einem Dutzend Quelldateien! Das kannst du 
machen, wenn du die Grundlagen mal WIRKLICH verstanden hast und größere 
Projekte bearbeitest. Im Moment zerfaserst du die Informationen fast bis 
zur Unkenntlichkeit!

von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> Wie kann ich das Setup effizienter gestalten, ohne jetzt hoch
> wissenschaftlich zu werden? :D

Verschiebe die Zeile ganz ans Ende von setup():
> time_old = millis();

> Und wieso kann ich "time_old = millis();" nicht
> erst in der Funktion "between" aufrufen?

Weil du deine Variable dann bei jedem einzelnen Funktionsaufruf 
überschreiben würdest.
> if ( (millis() - time_old) >= FSM_PERIOD)
würde niemals zutreffen.

Falk B. schrieb:
> Das ist Chaos^3. Du hast weder Plan noch
> Struktur, kein Wunder, daß da alles mögliche klemmt.

Sehe ich auch so

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Stefan ⛄ F. schrieb:
>> FSM_PERIOD = 100;
>
> Wenn ich hier statt der 100ms 1ms "oder" 10ms eintrage.
>
> Wie kann ich das Setup effizienter gestalten, ohne jetzt hoch
> wissenschaftlich zu werden? :D

Wozu? Setup wir nur einmal beim Start aufgerufen, fertig. Das kann fast 
beliebig ineffizient und langsam sein.

> Und wieso kann ich "time_old = millis();" nicht erst in der Funktion
> "between" aufrufen?

Wozu?

Du bist ein Chaot. Viel Spaß noch beim Programmieren.

von EAF (Gast)


Lesenswert?

LostInMusic schrieb:
> Aber auch nicht schlechter.
Kann man geteilter Meinung sein....
1
while (millis()<z) {yield()};

Dann hätten wenigstens noch andere Programmteile eine Chance, irgendwas 
in den Pausen zu erledigen.

von Falk B. (falk)


Lesenswert?

EAF schrieb:
> Dann hätten wenigstens noch andere Programmteile eine Chance, irgendwas
> in den Pausen zu erledigen.

Klar, und das bei jemanden, dem die elementarsten Kenntnisse fehlen.

von EAF (Gast)


Lesenswert?

while (millis()<z) {yield();};

von Wolfgang (Gast)


Lesenswert?

LostInMusic schrieb:
>>Auch nicht besser, als ein delay()
>
> Aber auch nicht schlechter.

Schon schlechter.
delay() wird nach der eingetragenen Zeit fertig, während while 
(millis()<z) {yield();}; den µC für fast 50 Tage in der Schleife hält, 
wenn's dumm läuft. Den Fehler finde mal.

von Markus O. (markusjo)


Lesenswert?

Ich bin erst seit kurzem dabei und das ist mein erstes Projekt dass so 
umfangreich ist, auch wenns für dich nicht umfangreich ist. Ich bin froh 
dass es funktioniert. Will es aber natürlich verbessern und Optimerungen 
auch für zukünftige Projekte nutzen.

Imemr nur zu sagen dass das scheiße und Chaos ist hilft mir nicht 
weiter, oder zu sagen lies das und jenes. Es wäre für mich 100mal 
wertvoller wenn du mir sagen würdest was an dem ganzen Programm mist ist 
und wie ich besser fahren würde.

Ich danke dir und den anderen sehr für die Hilfe.

von Markus O. (markusjo)


Lesenswert?

Stefan ⛄ F. schrieb:
> Weil du deine Variable dann bei jedem einzelnen Funktionsaufruf
> überschreiben würdest.

Wenn ich das in die Fubktion mit "static unsigned long" schreibe, 
scheint es zu funktionieren und es gibt auch am Anfang, die ersten 2-3 
Zyklen, nicht dieses Problem. Oder tritt irgendwann ein mir bisher 
verborgenes Problem auf?

von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> Wenn ich das in die Funktion mit "static unsigned long" schreibe,
> scheint es zu funktionieren

Ja weil die Variable wegen "static" so lange "lebt" wie globale 
Variablen und nur einmal initialisiert wird.

von ... (Gast)


Lesenswert?

> in die Fubktion mit "static unsigned long"

Du weisst was das static tut?
[ ]

von EAF (Gast)


Lesenswert?

Markus O. schrieb:
> Imemr nur zu sagen dass das scheiße und Chaos ist hilft mir nicht
> weiter, oder zu sagen lies das und jenes. Es wäre für mich 100mal
> wertvoller wenn du mir sagen würdest was an dem ganzen Programm mist ist
> und wie ich besser fahren würde.

Es gab viel Kritik.
Es gab auch viele Vorschläge, wie man es besser macht. Verschiedenste.
Dass du die Grundlagen der Sprache, hier C++, nicht aus Büchern lernen 
möchtest, macht die Situation für keine Seite befriedigender.

Mit dir müsste man einen (C++ ?) Grundlagenkurs durchziehen.
Dir die minimalsten Style Guides eintrichtern
Und dann auch noch die üblichen Designpattern abarbeiten.

Es gibt ein gefühltes Dutzend unterschiedlicher Wege eine FSM zu bauen, 
wovon ich dir mindestens 2 funktionierende gezeigt habe.
Das war offensichtlich fruchtlos.

Wo der Haken im Feuer liegt, ist dass du die logische Denke, noch nicht 
auf dem Schirm hast.
Du hast zwar einen Plan, in deiner Fantasie, das ist schon mal gut.
Aber leider beugt sich der µC nicht deinen Wünschen. Einfach weil du es 
ihm noch nicht verkaufen kannst...

Jetzt kannst du den Schuldigen bei uns suchen, beim Compiler, oder beim 
Wettergott...

Natürlich helfen dir die Antworten nicht weiter, solange du dein Denken 
nicht anpasst. Aber genau das, das können wir nicht für dich leisten. 
Das kannst nur du.


PS:
Ich wurde hier im Forum sogar schon dafür "verhauen", weil ich versucht 
habe dir wenigstens ein paar Programmiereranstandsregeln vor den Kopf zu 
setzen.

von W.S. (Gast)


Lesenswert?

Markus O. schrieb:
> Wenn ich das in die Fubktion...

Nenne mal deine Funktion 'millis()' um in 'momentanerZaehlerstand()', 
dann wird es dir wahrscheinlich ein Stück klarer.

Mein wirklich sehr ernst gemeinter Rat: Lerne, vor dem Programmieren 
dein Projekt zu durchdenken und die Dokumentationen (soweit vorhanden) 
oder ersatzeshalber den Quelltext von Zeugs, was du bislang einfach so 
in dein Projekt eingebunden hast, zu lesen und zu verstehen - und zwar 
gründlich lesen und auch alles verstehen. Das ist ganz wichtig.

Und nochwas: Einfach bloß fremde Zutaten in seine Firmware einzubauen, 
macht noch keine funktionable Firmware oder gar gute Firmware aus. Das 
hatte mal ein Schauspieler etwa so formuliert: all die Zutaten für ein 
gutes Curry in eine Topf zu werfen, macht noch kein gutes Curry aus.

Für eine gute Firmware ist auch ein Stück eigene Phantasie nötig, aber 
die soll immer gefolgt werden vom gründlichen Durchdenken.

W.S.

von Markus O. (markusjo)


Lesenswert?

... schrieb:
>> in die Fubktion mit "static unsigned long"
>
> Du weisst was das static tut?
> [ ]

Nimmt den Wert nur einmal an und geht zum nächsten Zyklus nicht 
verloren??

von Markus O. (markusjo)


Lesenswert?

Danke für die konstruktive Kritik. Ich werde versuchen die hier 
genannten Vorschlägen umzusetzten.

Nun kam mir ein weiteres Problem auf.
Wenn ich den Menüpunkt verlasse, in dem der Zyklus abgearbeitet wird, 
zählt der Zähler wohl im Hintergrund weiter. Wenn ich den Menüpunkt dann 
wieder betrete, werden die beiden Aktoren sehr schnell hin- und her 
geschaltet, ohne die Pausen des Timers zu beachten. Bisher hab ich keine 
Idee wie ich das abstellen kann.
Die Differenz zwischen timerold und millis wird wohl so groß dass die 
dann beim wiederbetreten des Menüpunktes schnell abgearbeitet werden.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

Am "einfachsten" wäre es wohl, wenn du deinem BlinkModul Nachrichten 
senden könntest.
z.B. Stopp und Start.
Dann könnte es selber seinen Ruhezustand einnehmen und gesittet wieder 
anlaufen.

Wie soll man das nennen... vielleicht "Schnittstellen bauen/definieren"?

von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> Nun kam mir ein weiteres Problem auf.

War klar, das wird eine nicht endende Fortsetzungsgeschichte.

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Nun kam mir ein weiteres Problem auf.
> Wenn ich den Menüpunkt verlasse, in dem der Zyklus abgearbeitet wird,
> zählt der Zähler wohl im Hintergrund weiter. Wenn ich den Menüpunkt dann
> wieder betrete, werden die beiden Aktoren sehr schnell hin- und her
> geschaltet, ohne die Pausen des Timers zu beachten. Bisher hab ich keine
> Idee wie ich das abstellen kann.
> Die Differenz zwischen timerold und millis wird wohl so groß dass die
> dann beim wiederbetreten des Menüpunktes schnell abgearbeitet werden.

Ja. Weil deine Menusteuerung blockierend arbeitet und viel zu lange die 
CPU blockiert, meist mit unsinnigem Warten bzw. viel zuvielen 
Durchläufen. Siehe Multitasking (Hallo Echo, Echo, Echo, Echo, Echo, 
Echo . . .

von Stefan F. (Gast)


Lesenswert?

Falk B. schrieb:
> Siehe Multitasking

+1
Kann man gar nicht oft genug wiederholen.

von LostInMusic (Gast)


Lesenswert?

>Dann kann das kein vernünftiger Dauertest über den Überlauf von millis()
>hinaus gewesen sein.

lach Das stimmt! Mir ist tatsächlich das Versäumnis anzulasten, heute 
Nachmittag auch mal die 49 Tage bis zum Rollover der millis abzuwarten 
:-)

Vielleicht spielt dieser Aspekt beim TO ja auch keine Rolle, etwa wenn 
seine Höllenmaschine jeden Morgen ein- und jeden Abend wieder 
ausgeschaltet wird.

Wenn man das wasserdicht und failsafe machen wollte, würde man den 
10-Millisekunden-Tick mit einem der Hardware-Timer des Controllers 
generieren, denn (unter anderem) dazu sind sie ja da. Das geht übrigens 
auch im Arduino.

Wie es aussieht, hat der TO aber erstmal ganz andere Probleme zu lösen.

von chris_ (Gast)


Lesenswert?


von Stefan F. (Gast)


Lesenswert?

@Markus
Bei all der Meckerei, ich bin wirklich wie Falk der Meinung, dass du 
dein Projekt mit Zustandsautomaten strukturieren solltest. So kommt da 
eine ausbaufähige Grundstruktur rein. Die Komplexität mag sich am Anfang 
abschrecken, aber schon bald werden die Vorteile überwiegen.

Ich hatte dazu mal einen Aufsatz geschrieben, wo auch auf die korrekte 
Berechnung von Zeitspannen hingewiesen wird. Einfach weil diese beiden 
Sachen hier immer wieder hoch kommen. Zustandsautomaten sind die 
Antwort auf Projekte wie deins, wenn man nicht schon ein Betriebssystem 
als Basis hat.

Du bist nicht der Einzige, der sich damit schwer tut. Ich habe auch 
lange Zeit Kuddelmuddel Programmiert, bis mir ein Buch über allgemeine 
Patterns dieses Konzept nahelegte. Anfangs habe ich es nicht verstanden, 
aber der Gedanke reifte im Lauf der Zeit. Heute nutze ich das Konzept 
sehr oft - nicht nur auf Mikrocontrollern.

Schau es dir mal an: 
http://stefanfrings.de/multithreading_arduino/index.html

Aber lies auch den Artikel den Falk dir empfahl, der ist wirklich gut.

von Wolfgang (Gast)


Lesenswert?

LostInMusic schrieb:
> Wenn man das wasserdicht und failsafe machen wollte, würde man den
> 10-Millisekunden-Tick mit einem der Hardware-Timer des Controllers
> generieren, denn (unter anderem) dazu sind sie ja da. Das geht übrigens
> auch im Arduino.

Was meinst du wohl, wie der 1-Millisekunden-Tick, der beim Arduino 
normalerweise läuft, generiert wird?
Genau - mit einem Hardware-Timer.

von LostInMusic (Gast)


Lesenswert?

Man kann den Tick aber auch über millis() auf eine Weise erzeugen, die 
gewährleistet, dass beim Rollover alles gutgeht. Deshalb ist es 
zugegebenermaßen auch besser, es so zu machen:
1
uint16_t z;
2
int t;
3
4
void Periodically_every_10ms() {
5
  // LEDs ein-/ausschalten
6
  // ...
7
  // ...
8
}
9
10
void setup() {
11
  // put your setup code here, to run once:
12
  // ...
13
  z = 0;
14
}
15
16
void loop() {
17
  // put your main code here, to run repeatedly:
18
  uint16_t z_now = millis();
19
20
  if (uint16_t(z_now - z)>=10) {
21
    z = z_now; 
22
    Periodically_every_10ms();
23
  }
24
}

von LostInMusic (Gast)


Lesenswert?

>wie der 1-Millisekunden-Tick, der beim Arduino normalerweise läuft,
>generiert wird? Genau - mit einem Hardware-Timer.

Das ist mir klar. Man müsste sich eigentlich nur in die zugehörige ISR 
(T/C0 Overflow? T/C1 Compare Match?) einhooken können. Keine Ahnung, ob 
man das irgendwie tricksen kann.

von EAF (Gast)


Lesenswert?

LostInMusic schrieb:
> Man kann den Tick aber auch über millis() auf eine Weise erzeugen, die
> gewährleistet, dass beim Rollover alles gutgeht. Deshalb ist es
> zugegebenermaßen auch besser, es so zu machen:

Na na....
Der Überlauf funktioniert mit millis() perfekt.
Da ist überhaupt kein Klimmzug nötig.
Und irgendwas zum Zugeben sehe ich da auch nicht.

Welche Sorgen plagen dich mit millis()?

von LostInMusic (Gast)


Lesenswert?

Ich bezog mich auf den Post von Wolfgang (Gast) 14.10.2021 17:58.

von EAF (Gast)


Lesenswert?

LostInMusic schrieb:
> Ich bezog mich auf den Post von Wolfgang (Gast) 14.10.2021 17:58.
Und ich mich auf deinen!
Drum habe ich dich ja auch zitiert.

Aber ich sehe schon, deine Ideen sich offensichtlich so genial, dass ich 
mir darüber kein Urteil erlauben darf.
Lassen wir es mal dabei... ok?

von LostInMusic (Gast)


Lesenswert?

>dass ich mir darüber kein Urteil erlauben darf.

Habe ich das gesagt? Selbstverständlich darfst Du das und jeder andere 
auch. Also wenn noch was raus will bei Dir: Feuer frei! :-)

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> while (millis()<z) {yield();};

Aber nicht bei deiner Formulierung

EAF schrieb:
> Der Überlauf funktioniert mit millis() perfekt.

von EAF (Gast)


Lesenswert?

Wolfgang schrieb:
> EAF schrieb:
>> while (millis()<z) {yield();};
>
> Aber nicht bei deiner Formulierung

Das ist nicht meine "Formulierung".
Einzig das yield(); ist meine Hinzufügung.

Weiterhin gilt (wenn auch nur für mich):
> Der Überlauf funktioniert mit millis() perfekt.

LostInMusic schrieb:
>>dass ich mir darüber kein Urteil erlauben darf.
>
> Habe ich das gesagt? Selbstverständlich darfst Du das und jeder andere
> auch. Also wenn noch was raus will bei Dir: Feuer frei! :-)
Eigentlich hätte ich ja gerne erfahren, wo du bei millis() ein overflow 
Problem siehst, welches man mit einem weiteren Zeitraster beheben 
kann/muss?
vielleicht kann ich ja noch was von dir lernen.....

von Stefan F. (Gast)


Lesenswert?

EAF schrieb:
> Eigentlich hätte ich ja gerne erfahren, wo du bei millis() ein overflow
> Problem siehst, welches man mit einem weiteren Zeitraster beheben
> kann/muss?

Der maximal mögliche Wert ist 18446744073709551615

Mal angenommen "jetzt" liefert millis() den Wert 18446744073709551000 
und er soll mit "millis()<z" 10 Sekunden warten, auf welchen Wert 
würdest du z setzen wollen?

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Wolfgang schrieb:
>> EAF schrieb:
>>> while (millis()<z) {yield();};
>>
>> Aber nicht bei deiner Formulierung
>
> Das ist nicht meine "Formulierung".
> Einzig das yield(); ist meine Hinzufügung.

Immerhin hast du das (millis()<z) kritiklos übernommen, obwohl es großer 
Mist ist.

von Stefan F. (Gast)


Lesenswert?

Ich sehe gerade, dass millis() nur 32 Bit zurück liefert. Aber das 
ändert nichts am Prinzip. Nochmal korrigiert:

Der maximal mögliche Wert ist 4294967295

Mal angenommen "jetzt" liefert millis() den Wert 4294967000
und er soll mit "millis()<z" 10 Sekunden warten, auf welchen Wert
würdest du z setzen wollen?

von EAF (Gast)


Lesenswert?

Wolfgang schrieb:
> Immerhin hast du das (millis()<z) kritiklos übernommen, obwohl es großer
> Mist ist.
Unfug!

Das von dir zitierte Posting soll ganz alleine zeigen, wie man die beim 
Warten verplemperte Rechenzeit doch noch nutzen könnte.
Nicht mehr und nicht weniger.
Ob du das verstehst oder auch nicht, ist ganz alleine dein Problem.

Die Kritik habe ich ein gefühltes Dutzend mal in diesem Thread schon 
angebracht. Incl. Beispiel, wie man es richtig macht.
Und nicht hur ich...
Auch wenn "einige" meinen man müsste ein zweites Zeit Raster einführen, 
ich sage, das führt nur zu einer weiteren Chance sich ins eigene Knie zu 
schießen.

Stefan ⛄ F. schrieb:
> Mal angenommen
Du willst mir doch nicht ernsthaft unterstellen zu wollen, dass ich 
nicht mit millis() umzugehen weiß?
Oder hast du das betreffende Posting gar nicht Sinn erfassend gelesen?

von Stefan F. (Gast)


Lesenswert?

EAF schrieb:
> Du willst mir doch nicht ernsthaft unterstellen zu wollen, dass ich
> nicht mit millis() umzugehen weiß?

So habe ich deine Rückfrage aufgefasst:

EAF schrieb:
> Eigentlich hätte ich ja gerne erfahren, wo du bei millis() ein overflow
> Problem siehst

Ich habe dir verdeutlicht, wo ich das Overflow Problem sehe.

von W.S. (Gast)


Lesenswert?

EAF schrieb:
> Das von dir zitierte Posting soll ganz alleine zeigen, wie man die beim
> Warten verplemperte Rechenzeit doch noch nutzen könnte.
> Nicht mehr und nicht weniger.

Du scheinst ein bissel aufgebracht zu sein. Dabei sind deine Beispiele 
nix Systematisches, sondern sehen eher einem ad hoc workaround ähnlich.

Also rege dich lieber wieder ab.

Was mir bei all dieser Diskussion aufgefallen ist, ist wie hanebüchen 
und nicht sehr weitsichtig das ganze Zeugs gestaltet worden ist. Da ist 
es aus meiner Sicht kein Wunder, daß sich ein Programmierer darin ganz 
leicht verheddert und dann unfreiwillig ein weiteres Laokoon-Denkmal 
bildet.

W.S.

von EAF (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich habe dir verdeutlicht, wo ich das Overflow Problem sehe.
Du warst aber gar nicht gefragt....

Sondern LostInMusic, der ja sicherlich nicht ohne Grund die seltsame z 
Konstruktion gebaut hat.

Stefan ⛄ F. schrieb:
> So habe ich deine Rückfrage aufgefasst:
Ein Irrtum!
Siehe: Das hier gezeigte Beispiel mit der class SimpleTimer
Da gibts kein "Überlaufproblem" solange die Intervalle kleiner 49,x Tage 
sind.

von EAF (Gast)


Lesenswert?

W.S. schrieb:
> Dabei sind deine Beispiele
> nix Systematisches, sondern sehen eher einem ad hoc workaround ähnlich.
Leider sieht man von dir hier kein Beispiel!

von Markus O. (markusjo)


Lesenswert?

@Stefan
Hallo Stefan,
hab besten Dank.

Ich dachte mein Menü basiert aus einer State Machine - Switch Cases. 
Oder nutze ich die dafür verkehrt, dass die nicht zyklisch durchlaufen 
werden?

Welches Buch hat dir geholfen?

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
> Ja. Weil deine Menusteuerung blockierend arbeitet und viel zu lange die
> CPU blockiert, meist mit unsinnigem Warten bzw. viel zuvielen
> Durchläufen. Siehe Multitasking (Hallo Echo, Echo, Echo, Echo, Echo,
> Echo . . .

Ich schmeiße jetzt nicht mal eben alles über den Haufen. Ich versuche 
mich da langsam anzunähern. Ich habe das jetzt erstmal mit ->

if((millis() - time_old) >= (1.5 * FSM_PERIOD))time_old = millis();

in den Griff bekommen.
Ist das von der Lösung ok?

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Wolfgang schrieb:
>> Immerhin hast du das (millis()<z) kritiklos übernommen, obwohl es großer
>> Mist ist.
> Unfug!

Dann lies dir mal das Beispiel von Stefan ⛄ F. durch und denk nochmal 
drüber nach.

Stefan ⛄ F. schrieb:
> Mal angenommen ...

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> in den Griff bekommen.
> Ist das von der Lösung ok?

Keine Sekunde. Murks bleibt Murks. Aber mach mal.

von Stefan F. (Gast)


Lesenswert?

Markus O. schrieb:
> Ich dachte mein Menü basiert aus einer State Machine - Switch Cases.

Ja tut es, der Ansatz ist durchaus zu erkennen. Aber ich vermisse das 
Gesamt-Konzept. Wie gesagt dauert es womöglich eine Weile, bis es Klick 
macht und du einen Plan hast wie man das anstellen kann. Ich finde das 
nicht weiter Schlimm.

Lass dich auf jeden Fall nicht zu der Annahme verleiten, dass der Code 
aussagekräftig genug sei. Erstelle zuerst den Plan, der alle 
Zustandsautomaten der Maschine beschreibt. Dann erstelle den Code, der 
das in die Tat umsetzt.

Wenn man erst drauf los coded muss man schon ein extremes Genie sein, um 
dabei auch noch eine saubere nachvollziehbare Struktur aufzubauen. Ich 
habe zwei Programmier-Genies erlebt, aber Struktur haben sie nicht 
hinbekommen. Ihr Code war für andere immer unlesbar und unpflegbar.

> Welches Buch hat dir geholfen?

Ich weiß es nicht mehr. Das Buch stand in einem Regal in der Firma 
herum, wo ich vorher arbeitete. Da komme ich nicht mehr heran.

von EAF (Gast)


Lesenswert?

Wolfgang schrieb:
> denk nochmal drüber nach.
Würde ich dir auch raten...
Auch scheint verstehendes lesen und sinnerhaltenes zitieren, nicht so 
dein Ding zu sein.

Drum:
Ich beantrage die unbefristete Merkbefreiung für dich.
Und denke, dass dann auch deinem neuen Job, als Regelstab in einem 
Schwerwasserreaktor, nichts mehr im Wege steht.

von W.S. (Gast)


Lesenswert?

Markus O. schrieb:
> Ich dachte mein Menü basiert aus einer State Machine

Ähem... ganz so ist das nicht.
Ich gebe dir mal ein eher einfaches Beispiel:
Stelle dir vor, daß da ein Amt ist mit einem Amts-Haus und vielen Büros 
drin. Aber es gibt nur einen einzigen Beamten. Der flitzt von Büro zu 
Büro und schaut in jedem Büro nach, was dort als nächstes zu tun ist. 
Dazu gibt es auf dem Schreibtisch einen Zettel, wo das drauf steht. Wenn 
da z.B. drauf steht "warten bis die Post da ist" dann schaut der Beamte 
nach, ob da eine Nachricht über eingetroffene Post vorliegt und wenn 
nicht, dann saust er sofort zum nächsten Büro.

Kurzum, die genannte 'state machine' besteht nicht darin, daß sich der 
Beamte in ein bestimmtes Büro begibt, sondern darin, daß er in einem 
Büro den jeweiligen Status der Bearbeitung der dortigen Dinge vorfindet 
und wenn angesagt und möglich den Zustand dieses Büros einen Schritt 
weiter bringt (und das auf dem Zettel vermerkt).

Ich hatte ja schon mal über ein einfaches Menü als array von 
Menü-Einträgen für ein simples Text-LCD geschrieben. Sowas ist auch 
sowas wie eine einfache 'state machine' und der Zustand (und das, was 
grad angezeigt wird) hängt nicht davon ab, in welcher Warteschleife grad 
die CPU herumtrampelt, sondern von einer Variablen, wo die Nummer des 
gerade aktiven Menü-Eintrages vorgehalten wird. Das ist sozusagen der 
momentane Zustand der besagten Maschine und Änderungen dieses Zustandes 
drücken sich nicht aus durch irgend einen Durchlauf irgend einer 
switch-case Anweisung. Das Beispiel des Menüs auf dem Alpha-LCD soll 
hier nur als Illustration dienen.

Man kann das auch einfacher ausdrücken: Eine 'state machine' sollte man 
nicht dadurch bauen, daß der Instruktions-Zeiger der CPU als 
Zustands-Zeiger für diese Maschine dienen soll.

W.S.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Ein Bild sagt mehr als 1000 Worte. Sinngemäß ein gutes Beispiel an 
Quelltext. Siehe Anhang. Alles aufgeräumt und in Form gebracht.

Tastenentprellung und LED-Steuerung laufen im 10ms Zeitraster, das Menu 
im 100ms Zeitraster, das reicht locker.

von LostInMusic (Gast)


Lesenswert?

>[...] wo du bei millis() ein overflow Problem siehst, welches man mit
>einem weiteren Zeitraster beheben kann/muss?

Zunächst mal habe ich kein "weiteres Zeitraster" eingeführt, sondern 
problematischen Code zur Generierung des 10-ms-Ticks durch 
unproblematischen Code mit derselben Funktionalität ersetzt.

Nun zum Problem und seiner Erklärung.

Der Code
1
  while (millis()<z) {};
2
  // Was unterhalb dieser Zeile steht, wird alle 10 ms ausgeführt
3
  z = z + 10;
4
  [...]       // user specific part
tut erstmal bis unmittelbar unterhalb 2^32 Millisekunden = gut 49 Tage 
lang anstandslos das, was er soll, nämlich einen 10-ms-Zeittick 
generieren. Dann kommt es aber zu einem Unfall. Wieso?

z hat zu diesem Zeitpunkt den Wert 0xFFFFFFFA (dezimal 4294967290) und 
in der Zeile "z = z + 10" werden nun noch weitere 10 draufaddiert. 
Dadurch überschreitet z die 32-bit-Grenze, Rollover genannt. Das 
Ergebnis der Addition wird 0x00000004 und das ist schlecht, weil dadurch 
die Bedingung "millis()<z" in der while-Schleife bis auf weiteres failt 
(man könnte wegen des kleinen z-Wertes von 4 auch sagen "extrem failt"). 
Durch kommt es beim nächsten Durchlauf der while-Schleife zum 
sofortigen Exit (zur Erinnerung: Erwünscht ist ein Exit alle 10 ms). 
Danach erhöht sich zwar z von 4 auf 14 (dezimal), aber das ist immer 
noch ein mickriger Wert. Nun passiert das wirklich Böse: Der Sofort-Exit 
der while-Schleife wiederholt sich mit hoher Frequenz - er ist sozusagen 
sein eigener Grund dafür. Deshalb rast das Programm bis auf weiteres 
ungebremst immer wieder durch die while-Schleife durch (real wird es 
alle paar Dutzend Taktzyklen der CPU da vorbeikommen, soviel wie der 
Test auf "<" in der while-Schleife + die 10-Addition + der User-Code für 
die LEDs brauchen). Irgendwann wird es dann auch das schneckenlahme 
millis (lahm, weil es sich nur alle 1 ms einmal erhöht) über den 
32-bit-Rollover geschafft haben und kann dann endlich z (was 
währenddessen auch in Zehnerschritten auf einen hohen Wert inkrementiert 
wurde) einholen. Dann hat der Spuk ein Ende. Beim nächsten Rollover in 
vielen Tagen wiederholt er sich dann natürlich.

Das war jetzt die ausführliche Erklärung. Hast Du eigentlich gelesen, 
was  Stefan F. (stefanus) in seinem Post weiter oben nach "Mal 
angenommen..." geschrieben hat? Seine Frage bringt es nämlich schon gut 
auf den Punkt.

Falls millis und z beide "signed" wären, würde der Schlamassel übrigens 
schon beim Rollover 0x7FFFFFFF --> 0x80000000 passieren, also nach gut 
21 Tagen.

Deshalb sollte man den 10-ms-Tick nicht so generieren (außer man lässt 
das Device niemals über 49 Tage lang laufen - dann wäre es egal), 
sondern das mit "if (uint16_t(z_now - z)>=10) {}" machen. Diese Variante 
ist nämlich frei von jeglicher Rollover-Problematik.

Wenn Dich das alles noch genauer interessiert, schreib Dir ein 
Testprogramm und lass es im Simulator laufen. Da kannst Du diese Effekte 
dann im Detail nachvollziehen.

von Markus O. (markusjo)


Lesenswert?

Vielen Dank Falk,

jetzt erschlag mich bitte nicht...
Was meinst du hiermit?

(1<<0)
(1<<1)
(1<<2)
(1<<3)
(KEY_SELECT | KEY_UP | KEY_DOWN)

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Auch scheint verstehendes lesen und sinnerhaltenes zitieren, nicht so
> dein Ding zu sein.

Ob du da ein yield() in die while-Schleife einbaust oder nicht, hilft 
dem Hauptprogramm überhaupt nichts - es kann einfach 49 Tage in der 
Schleife hängen. Nur konkurrierende Prozesse, die auf kooperatives 
Multitasking angewiesen sind, profitieren von dem yield().

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Was meinst du hiermit?
>
> (1<<0)

Man nehme eine 1 und schiebe sie 0 mal nach links. Als Zahl ist das 1.

> (1<<1)
> (1<<2)
> (1<<3)

Oder 1, 2, 3 mal. Als Zahl ist das 2, 4 und 8.

> (KEY_SELECT | KEY_UP | KEY_DOWN)

Das ist eine binäre ODER-Verknüpfung dieser 3 Bimuster/Zahlen, siehe 
Bitmanipulation.

von EAF (Gast)


Lesenswert?

LostInMusic schrieb:
> Zunächst mal habe ich kein "weiteres Zeitraster" eingeführt, sondern
> problematischen Code zur Generierung des 10-ms-Ticks durch
> unproblematischen Code mit derselben Funktionalität ersetzt.

Genau das ist es was ich bezweifle!
Die Problem mit deinem Code sind:

LostInMusic schrieb:
> long z;  // zählt in Zehnerschritten von 0 bis 2^32-1
signed, damit ist  0 bis 2^32-1 falsch.
Näher dran wäre (0 bis 2^32-1)/2 und danach erfolgt ein Überlauf welcher 
weder in C, noch in C++ spezifiziert ist.

LostInMusic schrieb:
> z = z + 10;
Hier wird irgendwann ein signed Überlauf stattfinden welcher weder in C, 
noch in C++ spezifiziert ist.
Selbst wenn z unsigned long wäre, würde ein Überlauf stattfinden, 
ungefähr in zu der Zeit, wo auch millis seinen Überlauf hat. Dies beiden 
Überläufe kompensieren sich nicht gegenseitig, so dann nachfolgende 
Vergleiche versagen werden.

LostInMusic schrieb:
> while (millis()<z) {};
Dieser versagt dann.
Zudem wird das restliche Programm in der Wartezeit vollständig 
blockiert.
Also ein "Doppelfehler"

So viel zu deinem zuerst hier gezeigten Programm.
----------------

LostInMusic schrieb:
> Deshalb sollte man den 10-ms-Tick nicht so generieren (außer man lässt
> das Device niemals über 49 Tage lang laufen - dann wäre es egal),
> sondern das mit "if (uint16_t(z_now - z)>=10) {}" machen.
Du hast also eingesehen, dass dein ursprünglich vorgeschlagener Weg 
falsch ist, und später dann eine "Verbesserung" eingebracht.

Wobei ich da nicht sehe, wozu der Cast da dienlich ist, oder überhaupt 
das unsigned long zu uint16_t runter gebrochen wird.
Der Sinn erschließt sich mir nicht.
Meine Denke sagt: Das macht es unnötig kompliziert, und kann darum 
eliminiert werden. Ohne Verlust.

Ok, 2 Byte spart es ein..
Aber dafür muss dann im nachfolgenden Programm aus dem 10ms Takt wieder 
eine z.B. 500ms Leuchtdauer gebaut werden. Auch das kostet etwas, und 
gleicht den 2 Byte Gewinn sicherlich wieder aus.

------

Zu dem 2ten Zeitraster:
Egal ob das deine Erfindung ist, oder von wem es kommt.
Nicht-Arduino Programmierer, welche sich mit µC befassen, werden sich 
ein Zeitraster basteln müssen, eben um Zeitabhandlungen bauen zu können.
Das ist wohl ein unumstößliches Faktum.
Das geht in Fleisch und Blut über.

Das wird dann auch wohl der Grund, für den Trieb sein, hier ein 
eigenes/weiteres Zeitraster auf zu ziehen. Also sowas wie Gewohnheit, 
und Unwissen über die Arduino Umgebung.

Arduino Programmierer müssen das nicht tun, denn sie haben mit millis() 
ein vorgefertigtes Zeitraster, welches zur Nutzung bereit steht.

Es gibt also aus der Ecke keine Notwendigkeit einen 2ten Takt 
einzuführen oder zu generieren. Meine Denke sagt: Das macht es unnötig 
kompliziert, und kann darum eliminiert werden. Ohne Verlust an 
Funktionalität.

-------------


LostInMusic schrieb:
> Hast Du eigentlich gelesen,
> was  Stefan F. (stefanus) in seinem Post weiter oben nach "Mal
> angenommen..." geschrieben hat?
Dass ich verstanden habe, wie mit millis() umzugehen ist, dürfe meine 
SimpleTimer Klasse und ihr Einsatz in dem Beispiel zeigen.
Auch wenn weder Stefan, Wolfgang, W.S, und auch Du, mir das nicht 
zugestehen möchten.
Von mir aus ist das auch ein "Dirty Hack"...
Das Urteil würde ich sogar akzeptieren, wenn der Urteiler zeigt, wie es 
besser geht.

Zudem:
Sorry, was Stefan schreibt, ist (für mich) oft recht uninteressant...
- manchmal haben millis() bei ihm 64 Bit
- dann muss er wieder einen Schwank aus deinem Java zum besten geben
- auch häufiger mal versteht er nicht, was man ihm sagen will
- Selbstdarstellung
Gegen seine Links auf seine Seite will ich mal nicht viel sagen, denn 
sie könnten ja für irgendeinen hilfreich sein. Nur: Die Häufigkeit ist 
sehr auffällig, und nervt (mich) schon etwas.

LostInMusic schrieb:
> Wenn Dich das alles noch genauer interessiert, schreib Dir ein
> Testprogramm und lass es im Simulator laufen. Da kannst Du diese Effekte
> dann im Detail nachvollziehen.
Ja, ich danke dir für den Versuch, mich für blöd zu erklären.
Ich hoffe dass du dich damit überzeugen kannst.

von Falk B. (falk)


Lesenswert?

Hey ihr Koniferen EAF, LostInMusic, Wolfgang und der Rest der 
Ungekrönten. Labert euch per PM oder in Offtopic voll und laßt den 
Scheiß HIER bleiben!
Danke!

von EAF (Gast)


Lesenswert?

Falk B. schrieb:
> und laßt den Scheiß HIER bleiben!
Warst du nicht derjenige, welcher die Dummheit mit dem zweiten 
Zeitraster hier in die Welt geworfen hat?
Schön blöd, sich dann über die Diskussion zu echauffieren... oder?

von Stefan F. (Gast)


Lesenswert?

Ich stelle mir gerade einen Fütterautomat vor, der alle par Stunde ein 
paar Brekkies ausgibt, aber nach 49 Tagen den ganzen Sack auf einen 
Schlang aus schüttet.

Solche Szenarien sind nicht selten, die treten aber meistens erst auf, 
wenn das Produkt schon einige Jahre alt ist und man es nicht mehr 
umtauschen kann.

von LostInMusic (Gast)


Lesenswert?

>Näher dran wäre (0 bis 2^32-1)/2 und danach erfolgt ein Überlauf welcher
>weder in C, noch in C++ spezifiziert ist.

Allen Undefiniertheiten zum Trotz erzeugt der C-Compiler letzlich ein 
Hex-File, das man disassemblieren und analysieren kann. Daraus geht 
eindeutig hervor, was tatsächlich passieren würde. Ich habe mir das 
Compilat angeschaut und kann Dir versichern, dass der "Unfall" so 
eintritt, wie ich es beschrieben habe.

Mit der Frage, ob wegen "undefiniert" eventuell andere Compiler anderen 
Code produzieren, der zu anderen Unfällen zu anderen Zeitpunkten führt, 
möchte ich mich nicht mehr beschäftigen.

>Zudem wird das restliche Programm in der Wartezeit vollständig blockiert.

Manchmal (und gar nicht so selten) gibt es gar kein restliches Programm.
Wie hier zum Beispiel. Dann wird es auch nicht blockiert.

>und später dann eine "Verbesserung" eingebracht.

Ja, ich habe problematischen Code zur Generierung des 10-ms-Ticks durch 
unproblematischen Code mit derselben Funktionalität ersetzt. Aber das 
sagte ich doch schon.

>wozu der Cast da dienlich ist, oder überhaupt das unsigned long zu
>uint16_t runter gebrochen wird.

Der generierte Code ist dann kleiner und schneller, weil der Controller 
es mit 16-bit-Arithmetik einfacher hat. Man könnte hier sogar auf 
uint8_t casten, weil 10 in ein Byte passt. Man dürfte dann nur nicht die 
10 in z. B. 300 ändern - das ginge dann schief.

>Aber dafür muss dann im nachfolgenden Programm aus dem 10ms Takt wieder
>eine z.B. 500ms Leuchtdauer gebaut werden.

Ja. Stört mich kein bischen.

>Zu dem 2ten Zeitraster:

Kann man ablehnen, muss man aber nicht. Ich finde es angenehm, in einem
Programm eine Stelle zu haben, von der ich weiß, dass dorthin 
geschriebener
Code in einem definierten Zeitintervall (hier die 10 ms) ausgeführt 
wird.

>Auch wenn weder Stefan, Wolfgang, W.S, und auch Du, mir das nicht
>zugestehen möchten.

Seltsames Statement. Wie kommst Du darauf?

>[...] und nervt (mich) schon etwas.

Mich nicht. Im Gegenteil.

>Ja, ich danke dir für den Versuch, mich für blöd zu erklären.

Dann ist es falsch rübergekommen.

Mach Dich mal ein bisschen locker.

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
>> (1<<1)
>> (1<<2)
>> (1<<3)

verstehe nicht wie das hier zur Anwendung kommt.

Falk B. schrieb:
> Das ist eine binäre ODER-Verknüpfung dieser 3 Bimuster/Zahlen, siehe

Dachte das wird mit "||" gemacht?? oder gibt es da solche Ausnahmen?

von EAF (Gast)


Lesenswert?

Markus O. schrieb:
> Dachte das wird mit "||" gemacht??

"||" logisches Oder
"|" binäres Oder

Bitte C oder C++ Buch zu Operatoren befragen....

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Falk B. schrieb:
>>> (1<<1)
>>> (1<<2)
>>> (1<<3)
>
> verstehe nicht wie das hier zur Anwendung kommt.

Damit werden Zahlen bzw. Bitmuster erzeugt. Das ist für den Fachmann 
besser lesbar als die reine Zahl

(1<<0) == 1 == 0b0001
(1<<1) == 2 == 0b0010
(1<<2) == 4 == 0b0100
(1<<3) == 8 == 0b1000

Die linke Spalte habe ich als Schreibweise genutzt, die mittlere geht 
auch, ist aber nicht ganz so offensichtlich (naja, für Fortgeschrittene 
schon), die rechte versteht nicht jeder C-Compiler, weil die 
Binärschreibweise noch nicht standardisiert ist. Der avr gcc und damit 
Arduino versteht sie.

Mit diesen Bitmustern wird Bitmanipulation gemacht. Bits setzen, 
löschen, prüfen. Das wird in der Tastenentprellung benutzt, um die vier 
Tasten als einzelne Bits in einem Byte zu speichern und zu verarbeiten.
Da ist sogar noch Platz für 4 weitere Bits bzw. Tasten.

Hast du mein Beispiel mal real ausprobiert? Wie läuft es?

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

EAF schrieb:
> LostInMusic schrieb:
>> Deshalb sollte man den 10-ms-Tick nicht so generieren (außer man lässt
>> das Device niemals über 49 Tage lang laufen - dann wäre es egal),
>> sondern das mit "if (uint16_t(z_now - z)>=10) {}" machen.
> Du hast also eingesehen, dass dein ursprünglich vorgeschlagener Weg
> falsch ist, und später dann eine "Verbesserung" eingebracht.

Ihr beiden kommt aus der Furche nicht heraus, in die ihr mitsamt dem 
Arduino gestolpert seid.

All so etwas, das im Millisekunden- oder gar Sekunden-Bereich an 
Zeitangelegenheiten stattfindet, macht man zweckmäßig mit einer 
Software-Uhr in der Firmware. Die liefert nicht nur die aktuelle Uhrzeit 
(im Millisekunden-Takt ab Systemstart bzw. ab Mitternacht), sondern sie 
kann auch so ganz nebenbei zeitsynchron verschiedene Handler starten, 
die womöglich von der konkreten Anwendung benötigt werden.

Obendrein kann so eine Software-Uhr auch die Mitternacht handhaben, 
indem sie z.B. den Millisekunden-Zähler und etwaige sonstige Zeitmarken 
um die Länge eines Tages (86400000 ms) zurücksetzt. Das setzt allerdings 
voraus, daß andere Programmteile sich eigene Konkurrenz-Zähler 
verkneifen, denn die können von der Uhr nicht auch noch verwaltet 
werden.

Dazu kann eine Uhr auch sowas wie 'delayed events' verwalten. Sowas 
sieht aus Sicht der aufrufenden Programme etwa so aus:
1
extern bool  Add_Delayed_Event   (dword millisec, dword aEvent);
2
extern void  Kill_Delayed_Event  (dword aEvent);
3
extern bool  Exists_Delayed_Event(dword aEvent);

Die Benutzung sieht dann so aus:
1
 Add_Delayed_Event(2000, evSchalteLampeAus);
Das führt dann nach einer Zeitspanne von 2 Sekunden dazu, daß es dann 
einen Event 'evSchalteLampeAus' gibt, auf den man in der zuständigen 
Lampen-Logik entsprechend reagieren kann.

So ungefähr kann man all solche Probleme generell lösen. Ich hatte - 
wimre - so etwa vor 7 Jahren dazu mal eine Zip-Datei mit entsprechenden 
Quellen gepostet.

W.S.

von Johannes S. (Gast)


Lesenswert?

W.S. schrieb:
>Die liefert nicht nur die aktuelle >Uhrzeit (im Millisekunden-Takt ab 
>Systemstart

Und was macht millis() jetzt schlechter?

Und nochmal, das erste Programm des TO war ok, es fehlte nur die 
Initialisierung der Softwaretimer. Wenn timeout 500 ist und der Timer 
nach 501 ms aufgerufen wird, dann ist (501 - 0) > 500 und der Timer 
meldet schon beim ersten Aufruf abgelaufen.

Das hat nix mit Arduino Schiene zu tun. Diese loop in eine Funktion 
gepackt kann dann zyklisch in der Auswertung mit dem LCD aufgerufen 
werden, dafür wurde die ja nicht blockierend gebaut.

Und wie man den millis() Überlauf behandelt wurde hier schon vor vielen 
Jahren den Arduino Nutzern erklärt:
https://playground.arduino.cc/Code/TimingRollover/

von EAF (Gast)


Lesenswert?

W.S. schrieb:
> Ihr beiden kommt aus der Furche nicht heraus,
Tja...

Markus O. schrieb:
> momentan verzweifle ich daran mehrer Millis(), nacheinander, zu
> verwenden.

Arduino und seine millis() sind halt in diesem Thread das Thema.
Auch wenn dir das nicht so zu schmecken scheint.

Tipp:
Vielleicht kannst du ja doch noch etwas eine deiner Einstellung 
arbeiten, damit dir das nicht ganz so weh tut.


-----

LostInMusic schrieb:
> Mit der Frage, ob wegen "undefiniert" eventuell andere Compiler anderen
> Code produzieren, der zu anderen Unfällen zu anderen Zeitpunkten führt,
> möchte ich mich nicht mehr beschäftigen.

Schade eigentlich...
Da doch gerade im Arduino Umfeld durchaus Differenzen, auch größere, 
zwischen den µC und auch den Compilern zu erwarten sind.

Hier ein Beispiel mit einem UNO. Eine einfache Addition mit einer 
Vorzeichen behafteten Zahl, mit einem lustigen Seiteneffekt.
1
byte feld[200];
2
void setup()
3
{
4
  Serial.begin(9600);
5
    
6
  // alle Zellen vorbesetzen
7
  for(byte &data:feld) data = 0xFF;
8
  
9
  int result = 0; 
10
  for(byte data:feld) result += data;
11
  Serial.println(result); 
12
}
13
void loop(){}
Die Ausgabe ist: 4294952760
Was natürlich eigentlich nicht sein dürfte.
In deinen Vorstellungen wäre -14536 zu erwarten gewesen.
Du siehst die Diskrepanz?

Woran liegts?
Nein, nicht im Arduino Code...
Denn, mit volatile int result = 0; kommt das erwartete.
Sondern:
Der Compiler/Optimizer (-Os) "weiß", dass bei einer Addition einer 
positiven Zahl mit einer positiven Zahl nie ein negatives Ergebnis 
entstehen kann, und hat darum in der Ausgabe Routine alle Abhandlungen 
für negative Zahlen entfernt.
Das ist einer der Effekte, welcher einem blühen kann, wenn man sich auf 
unspezifizierte Wege begibt, wie z.B. der signed Überlauf eine solche 
Falle ist. Da gibt nichts dran rum zu deuteln, unspezifizierte 
Operationen sollte man unterlassen.


Aber schön, dass du dir den generierten Code auch in Assembler anschauen 
kannst.



LostInMusic schrieb:
> Ja. Stört mich kein bisschen.
Offensichtlich....

von chris_ (Gast)


Lesenswert?

EAF
>der Compiler/Optimizer (-Os) "weiß", dass bei einer Addition einer
>positiven Zahl mit einer positiven Zahl nie ein negatives Ergebnis
>entstehen kann, und hat darum in der Ausgabe Routine alle Abhandlungen
>für negative Zahlen entfernt.
>Das ist einer der Effekte, welcher einem blühen kann, wenn man sich auf
>unspezifizierte Wege begibt, wie z.B. der signed Überlauf eine solche
>Falle ist. Da gibt nichts dran rum zu deuteln, unspezifizierte
>Operationen sollte man unterlassen.

Das klingt interessant. Vor einiger Zeit hatten wir dieses lang 
diskutierte, ultimative Ergebnis:
Beitrag "Re: rollover save timer in c"

Ich sehe dein Argument und es erscheint mir durchaus richtig.

von W.S. (Gast)


Lesenswert?

Johannes S. schrieb:
> Und was macht millis() jetzt schlechter?

millis() macht rein garnix, es bietet keinerlei benutzbare 
Funktionalität und keine Verwaltungsfunktion für rein garnix. Das ist 
alles der jeweiligen Anwendung überlassen, ich würde das mal vergleichen 
mit einem hingeschütteten Haufen Kies, Splitt und Asphalt als Gegensatz 
zu einer gebauten Straße, über die man von A nach B kommen kann.

Das ist es, was millis() schlechter macht. Und ein jeder muß sich erst 
noch irgend etwas dazu einfallen lassen, um daraus wirlich einen Nutzen 
ziehen zu können. Bei Anfängern dann eben in main.c und jeder Gedanke an 
saubere Strukturierung in der Firmware ist dahin. Pfuscher!

W.S.

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Die Ausgabe ist: 4294952760
> Was natürlich eigentlich nicht sein dürfte.
> In deinen Vorstellungen wäre -14536 zu erwarten gewesen.
> Du siehst die Diskrepanz?
>
> Der Compiler/Optimizer (-Os) "weiß", ... und hat darum in der
> Ausgabe Routine alle Abhandlungen für negative Zahlen entfernt.

Was bitte hat die Ausgabe negativer Zahlen durch Serial.print() mit dem 
Thema millis() zu tun?

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Obendrein kann so eine Software-Uhr auch die Mitternacht handhaben,
> indem sie z.B. den Millisekunden-Zähler und etwaige sonstige Zeitmarken
> um die Länge eines Tages (86400000 ms) zurücksetzt.

millis() zählt die Laufzeit, nicht die Uhrzeit. Das muss man 
Unterscheiden können.

Auf gar keinen Fall darf millis() zurückgesetzt werden, das wäre Broken 
by Design. Damit löst du die wildesten Fehlfunktionen aus.

Was bei Arduino millis() ist, entspricht bei Linux /proc/uptime bzw. das 
uptime Feld im Rückgabewert von sysinfo(), bei Android 
SystemClock.UptimeMillis(), bei Windows GetTickCount(). Jedes 
Betriebssystem hat so einen Zähler. Er darf niemals zurück gesetzt 
werden. Auch nicht wenn die Uhr korrigiert wird.

W.S. schrieb:
> Pfuscher!

Je mehr du andere Leute derartig beschimpfst, umso weniger nimmt man 
dich ernst. Selbst wenn du damit Recht hättest.

von Johannes S. (Gast)


Lesenswert?

W.S. schrieb:
> millis() macht rein garnix,

was für ein Unfug zum Sonntagmorgen.
Arduino bietet erstmal ein spartanisches API, das ist bekannt. Gerade 
deshalb ist es bei Einsteigern beliebt, es ist überschaubar, man hat nix 
mit toolchains, Compileroptionen und Buildsystemen zu tun und das Leben 
beginnt mit setup() und dauert endlos in loop().

Anstatt hier nur wie üblich Häme und Spott abzulassen, sollte man den TO 
loben weil er sich Gedanken gemacht hat und schon den Ansatz mit der 
Statemaschine gewählt und wohl auch verstanden hat. Ich stichel ja auch 
gerne das es andere, bessere OS gibt, aber trotzdem muss man nicht 
gleich das ganze erarbeitete vom Tisch wischen. Das typische 'not 
invented here' Problem, nicht versuchen zu verstehen sondern erstmal 
alles anders machen.

Mit dem Grundbaukasten den das Arduino API bietet lässt sich die Aufgabe 
lösen, man braucht keinen Luxus von RTOS, Queues oder Events. Die 
millis() reichen aus um eine Zeit x zu warten, auch nicht blockierend. 
Dazu braucht man einen Merker für die Startzeit der in einer Funktion 
nicht auf dem Stack liegen darf, auch das hat der TO richtig gemacht. 
Die kleine Unschärfe war jetzt diesen als static in der Funktion zu 
deklarieren. Damit ist diese Variable nur in der Funktion selbst 
sichtbar. Um eine Differenz jetzt - Start zu bilden muss man aber Start 
vorher einmal auf heute setzen und nicht auf dem Anfang des Kalenders 
belassen. Thats all. Also die Static previousMillis raus aus der 
Funktion zu einer globalen Variablen machen und vor dem Aufruf von 
TimerAct() einmal auf die aktuellen millis() setzen. Problem gelöst mit 
den gegebenen Mitteln. Ohne Geschwafel, warum man da wieder 100+ 
Diskussionen raus machen muss...

So eine Aufgabe kann man dann zur Übung nehmen das weiter zu verbessern, 
keine Frage. Man kann probieren es mit Events zu lösen oder mit RTOS 
(FreeRTOS kann man auch mit Arduino auf AVR nutzen). Aber jede 'bessere' 
Lösung hat ihre eigenen Tücken, braucht mehr Resourcen oder Fehler sind 
versteckter. Für einen Einsteiger der noch Pattern wie Bitshifting 
lernen muss sicher einen touch too much.

Und Arduino bietet die Möglichkeit OO zu nutzen, das kann man auch hier 
für die SW Timer gebrauchen. Einfach um die globalen Variablen zu 
vermeiden durch Kapselung. Dann sind auch 'mehrere millis' möglich, 
obwohl für diese Aufgabe noch nicht nötig, es läuft ja nur ein Timer.
1
class SimpleTimer {
2
  public:
3
    void start(int timeout) { 
4
      _timeout = timeout;
5
      _startTime = millis();
6
    }
7
    
8
    bool isTimerExpired() {
9
       unsigned long now = millis();
10
       if ((now - _startTime) >= _timeout) {
11
          return true;
12
       }
13
       
14
       return false;
15
    }
16
  private:
17
    int _timeout;
18
    unsigned long _startTime;
19
};
20
21
void testTimer()
22
{
23
    SimpleTimer t;
24
    
25
    t.start(500);
26
    
27
    while(not t.isTimerExpired()) {
28
      // do something
29
   }
30
}

nur grob im online gdb getestet weil ich hier gerade keinen Arduino 
rumliegen habe.

von EAF (Gast)


Lesenswert?

Wolfgang schrieb:
> Was bitte hat die Ausgabe negativer Zahlen durch Serial.print() mit dem
> Thema millis() zu tun?

Das sieht mir irgendwie danach aus, als könntest du mehrere Postings 
nicht sinnerhaltend nacheinander lesen.


Aber gut, gerne noch mal eine Zusammenfassung für dich:
Ein Programm mit einem Unspezifiziertem Verhalten (signed Überlauf) ist 
ein defektes Programm.
Das Programm darf dir dann sogar das Wohnzimmer tapezieren.

Der Serial.print zeigt nur die Folgen, die dabei auftreten können.
Es ist hier der "Fehler ans Licht Bringer".
Welcher belegt, dass ein signed Überlauf eben nicht tolerierbar ist.
Das gilt auch im Zusammenhang mit millis()



Hier haben wir mindestens 2 Helden, denen das völlig egal zu sein 
scheint.
Die blasen das einfach ins Forum.


Falk B. schrieb:
> long time_old;
> // schnipp
> time_old += FSM_PERIOD;



LostInMusic schrieb:
> long z;  // zählt in Zehnerschritten von 0 bis 2^32-1
> // schnipp
>   z = millis() + 10;



Warum so viele Leute die Neigung haben das doch zu tun?
Keine Ahnung!
Und es sind schon recht viele.
Zu viele.
Nicht nur hier in diesem Thread.

Entweder kennen sie die Sprache nicht, welche sie verwenden, oder es ist 
ihnen schlicht scheißegal.
Vielleicht steckt auch Absicht dahinter... wer weiß... ich nicht.

Ist dir das auch egal?
Hast auch du den Zusammenhang jetzt verstanden?

von Wolfgang (Gast)


Lesenswert?

EAF schrieb:
> Aber gut, gerne noch mal eine Zusammenfassung für dich:
> Ein Programm mit einem Unspezifiziertem Verhalten (signed Überlauf) ist
> ein defektes Programm.

Das Überlaufverhalten für zwei Variablen auf dem selben Compiler dürfte 
ziemlich identisch sein und entsprechend birgt der direkte Vergleich vom 
altuellen Wert mit dem vorausberechneten Zielwerten das nun schon 
mehrfach beschriebene Fehlerpotential - unabhängig davon, ob das 
Verhalten unspezifiziert ist oder nicht.

Du kannst deine Theoriekiste wieder zu machen.

von EAF (Gast)


Lesenswert?

Wolfgang schrieb:
> birgt der direkte Vergleich vom
> altuellen Wert mit dem vorausberechneten Zielwerten das nun schon
> mehrfach beschriebene Fehlerpotential

Der eine Fehler schließt den anderen nicht aus.
Reite du auf dem einen rum, und ich auf dem anderen...
Denn beide Böcke finden wir hier in diesem Thread.
Wäre das für dich ok?



Oder bist du eher ein "Mein Fehler ist aber der wichtigere!" Type?


Wolfgang schrieb:
> unabhängig davon, ob das
> Verhalten unspezifiziert ist oder nicht.
Natürlich ist das unabhängig davon!

EAF schrieb:
> Hier haben wir mindestens 2 Helden, denen das völlig egal zu sein
> scheint.
Du möchtest nicht der dritte auf meiner Liste sein!

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
> Hast du mein Beispiel mal real ausprobiert? Wie läuft es?

Ich werde etwas Zeit dafür brauchen das zu verdauen. Das ist mir koplett 
fremd und nicht intuitiv. Bei sowas brauche ich etwas länger.
Ich werde mir das jeden Tag Stück für Stück anschauen und versuchen es 
nach und nach zu verstehen.

Vielen Dank erstmal.

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Ich werde etwas Zeit dafür brauchen das zu verdauen. Das ist mir koplett
> fremd und nicht intuitiv. Bei sowas brauche ich etwas länger.
> Ich werde mir das jeden Tag Stück für Stück anschauen und versuchen es
> nach und nach zu verstehen.

Zum Testen ob es läuft, muss man es nicht verstehen . . .
Egal

von Markus O. (markusjo)


Lesenswert?

Falk B. schrieb:
> Zum Testen ob es läuft, muss man es nicht verstehen . . .

Ja stimmt.

Funktioniert irgendwie nur so halb. das Hauptmenü wird, mit teils 
flackernden Symbolen, dargestellt und ich kann je eine Ebene weiter rein 
in Timer und Run. Danach passiert aber nichts mehr und wenn ich in eins 
der Untermenüs rein möchte, gehts für einen kurzen Moment rein und 
direkt wieder ins Hauptmenü. Ein Effekt als wären die Tatsten nicht 
entprellt und der gleiche Taster der für das einwählen und auswählen 
programmiert ist.

von Philipp K. (philipp_k59)


Lesenswert?

Könnte zum Beispiel so aussehen für mehrere Buttons.. da ging es um 
einstellbare Wiederholung bei gedrückt halten..

https://pastebin.com/WE0h4vpA

von Falk B. (falk)


Lesenswert?

Markus O. schrieb:
> Funktioniert irgendwie nur so halb. das Hauptmenü wird, mit teils
> flackernden Symbolen, dargestellt und ich kann je eine Ebene weiter rein
> in Timer und Run. Danach passiert aber nichts mehr und wenn ich in eins
> der Untermenüs rein möchte, gehts für einen kurzen Moment rein und
> direkt wieder ins Hauptmenü. Ein Effekt als wären die Tatsten nicht
> entprellt und der gleiche Taster der für das einwählen und auswählen
> programmiert ist.

Nicht so schön 8-0. Hmmm. So ganz kann ich das nicht glauben. Ich hab 
den Code noch einmal angesehen, ich sehe keinen groben Fehler.

Im Hauptmenu muss man mit UP/DOWN den Cursor an drei Stellen bewegen 
können, immer vor dem Wort. Mit SELECT kann man dann die Untermenus 
auswählen. Bei Timer Sollte der Potiwert dargestellt werden und sich 
auch live ändern. Bei Save wird nur der einmal eingestellt Potiwert 
angezeigt, hier geht nur BACK zum Hauptmenu. Im 3. Menu RUN kann man die 
LED-Sequenz einschalten (SELECT)- und ausschalten (BACK).

Zeig mal ein Video davon.

: Bearbeitet durch User
von markus (Gast)


Lesenswert?

Falk B. schrieb:
> Man nehme eine 1 und schiebe sie 0 mal nach links. Als Zahl ist das 1.
>
>> (1<<1)
>> (1<<2)
>> (1<<3)
>
> Oder 1, 2, 3 mal. Als Zahl ist das 2, 4 und 8.
>
>> (KEY_SELECT | KEY_UP | KEY_DOWN)

Also ist das als würde man

#define KEY_UP          1
#define KEY_DOWN        2
#define KEY_SELECT      4
#define KEY_BACK        8

schreiben?

Wieso so, wo ist der Unterschied in der Anwendung?

von Falk B. (falk)


Lesenswert?

markus schrieb:
>> Man nehme eine 1 und schiebe sie 0 mal nach links. Als Zahl ist das 1.
>>
>>> (1<<1)
>>> (1<<2)
>>> (1<<3)
>>
>> Oder 1, 2, 3 mal. Als Zahl ist das 2, 4 und 8.
>>
>>> (KEY_SELECT | KEY_UP | KEY_DOWN)
>
> Also ist das als würde man
>
> #define KEY_UP          1
> #define KEY_DOWN        2
> #define KEY_SELECT      4
> #define KEY_BACK        8
>
> schreiben?

Ja.

> Wieso so, wo ist der Unterschied in der Anwendung?

Geschmackssache. (1<<3) sagt, daß Bit 3 gemeint ist. 8 sagt das nicht 
direkt, außer den Nerds, die das sofort als Binärmuster vor Augen haben 
;-)

von markus (Gast)


Lesenswert?

Falk B. schrieb:
> Geschmackssache. (1<<3) sagt, daß Bit 3 gemeint ist. 8 sagt das nicht
> direkt, außer den Nerds, die das sofort als Binärmuster vor Augen haben
> ;-)

man, man, man :D

von W.S. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> millis() zählt die Laufzeit, nicht die Uhrzeit. Das muss man
> Unterscheiden können.

Also, genau so, wie es hier herausgekommen ist, macht millis() 
garnichts, sondern liefert nur den momentanen Stand des Zählers in der 
Systemuhr.

Und es sieht auch danach aus (nach den hier zu lesenden Beiträgen), daß 
sich weder diese Systemuhr noch irgendwer anders um das Behandeln des 
Tages kümmert, so daß eben dieser Zähler nach rund 40 Tagen überläuft. 
OK, für kleine Basteleien auf dem Werktisch reicht das aus, aber sauber 
und durchdacht ist das nicht.

Natürlich kann man kurze Laufzeiten dadurch bestimmen, daß man sich den 
Startzeitpunkt merkt und von da ab die Zeit selber mißt:
1
 Laufzeit = millis() - Startzeitpunkt;
2
 if (Laufzeit > MeinGewünschtesIntervall)
3
 { TueWas();
4
   Startzeitpunkt = millis();
5
   ...
Also so ungefähr. Aber so etwas jedesmal zu machen, wenn man die 
Zeitverzögerung braucht und obendrein damit eine Firmware zu bauen, die 
den Überlauf des obigen Zählers nicht verhindert, ist nur eine Bastelei. 
Und eine aufwendige Bastelei zudem. Ich würde so etwas zuerst einmal 
richtig machen und zwar so, daß man es immer wieder verwenden kann. Ist 
überhaupt nicht aufwendig, da gehen die Unkenrufe bzgl. RTOS usw. voll 
ins Leere.

W.S.

von EAF (Gast)


Lesenswert?

W.S. schrieb:
> Also, genau so, wie es hier herausgekommen ist, macht millis()
> garnichts, sondern liefert nur den momentanen Stand des Zählers in der
> Systemuhr.
Richtig erkannt!

W.S. schrieb:
> Und es sieht auch danach aus (nach den hier zu lesenden Beiträgen), daß
> sich weder diese Systemuhr noch irgendwer anders um das Behandeln des
> Tages kümmert, so daß eben dieser Zähler nach rund 40 Tagen überläuft.
Richtig erkannt!

W.S. schrieb:
> Natürlich kann man kurze Laufzeiten dadurch bestimmen, daß man sich den
> Startzeitpunkt merkt und von da ab die Zeit selber mißt:
Auch richtig!
Solange die abzumessenden Intervalle/Zeiten kleiner als 49,x Tage sind, 
ist das auch kein Problem. Den Überlauf steckt es locker weg.

W.S. schrieb:
> Aber so etwas jedesmal zu machen, wenn man die
> Zeitverzögerung braucht und obendrein damit eine Firmware zu bauen, die
> den Überlauf des obigen Zählers nicht verhindert, ist nur eine Bastelei.
Das ist deine Beurteilung!
Ein Abbild deiner Fantasieren.

Ich sage: Ein 32Bit Zähler ist billig.
Das verwalten einer Time/Date Struktur ist viel aufwändiger.

Also, wenn das billige reicht, dann nehmen wir es doch.


----------
Außerdem redest du schon wieder über C++ und Arduino.
Dabei hast du doch überhaupt keine Ahnung davon.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

EAF schrieb:
> Außerdem redest du schon wieder über C++ und Arduino.
> Dabei hast du doch überhaupt keine Ahnung davon.

Wir haben doch im Nachbarthread festgestellt, dass er auch kein C kann.
Aber das er immer versucht als Oberlehrer alle zu maßregeln.
-> sehr peinlich dieser W.S.
Link: Beitrag "Re: Mikrocontroller: Funktion strstr"

@W.S.
Bitte erstmal Grundlagen lernen bevor man anderen Leuten 9 mal Kluge 
Tipps gibt ;)

von HildeK (Gast)


Lesenswert?

markus schrieb:
> Falk B. schrieb:
>> Geschmackssache. (1<<3) sagt, daß Bit 3 gemeint ist. 8 sagt das nicht
>> direkt, außer den Nerds, die das sofort als Binärmuster vor Augen haben
>> ;-)
>
> man, man, man :D

Der Grund sind nicht unbedingt die Nerds.
Bei den internen Registern sind viele Einzelbitfunktionen mit Namen 
gekennzeichnet (und in Headerdateien mit der Bitnummer festgelegt). Es 
liest sich dann leichter, welche Funktion an- bzw. ausgeschaltet wurde.
Das könnten auch mal mehrere gleichzeitig in einem Register sein und 
dann wird eine Dezimalzahl extrem unübersichtlich.
Oder siehst du auf Anhieb, welche Bits gesetzt werden, wenn ich schreibe
PORTA = 37;
Leichter ist als zumindest die HEX-Darstellung
PORTA = 0x25;
noch leichter die Binärdarstellung
PORTA = 0b100101;
die nicht jeder Compiler verstehen muss.

Und übersichtlich ist dann
PORTA = (1<<PA0)|(1<<PA2)|(1<<PA5);
Diese Befehle setzen alle 8 Bit des Ports, will man nur die drei setzen 
und die anderen unbeeinflusst lassen, dann muss man schreiben
PORTA |= (1<<PA0)|(1<<PA2)|(1<<PA5);

von HildeK (Gast)


Lesenswert?

HildeK schrieb:
> Diese Befehle setzen alle 8 Bit des Ports

Schlecht ausgedrückt. Ich wollte sagen, es wird der ganze Port 
beeinflusst, gesetzt werden nur 0, 2 und 5 - die anderen aber 
zurückgesetzt!

von Wolfgang (Gast)


Lesenswert?

W.S. schrieb:
> so daß eben dieser Zähler nach rund 40 Tagen überläuft.
Ja, richtig verstanden - lass ihn doch.

> OK, für kleine Basteleien auf dem Werktisch reicht das aus, aber sauber
> und durchdacht ist das nicht.

Du hast es auch nicht verstanden

> und obendrein damit eine Firmware zu bauen, die
> den Überlauf des obigen Zählers nicht verhindert, ist nur eine Bastelei.

Du willst nicht wirklich regelmäßig an dem Zähler rumbasteln, oder?
Das mag für eine kleine Basteleien auf dem Werktisch ok sein, aber eine 
vernünftige Software lässt den HW-Zähler durchlaufen und formuliert die 
Software so, dass sie mit dem Überlauf klar kommt. Schon eine 32-Bit 
Subtraktion löst das Problem.

von W.S. (Gast)


Lesenswert?

EAF schrieb:
>> Aber so etwas jedesmal zu machen, wenn man die
>> Zeitverzögerung braucht und obendrein damit eine Firmware zu bauen, die
>> den Überlauf des obigen Zählers nicht verhindert, ist nur eine Bastelei.
> Das ist deine Beurteilung!

Tja, eben. Es ist meine Beurteilung solchen Vorgehens. Nebenbei bemerkt, 
da dir das offenbar nicht aufgefallen ist, reicht diese Lösung für 
Basteleien auch aus. Von daher habe ich kein Problem damit. Aber man muß 
so etwas dazusagen, damit andere nicht glauben, hier eine universelle 
Lösung zu haben. Das ist der Punkt.

W.S.

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Aber man muß so etwas dazusagen, damit andere nicht glauben,
> hier eine universelle Lösung zu haben.

Universell ist eine Hand voll Sand. Daraus kann man sich jeden Mikrochip 
so herstellen, wie man braucht.

Wenn du denkst, was andere glauben, dann denkst du mit hoher 
Wahrscheinlichkeit falsch, weil du anders denkst als andere.

von Wolfgang (Gast)


Lesenswert?

W.S. schrieb:
> Aber man muß so etwas dazusagen, damit andere nicht glauben, hier eine
> universelle Lösung zu haben. Das ist der Punkt.

Das einzige, was an dem überlaufenden Zähler nicht universell ist, ist 
dass man keine Verzögerungszeiten erzeugen kann, die länger als die 
Zählerperiode sind. Das wirst du nur lösen können, indem der Zähler mehr 
Bits bekommt. Ob für solche Zeiten eine Auflösung von Millisekunden 
erforderlich ist, kann jeder für sich entscheiden. Im Zweifelsfall hängt 
das von der Aufgabe ab.

von EAF (Gast)


Lesenswert?

W.S. schrieb:
> Es ist meine Beurteilung solchen Vorgehens.
Richtig!

Diese Beurteilung lässt offensichtlich außer acht, dass man immer das 
einfachere verwenden sollte, wenn es möglich ist.

Zudem gibts für deine "Tage" die time.h der Libc, welcher Arduino Usern 
vollumfänglich zur Verfügung steht.

Was alles aber nichts bringt, ohne stabile Zeitreferenz. Wo dann die 
gefühlt tausende Zeitlibs der Arduino Welt ins Spiel kommen.
Sei es übers Netzwerk, DCF, eine menge verschiedener RTCs usw.

Merke:
millis() gibt es auf jedem Kesselchen, welches sich mit Arduino 
bespielen lässt. Es ist also ideal und kostenlos für 
Intervalle/Zeiträume bis 49,x Tage.

Alles andere ist mit "Kosten" verbunden.


----------

Was du da hin projizierst, ist hier für die Aufgabenstellung hier völlig 
überzogen, hirnlos sinnfrei.

Und wenn man wirklich deine "Tage" und dann auch noch recht exakt haben 
möchte, greift man in den Pool der Arduino Libraries, dafür ist er ja 
da.

Das ist der Grund, warum sich keiner hinter dich stellt.


-----

W.S. schrieb:
> reicht diese Lösung für
> Basteleien

Nein, mit basteln hat das alles nichts zu tun.
Das ist einfach eine weitere Projektion von dir.
Die Gewichtung gibt es nur in deinem Kopf.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Sooo, ich hab zufällig in meiner Ramschkiste ein LCD mit I2C Adapter 
gefunden, da kann man den Quelltext mal live testen. OK, es waren noch 
ne handvoll kleine Fehler drin. Hier jetzt verbessert und getestet. Das 
Flackern ist auch weg, dank intelligentem LCD Refresh.

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.