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ß
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.
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
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
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.
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? ...
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
>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.
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
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
unsignedlongaktzeit,startzeit=0,Pausendauer=1000;
3
intPulse=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
elseSchalte_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.
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
unsignedlongdelayTime;
2
unsignedlongpreviousMillis;
3
4
voidsetTimer(unsignedlongt)
5
{
6
previousMillis=millis();
7
delayTime=t;
8
}
9
10
boolisTimerRunning()
11
{
12
if(delayTime==0){
13
returnfalse;
14
}
15
16
unsignedlongcurrentMillis=millis();
17
if(currentMillis-previousMillis>=delayTime)
18
{
19
// delay expired
20
delayTime=0;
21
returnfalse;
22
}else{
23
previousMillis=currentMillis;
24
returntrue;
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.
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.
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.
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();
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
LostInMusic schrieb:> Code-Beitrag meinerseits (real getestet):
Dann kann das kein vernünftiger Dauertest über den Überlauf von millis()
hinaus gewesen sein.
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.
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" ?
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?
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!
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
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.
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.
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.
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.
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?
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.
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.
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.
... 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??
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.
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"?
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 . . .
>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.
@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.
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.
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:
>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.
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()?
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?
>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! :-)
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.....
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?
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.
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?
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?
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.
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.
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.
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!
@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?
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?
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 ...
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.
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.
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.
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.
>[...] 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.
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().
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.
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.
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!
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?
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.
>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.
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?
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?
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:
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.
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/
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
bytefeld[200];
2
voidsetup()
3
{
4
Serial.begin(9600);
5
6
// alle Zellen vorbesetzen
7
for(byte&data:feld)data=0xFF;
8
9
intresult=0;
10
for(bytedata:feld)result+=data;
11
Serial.println(result);
12
}
13
voidloop(){}
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....
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.
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.
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?
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.
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
classSimpleTimer{
2
public:
3
voidstart(inttimeout){
4
_timeout=timeout;
5
_startTime=millis();
6
}
7
8
boolisTimerExpired(){
9
unsignedlongnow=millis();
10
if((now-_startTime)>=_timeout){
11
returntrue;
12
}
13
14
returnfalse;
15
}
16
private:
17
int_timeout;
18
unsignedlong_startTime;
19
};
20
21
voidtestTimer()
22
{
23
SimpleTimert;
24
25
t.start(500);
26
27
while(nott.isTimerExpired()){
28
// do something
29
}
30
}
nur grob im online gdb getestet weil ich hier gerade keinen Arduino
rumliegen habe.
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?
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.
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!
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.
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
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.
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.
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?
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
;-)
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
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.
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.
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 ;)
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);
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!
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.
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.
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.
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.
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.
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.