Moin,
ich wollte mir ein Gerät basteln wo ich ein Menü habe.
Soll son Gleichstromzähler mit Display und paar Tasten werden.
Dort kann man auch Konfigurationen ändern (Shunt Widerstand ändern,
Abfragintervall usw.).
Frage: Wie mache ich für das Konfiguration Menü ein Timeout?
Bin noch relativ ein Anfänger und möchte nachher nicht alles neu machen
müssen. Das verdirbt mir ein bisschen den Spass und darum gehts mir
eigentlich. Daher wollt ich mir von euch ein paar Ideen holen die sowas
schon öfter gemacht haben.
Ich hätte nun vor eine Statemaschine für das Menü zu machen das alle
100mS ausgeführt wird.
Wenn er in einem Konfigurations State ist, wird ein Counter hochgezählt
und wenn dieser z.B. auf 300 ist (30 Sekunden) springt er wieder in das
"Hauptmenü". Bei jedem Tastendruck oder Aktion wird der Counter
dementsprechend wieder zurück gesetzt auf 0.
Ist das schon die "beste" Lösung? Oder gibt es noch andere interessante
Lösungen?
Hatte vorher mal ein Menü programmiert (bei einer Uhr für Alarm Setzung
usw.) was ich mit while Schleifen realisiert habe. Dies fand ich unschön
und wollte diesmal ein bisschen professioneller angehen.
Gruß
Wolfsente schrieb:> Ich hätte nun vor eine Statemaschine für das Menü zu machen das alle> 100mS ausgeführt wird.
Das ist zu einstrassig gedacht. Du brauchst eine Timeoutfunktion, also
einen Timer, der z.B. alle 10 ms einen Time-Interrupt auslöst. In der
Interruptroutine zählst du einen Zähler hoch, und vielleicht noch
hundert andere Dinge, die zeitabhängig ablaufen sollen, da fällt einem
immer noch was ein.
Die Main Loop mit dem Menü kann ganz unabhängig davon ablaufen und
braucht sich auch um die Zeit nicht zu kümmern. Sie schaut bloss in
jedem Umlauf mal nach, ob der Zähler > x ist, wenn ja springt die
Statemachine zurück ins Hauptmenü. Und bei jedem Tastendruck wird der
Zähler auf Null gesetzt.
So ist die main Loop von der Laufzeit unabhängig, auch bei Änderungen
und Erweiterungen, und While-Schleifen braucht man weder da noch in der
Interruptroutine (da sind sie verboten).
Georg
Wolfsente schrieb:> Daher wollt ich mir von euch ein paar Ideen holen die sowas> schon öfter gemacht haben.
Kommt drauf an, ob du diese Ideen auch wirklich hören willst.
Also:
Ich baue mir in den meisten Fällen (mal ganz kleine µC ausgenommen) zu
allererst eine Systemuhr in die Firmware, die mit 1 oder 10 ms
Intervallen läuft und eben die Zeit in einer 32 Bit Variable hochzählt.
Als nächstes arbeite ich mit Events, also ereignisgesteuert.
So, nun hab ich es so eingerichtet, daß die Systemuhr etwas kann, das
ich salopp "verzögerte Events" nenne: Die Systemuhr hat eine kleine
Liste, wo man mit einer ihrer Methoden (etwa so: AddDelayedEvent(aEvent,
aDelay);) ein Ereignis hineinschreiben kann. Mit jedem Uhr-Tick gucht
die Systemuhr in diese Liste und wenn sie dort ein Ereignis findet,
dessen Zeit abgelaufen ist, dann holt sie sieses Ereignis aus der Liste
und schmeißt es in die Liste der grad anstehenden Ereignisse. Von dort
holt die Grundschleife in main das Ereignis heraus und bringt es zur
Abarbeitung.
Länglicher Text, hoffentlich trotzdem verständlich. Willst du einen
Timeout haben, dann sagst du das der Systemuhr und programmierst deine
Menübedienung ereignisgesteuert und eben nicht blockierend.
W.S.
Dein erster Ansatz ist schon sehr, sehr gut. Wenn Du genügend Rechenzeit
hast, die 100ms immer einzuhalten.
Falls nein, dann den "System-Ticker" einführen. Eine ISR im 1ms oder
10ms-Takt, die den SysTicker hochzählt. Soweit ist Konsens in den
Antworten.
Ab da gibt es mehrere Fallstricke und Ideologien
a) Beim Vergleichen von aktueller Zeit und Endzeit immer erst die
Differenz bilden: if((SysTicker-Startzeit)>300) ...
Niemals: if(SysTicker>Startzeit+300) ...
--> Am Ende funktionieren es trotzdem nicht. Wenn es eben geht, würde
ich bei einem ms-Ticker alle 20 Tage einen Geräte-Reset erzwingen. Die
allermeisten "und nach Drei Wochen (6Wochen/42Tage) gehen auf einmal
...."-Fehler gehen auf durchgegangene Überläufe 32-Bit-ms.
b) Events vs. brotlose Timer
Ich kann immer nur brotlose Timer empfehlen, die ein Satz (optionaler)
Informationen kapseln und zur Laufzeit der Statemaschine abgefragt
werden:
- Endzeit (oder Startzeit
- Reload-Zeit
- ein paar Flags (ob gestartet, ob retriggerbar, ggf. eine Funktion,
...)
Brotlos genannt werden die Timer, da sie kein Brot fressen (keine
Laufzeit kosten), wenn sie 3 Tage nicht aufgerufen werden. Auch brauche
sie abgesehen vom Sys-Ticker keine zentralen Resourcen, können auch
static in einem Unter-Unter-Unterblock existieren oder gar auf dem
Stack.
a) ein oder mehrer Ticker:
Ich nutze immer nur einen SysTicker und vergleiche Manche zählen mehrere
Timer in der ISR rauf (oder runter).
Bei einem einzigen SysTicker errechnet man ei:
[c]
/* Timer setzen */
int
Achim S. schrieb:> würde> ich bei einem ms-Ticker alle 20 Tage einen Geräte-Reset erzwingen
Wo du recht hast: Überlauf ist ein Problem. Dafür gibt es nach meinem
obigen Konzept 2 Lösungen:
1. Man zählt nicht weiter, wenn der Timer die gewünschte Zahl (Zeit)
erreicht hat. Ist fehleranfällig, wenn man die Zeit ändern will, die
Verwendung von definierten Konstanten ist obligatorisch.
2. Bei jedem Tastendruck wird der Timer auf die gewünschte Zahl gesetzt
und man zählt abwärts bis 0, das ist der Wert bei dem ins Hauptmenü
zurückgesprungen wird. Man muss aber auch hier dafür sorgen, dass bei 0
nicht weiter dekrementiert wird.
Achtung: ich habe KEINEN Systemtick vorgeschlagen, sondern eine
Zählvariable extra und ausschliesslich für diesen Zweck. In der
Timerroutine kann man durchaus einige solche Zähler bearbeiten, falls
man anderweitig noch Timeouts braucht. Damit entfällt auch jede
Timetick-Arithmetik. Ich brauche nie mehr als 5 solche Timeoutvariablen.
Georg
Achim S. schrieb:> Wenn es eben geht, würde> ich bei einem ms-Ticker alle 20 Tage einen Geräte-Reset erzwingen.
So einen Bullshit sollte man ganz schnell wieder vergessen.
Geräte müssen durchlaufen können, ohne jemals ein Reset zu benötigen.
Wenn man das Konzept der Differenzbildung einmal verstanden hat,
schreibt man sich ne Funktion dafür.
Oder man nimmt einen Scheduler, der die gewünschte Zeitverzögerung
abwärts zählt und dann die Task aufruft.
Einen Scheduler finde ich sehr bequem. Man stellt eine Timeout-Task
hinein und löscht sie wieder, wenn die zu überwachende Task vor Ablauf
beendet wurde.
Peter D. schrieb:> Wenn man das Konzept der Differenzbildung einmal verstanden hat
Eben - bei der Differenz zweier jeweils x Bit breiter Zahlen (Edit: mit
ebenso x Bit breitem Ergebnis) gibt es kein Überlaufproblem, es sei
denn, die Differenz selbst (hier: Delay-Zeit) überschreitet die halbe
Periodendauer des Überlaufs.
Wenn man nie Zeiten messen will, die länger als eine halbe Minute sind,
reicht es also, bei einem Millisekunden-Ticker mit 16 Bit Variablen zu
arbeiten.
Peter D. schrieb:> So einen Bullshit sollte man ganz schnell wieder vergessen.> Geräte müssen durchlaufen können, ohne jemals ein Reset zu benötigen.
LOL.
Genau.
Was ich aber nicht verstehe:
Wieso versuchen manche immer wieder mit vielen Worten nichts zu
sagen und dabei den Eindruck zu erwecken, als wüssten sie auch
noch wovon sie reden ?
Timeout ohne Systick ist Blödsinn, da blockierend - ein Timer dafür
verstehst sich von selbst.
Und ein Timeout braucht keine Differenz, da wird einfach eine
Variable dekrementiert.
Wenn ich einen Timeout brauche, dann wird die Variable auf diesen
Wert gesetzt, ein Flag auch und in der ISR wird geprüft, ob der
Flag gesetzt ist, falls ja, wird die Variable um 1 dekrementiert.
Wenn Variable==0, wird der Überlaufflag für entspr. Timeout gesetzt
und das wars dann.
1
#define CHK_BIT(var,pos) ((var) & (1<<(pos)))
2
#define SET_BIT(var,pos) ((var) |= (1<<(pos)))
3
#define RES_BIT(var,pos) ((var) &= ~(1<<(pos)))
4
5
// Die oberen 4 bits starten entspr. Counter
6
constmnuOn=7;
7
consttstOn=6;
8
constEvtOn=5;
9
constWasAuchImmerOn=4;
10
// Die unteren 4 bits werden gesetzt wenn Zeit abgelaufen ist
/* in der main() soll 10sec auf Taster gewartet werden - kann auch
20
als Routine geschrieben werden: StartTimer(tstOn, 10000) oder so... */
21
TastCnt=10000;
22
SET_BIT(MyFlag,tstOn);
23
24
//es wird auf Ablauf der Tasterzeit geprüft...
25
if(CHK_BIT(MyFlag,tstRdy){
26
RES_BIT(MyFlag,tstRdy);
27
...
28
}
29
30
/* in der ISR wird die Variable nur dekrementiert, bzw.
31
geprüft, wenn > 0 und entspr. Flag gesetzt ist... */
32
if(CHK_BIT(MyFlag,tstOn)&&(TastCnt>0)){
33
TastCnt--;
34
if(TastCnt<1){
35
SET_BIT(MyFlag,tstRdy);
36
RES_BIT(MyFlag,tstOn);
37
}
38
}
P.S.
Apropos Reset - da wir mit Industriesteuerungen zu tun haben:
Ein Systest (inkl. gewolltem Reset mittendrin um Stromausfall zu
simulieren) kann schon nützlich sein...
Peter D. schrieb:> Wenn man das Konzept der Differenzbildung einmal verstanden hat
ich habe das (wie Du lesen kannst) verstanden und beschrieben.
Trotzdem wirst Du bei größeren Projekten immer ein paar faule Stellen
haben. Entweder weil mehrere Leute daran arbeiten (nicht nur Du und ich)
oder weil es nicht so offensichtlich ist.
Die meisten Fehler sind so selten, dass sie trotzdem nicht
reproduzierbar auftreten. Schlimm ist es, wenn nach einiger Zeit Türen
nicht mehr zu gehen oder dergleichen.
Und die wenigsten Geräte müssen durchlaufen. Reset heisst ja nicht, dass
man Uhrzeit oder Parameter verliert. Oftmals gibt es mehrere Phasen pro
Tag, an denen ein kurzer Reset problemlos möglich wäre. Ab Tag 10 nutzt
man dann halt eine davon.
Marc V. schrieb:> da wird einfach eine Variable dekrementiert.> Wenn ich einen Timeout brauche, dann wird die Variable auf diesen> Wert gesetzt, ein Flag auch und in der ISR wird geprüft, ob der> Flag gesetzt ist, falls ja, wird die Variable um 1 dekrementiert.> Wenn Variable==0, wird der Überlaufflag für entspr. Timeout gesetzt> und das wars dann.
Der Weg ist zwar Überlauf-sauber, hat aber folgende Nachteile:
- feste Rechenzeiten je Tick und Timer notwendig (was andere Probleme
nach sich zieht)
- Eingriff in die ISR (relativ tiefer Eingriff in untere Schichten)
- asynchrone Schreibzugriffe (also Locks erforderlich)
Je nach Aufgabe, Menge der Zeitüberwachungen und SW-Architektur ist
dieser Ansatz problematisch (und wäre es bei uns, aus jedem der 3
Gründe).
Zur Info: Beim free running SysTicker-Ansatz muss nur das einmalige
Lesen atomar erfolgen. Dies ist bei den meisten µC für bis zu
32Bit-Variablen problemlos möglich ohne weitere Lock-Mechanismen..
Das meinte ich mit (offensichtlich zu Recht) mit
> Ab da gibt es mehrere Fallstricke und Ideologien
Achim S. schrieb:> Und die wenigsten Geräte müssen durchlaufen. Reset heisst ja nicht, dass> man Uhrzeit oder Parameter verliert. Oftmals gibt es mehrere Phasen pro
Reset heisst, dass der Anfangszustand unbekannt ist.
Das schliesst alle Parameter die sich im RAM befinden, ein.
Sich darauf zu verlassen, dass irgendwelche Parameter beim Reset
nicht verlorengegangen sind, wäre ein bisschen leichtsinnig, oder ?
Achim S. schrieb:> Entweder weil mehrere Leute daran arbeiten
Willst du damit sagen, daß der TO, der seine erste Menuesteuerung
realisieren will, eine multiple Persönlichkeit ist?
MfG Klaus
Achim S. schrieb:> Der Weg ist zwar Überlauf-sauber, hat aber folgende Nachteile:> - feste Rechenzeiten je Tick und Timer notwendig (was andere Probleme> nach sich zieht)
Bin nicht sicher, dass ich dich richtig verstanden habe aber nein,
fest steht nur, dass ein Flag abgefragt werden muss und das ist von
der Anzahl der Takte her kaum merkbar.
Solltest du es andersrum gemeint haben - feste Rechenzeiten gibt es
kaum - Ausführungszeiten schon - wozu das auch immer in einer Timer
ISR gut sein sollte...
> - Eingriff in die ISR (relativ tiefer Eingriff in untere Schichten)
Wenn man mit SysTick und Timer-ISR arbeitet, muss man sowieso in der
ISR rumwurschteln. Ohne Timer-ISR hat das Ganze auch wenig Sinn.
> - asynchrone Schreibzugriffe (also Locks erforderlich)
Nein, ISR kann nicht unterbrochen werden (oder es kann verhindert
werden) und solange der entsprechende Flag nicht gesetzt ist, fummelt
die ISR auch nicht am Cnt rum - und wenn der Flag dann gesetzt wird,
fummelt die main() aber nicht mehr daran ;-)
Der Schreibvorgang selbst kann natürlich atomar erfolgen, muss aber
nicht, da zuerst der Wert gesetzt wird, erst danach der Flag.
Und dieser Flag wird dann gleich in der ISR zurückgesetzt, nicht in
main(), es kann also in der ISR nichts mehr an Cnt Variable verändert
werden.
Moin Leute,
vielen Dank für die Anregungen!
Also von allen Antworten picke ich mir mal raus, wie ich das nun
umsetzen würde:
In der Timer ISR (10mS) wird ein Counter immer um 1 dekrementiert und
wenn (counter < 1) ist, wird nicht mehr dekrementiert.
In meiner Statemaschine (bei den Konfigurationssachen) prüf ich ob
(counter < 1), wenn das zutrifft spring ich in den State "Hauptmenü".
Der Counter wird immer auf 3000 gesetzt, wenn eine Taste gedrückt wird.
Würde hier also kein Flag für dieses Timeout verwenden.
Ich würde bei den counter int16_t verwenden, falls irgendwo doch nochmal
eine 0 dekrementiert wird. (Sollte aber nicht passieren (aber vorsicht
lieber als nachsicht :D))
Die Timer ISR würde ich dann noch für mehrere Counter Sachen verwenden.
Brauche z.B. noch ein Event für alle 3,5 Sekunden. Dafür würde ich aber
dann doch ein Flag setzen :D
Und ich würde die Statemaschine nicht alle 10mS durchlaufen lassen,
sondern ständig in der Mainloop .
Ich hoffe das wäre so gut umgesetzt und habe es richtig verstanden.
Das mit den addieren/subtrahieren der Systemticker ist MIR zu
gefährlich wegen den genannten Überlauf Problem was ihr ja diskutiert
(habt).
Hat jemand noch Tipp zur Display Aktualisierung?
Soll ich in der Statemaschine immer ein Flag setzen das eine Display
Aktualisierung fällig ist und wenn es aktualisiert worden ist das Flag
rücksetzen? Oder geht das auch eleganter?
Btw.:
Klaus schrieb:> Willst du damit sagen, daß der TO, der seine erste Menuesteuerung> realisieren will, eine multiple Persönlichkeit ist?
Noch bin ich übrigens nicht schizophren :D
Wolfsente schrieb:> Hat jemand noch Tipp zur Display Aktualisierung?> Soll ich in der Statemaschine immer ein Flag setzen das eine Display> Aktualisierung fällig ist und wenn es aktualisiert worden ist das Flag> rücksetzen? Oder geht das auch eleganter?
Nein, das ist so weder elegant noch clever.
Es setzt voraus, dass du deinen kompleten Displayinhalt im RAM
aufbewahrst.
Das mag bei einem 2x16 LCD noch nicht so schlimm sein, aber bei 8x40
oder gar 16x40 macht das schon keinen Sinn mehr - ausser viel mehr
benötigtem RAM wird die Geschwindigkeit auch zu einem Problem.
Und ob du einen Flag für DisplayRefresh setzst oder gleich die
Routine zum Refreshen aufrufst ist vom Standpunkt der zeitlichen
Ausführung absolut egal
Ob du aber nur 4 Digits für einen Wert neu schreiben musst oder gleich
320 oder 640 Zeichen weil dein ganzes Display neu gezeichnet werden
muss - das kann niemals egal sein.
Wolfsente schrieb:> Also von allen Antworten picke ich mir mal raus, wie ich das nun> umsetzen würde:> In der Timer ISR (10mS) wird ein Counter immer um 1 dekrementiert und> wenn (counter < 1) ist, wird nicht mehr dekrementiert.
Ja, kommt mir irgendwie bekannt vor...
> In meiner Statemaschine (bei den Konfigurationssachen) prüf ich ob> (counter < 1), wenn das zutrifft spring ich in den State "Hauptmenü".
Natürlich nicht, die ISR prüft das und setzt den entspechenden Flag.
> Der Counter wird immer auf 3000 gesetzt, wenn eine Taste gedrückt wird.> Würde hier also kein Flag für dieses Timeout verwenden.
Und damit hast du schon alle Vorteile die mein Vorschlag bietet aus
dem Fenster geworfen...
> Ich würde bei den counter int16_t verwenden, falls irgendwo doch nochmal> eine 0 dekrementiert wird. (Sollte aber nicht passieren (aber vorsicht> lieber als nachsicht :D))
Kommt mir auch sehr bekannt vor...
> Die Timer ISR würde ich dann noch für mehrere Counter Sachen verwenden.> Brauche z.B. noch ein Event für alle 3,5 Sekunden. Dafür würde ich aber> dann doch ein Flag setzen :D
Ja kommt mir auch sehr bekannt vor...
> Und ich würde die Statemaschine nicht alle 10mS durchlaufen lassen,> sondern ständig in der Mainloop .
Die Statemachine wird sowieso in einer while schleife durchlaufen...
P.S.
Nur aus Neugierde:
Was hast du denn aus anderen Antworten genau übernommen ?
Achim S. schrieb:> - asynchrone Schreibzugriffe (also Locks erforderlich)
Dass hält genauerem Nachdenken nicht stand. Z.B. kann die ISR ja nicht
vom Hauptprogramm unterbrochen werden, also ist da Read-Modify-Write
safe.
Und im Hauptprogramm gibt es kein Read-Modify-Write, sondern nur Lesen
oder nur Schreiben, und das sind bei 8 oder 16 bit Variablen fast immer
atomare Zugriffe, da kann auch nichts passieren. Notfalls könnte man ja
Enable/Disable Interrupt verwenden, aber auch das ist nur in seltenen
Sonderfällen notwendig. Aber toll wenn man den Chef mit Buzz-Wörtern wie
"notwendige Locks" beeindrucken kann.
Dass feste Rechenzeiten notwendig wären ist sowieso nur totaler
Blödsinn.
Aber von deinem Standpunkt hast du schon recht, wenn du die Software so
kompliziert machst, dass sie ausser dir keiner versteht, sicherst du
deinen Arbeitsplatz - zumindest wenn dein Chef keine Ahnung von der
Sache hat. Ich arbeite seit Jahrzehnten bei Timeouts mit weniger als 10%
deiner Software, bei absolut zuverlässiger Funktion, aber ich habe ja
auch keinen Chef.
Georg
Sobald man sich nicht sicher ist, kapselt man einen Mainzugriff auf eine
Interruptvariable atomar. Für den AVR gibt es dazu die "atomic.h".
Auch ein nur Lesen oder nur Schreiben von 16Bit muß auf nem 8Bitter
atomar gekapselt werden.
Es gibt beim AVR auch noch notwendige atomare Kapselung, die nicht
offensichtlich ist.
Z.B. bei den Timern wird für das High-Byte ein gemeinsames temporäres
Register verwendet. D.h. wenn z.B. ein Interrupt das ICR1 ausliest, muß
das Main das Setzen des OCR1A atomar kapseln.
Georg schrieb:> Dass hält genauerem Nachdenken nicht stand. Z.B. kann die ISR ja nicht> vom Hauptprogramm unterbrochen werden, also ist da Read-Modify-Write> safe.
Du wirst bei der Implementierung in ISR entweder den neuen Timerwert
und/oder das Kontrollflag in der Main-Routine UND im ISR setzen. Bsp:
Timerwert in Main auf 300 setzen, in jeder ISR dekrementieren. Es gibt
dabei zuweilen atomare Möglichkeiten, aber nicht immer und nicht immer
denkt jeder daran.
> Dass feste Rechenzeiten notwendig wären ist sowieso nur totaler> Blödsinn.
Wenn Du 100 Zeitüberwachungen hast (Steuerungstechnik, parallele
Module/Prozesse), dann müssen diese alle und in jedem ISR-Durchlauf
geprüft und ggf. decrementiert werden. Bei zwei oder 5 spielt es *keine
Rolle* (!).
> Ich arbeite seit Jahrzehnten bei Timeouts mit weniger als 10%> deiner Software, bei absolut zuverlässiger Funktion, aber ich habe ja> auch keinen Chef.
Bei größeren Projekten sind brotlose Timer eleganter und effizienter.
Habe ich auch nicht geglaubt, bis ich entsprechende Software gesehen
habe. Der Aufwand in Code insgesamt ist vergleichbar (falls Du das
meintest). Aber brotlose Timer bleiben im Modul gekapselt, die ISR weiss
davon nichts und muss es auch nicht.
Aber wie gesagt, bei 5 Timern ist jede Diskussion überflüssig. Dein
Ansatz hat kein Überlaufproblem und ist damit robust!
Achim S. schrieb:>> Dass feste Rechenzeiten notwendig wären ist sowieso nur totaler>> Blödsinn.> Wenn Du 100 Zeitüberwachungen hast (Steuerungstechnik, parallele> Module/Prozesse), dann müssen diese alle und in jedem ISR-Durchlauf
Wenn du 100 Zeitüberwachungen hast, stimmt meistens etwas nicht.
Und wenn man das alles auch noch in einer einzigen ISR abarbeiten
will - von welcher Steuerungstechnik redest du, wem willst du so
etwas andrehen ?
Entweder teilen sich mehrere uC die Aufgaben oder man fragt jemanden
aus der Branche wie so etwas normalerweise gelöst wird.
Übrigens, wie passt das zu deiner Aussage:
- Eingriff in die ISR (relativ tiefer Eingriff in untere Schichten)
Achim S. schrieb:> meintest). Aber brotlose Timer bleiben im Modul gekapselt, die ISR weiss> davon nichts und muss es auch nicht.
Auch diese Timer befinden sich in der ISR, werden nur durch
entsprechend
gesetzte Flags gestartet, angehalten, aktualisiert oder abgefragt, die
ISR weiss also sehr wohl davon - es wird nur keine Zeit verbraucht wenn
diese inaktiv sind...
Marc V. schrieb:> Wolfsente schrieb:>> Hat jemand noch Tipp zur Display Aktualisierung?>> Soll ich in der Statemaschine immer ein Flag setzen das eine Display>> Aktualisierung fällig ist und wenn es aktualisiert worden ist das Flag>> rücksetzen? Oder geht das auch eleganter?>> Nein, das ist so weder elegant noch clever.>> Es setzt voraus, dass du deinen kompleten Displayinhalt im RAM> aufbewahrst.> Das mag bei einem 2x16 LCD noch nicht so schlimm sein, aber bei 8x40> oder gar 16x40 macht das schon keinen Sinn mehr - ausser viel mehr> benötigtem RAM wird die Geschwindigkeit auch zu einem Problem.>> Und ob du einen Flag für DisplayRefresh setzst oder gleich die> Routine zum Refreshen aufrufst ist vom Standpunkt der zeitlichen> Ausführung absolut egal> Ob du aber nur 4 Digits für einen Wert neu schreiben musst oder gleich> 320 oder 640 Zeichen weil dein ganzes Display neu gezeichnet werden> muss - das kann niemals egal sein.>
Also einfach gleich den neuen Wert nur an der entsprechenden Stelle
aktualisieren?
> Ja, kommt mir irgendwie bekannt vor...> Kommt mir auch sehr bekannt vor...> Ja kommt mir auch sehr bekannt vor...>
Vllt liegts daran das auch vieles von dir kommt. Lag vllt daran das mir
das am besten gefallen hat und sauber klang.
Kommt so rüber als denkst du ich hätte mir das aus den Fingern gesogen,
aber wollte doch nur mitteilen wie ich das nur vorhabe...
>> Der Counter wird immer auf 3000 gesetzt, wenn eine Taste gedrückt wird.>> Würde hier also kein Flag für dieses Timeout verwenden.>> Und damit hast du schon alle Vorteile die mein Vorschlag bietet aus> dem Fenster geworfen...>
Siehe:
Wolfsente schrieb:> Ich hoffe das wäre so gut umgesetzt und habe es richtig verstanden.
Dann hab ich ja doch nicht alles so richtig verstanden :D
Was habe ich denn für ein Vorteil das ich ein Flag in der Main prüfe
anstatt den Counter selbst? Hat doch jeder sein eigenen Counter.
Wenn ich das richtig verstanden habe setzt du denn Counter ja auch bei
jedem Tastendruck.
Würde die Abfrage in der ISR so machen:
if(!(counter<0))
{
counter--;
}
Wofür die Flags?
Btw. die counter würden verständliche Namen kriegen.
Darfst dich gerne selber Zitieren wenn du das genau beschrieben hast
warum das besser ist. Ich finde es nicht :(
>> Und ich würde die Statemaschine nicht alle 10mS durchlaufen lassen,>> sondern ständig in der Mainloop .>> Die Statemachine wird sowieso in einer while schleife durchlaufen...
Hatte aber vor mit einem Flag sie nur alle 10mS auszuführen. Was ich
nicht mehr machen werde.
> P.S.> Nur aus Neugierde:> Was hast du denn aus anderen Antworten genau übernommen ?
Ich habe nicht auf die Namen der Antworten geachtet.
Wenn das meiste von dir kommt ist doch schön, das ich am meisten auf
dich gehört habe! :D
Aber anscheinend nicht alle Vorteile entdeckt habe.
Wolfsente schrieb:> Kommt so rüber als denkst du ich hätte mir das aus den Fingern gesogen,> aber wollte doch nur mitteilen wie ich das nur vorhabe...
LOL.
Nein, Georg hat auch so etwas ähnliches vorgeschlagen, deswegen.
> Was habe ich denn für ein Vorteil das ich ein Flag in der Main prüfe> anstatt den Counter selbst? Hat doch jeder sein eigenen Counter.
Weil der Zugriff auf diesen 16bit Counter ausserhalb der ISR nicht
atomar erfolgt, ein Flag ist aber meistens nur ein bit in einer 8bit
Variable.
> Würde die Abfrage in der ISR so machen:> if(!(counter<0))> {> counter--;> }
Wirklich ?
> Wofür die Flags?
a) Weil es übersichtlicher ist.
b) Weil es sicherer und schneller ist einen Byte zu prüfen als z.B.
einen 32bit Zähler.
c) Wenn sich alle Flags (bits) in einer Variable befinden, dann sagt
dir ein einfacher Test auf Null ob du überhaupt etwas laufen hast ;-)
Reicht das ?
> Also einfach gleich den neuen Wert nur an der entsprechenden Stelle> aktualisieren?
Ja.
Marc V. schrieb:> Achim S. schrieb:>> meintest). Aber brotlose Timer bleiben im Modul gekapselt, die ISR weiss>> davon nichts und muss es auch nicht.>> Auch diese Timer befinden sich in der ISR, werden nur durch entsprechend> gesetzte Flags gestartet, angehalten, aktualisiert oder abgefragt, die> ISR weiss also sehr wohl davon
Du verwechselst da etwas. Ich war einer derjenigen mit ausschließlich
einem SysTicker++ in der ISR.
Und der mit mehreren Timern da größere (Industrie-)Software. Kein
Arduino in einem Nachtlicht oder so.
Achim S. schrieb:> - feste Rechenzeiten je Tick und Timer notwendig (was andere Probleme> nach sich zieht)Achim S. schrieb:> Wenn Du 100 Zeitüberwachungen hast
Mal abgesehen davon, dass das in der Praxis ziemlich selten vorkommt,
redest du an deiner eigenen Forderung vorbei. Es ist klar, dass in einer
ISR alles so schnell wie möglich abgearbeitet werden muss, das ist
Anfängerwissen. Du hast aber feste Rechenzeiten verlangt, dafür gibt es
nicht den geringsten Grund, weder in der ISR noch im Hauptprogramm.
Georg
Achim S. schrieb:> Kein Arduino in einem Nachtlicht oder so.
Ich wäre aber an so etwas sehr interessiert.
Ist es möglich, ein fertiges Projekt mit Code und ausführlichen
Erklärungen (für uns Anfänger) zu kriegen ?
Marc V. schrieb:> Wolfsente schrieb:>> Kommt so rüber als denkst du ich hätte mir das aus den Fingern gesogen,>> aber wollte doch nur mitteilen wie ich das nur vorhabe...>> LOL.> Nein, Georg hat auch so etwas ähnliches vorgeschlagen, deswegen.
Sehr gut, dann hatte ich mich doch nicht ungünstig formuliert :D
Marc V. schrieb:>> Würde die Abfrage in der ISR so machen:>> if(!(counter<0))>> {>> counter--;>> }>> Wirklich ?
Sorry meinte counter<1
Ohne Flags zu setzen wäre das doch in Ordnung gewesen.
Marc V. schrieb:>> Wofür die Flags?>> a) Weil es übersichtlicher ist.
Wo ich jetzt über den Punkt richtig nachgedacht habe (mit deinem Punkt
c) zusammen) finde ich, hast du da recht.
Fand aber vorher aber nämlich..
(menueTimeOut<1)
übersichtlicher als
(CHK_BIT(MyFlag, tstOn))
> b) Weil es sicherer und schneller ist einen Byte zu prüfen als z.B.> einen 32bit Zähler.
Mh... ok... Da die ISR ja nur alle 10mS aufgerufen wird um zu
vergleichen, macht das Sinn.
> c) Wenn sich alle Flags (bits) in einer Variable befinden, dann sagt> dir ein einfacher Test auf Null ob du überhaupt etwas laufen hast ;-)>
Nagut hast mich ja überzeugt :D
Vielen Dank für deine Erklärungen!
Ich werde versuchen diesen "Stil" dann zu übernehmen.
Wolfsente schrieb:> Nagut hast mich ja überzeugt :D>> Vielen Dank für deine Erklärungen!> Ich werde versuchen diesen "Stil" dann zu übernehmen.
Solange du ein paar simple Grundregeln befolgst, kannst du es machen
wie es für dich am übersichtlichen ist.
Nur nicht vergessen, dass schon nach ein paar Wochen und erneutem
Blick aufs Programm die Frage kommt:
Welcher Idiot hat das geschrieben, warum ist das so und wo sind dieKommentare dazu ?
P.S.
Ich packe alle Flags zum Timer starten in einen Byte, alle Flags zum
Abfragen des Zustands in einen anderen.
Auf diese Weise kannst du in der ISR gleich die komplette Abfrage auf
Timerzustände mit if(MyFlag != 0) { ... } überspringen.
Macht schon was aus bei 5-8 Timern.
War unheimlich stolz damals als ich darauf gekommen bin...
Marc V. schrieb:> Achim S. schrieb:>> Und die wenigsten Geräte müssen durchlaufen. Reset heisst>> ja nicht, dass man Uhrzeit oder Parameter verliert.>> Reset heisst, dass der Anfangszustand unbekannt ist.
Das heisst es wohl beim Mikrocontroller. Bei der SPS - die
je offenbar nach (hier) herrschender Meinung hoffnungslos
veraltet und dem Mikrocontroller hoffnungslos unterlegen
ist - heisst es das nicht.
> Das schliesst alle Parameter die sich im RAM befinden,> ein.
Mal was von gestuetztem RAM gehoert?
> Sich darauf zu verlassen, dass irgendwelche Parameter> beim Reset nicht verlorengegangen sind, wäre ein bisschen> leichtsinnig, oder ?
Wieso sollte man sich denn NICHT darauf verlassen, dass
remanente Merker nach dem RESET noch da sind? Deswegen
heissen sie ja "remanent".
Marc V. schrieb:> Achim S. schrieb:>>> Dass feste Rechenzeiten notwendig wären ist sowieso nur>>> totaler Blödsinn.>> Wenn Du 100 Zeitüberwachungen hast (Steuerungstechnik,>> parallele Module/Prozesse), dann müssen diese alle und>> in jedem ISR-Durchlauf>> Wenn du 100 Zeitüberwachungen hast, stimmt meistens etwas> nicht.
Hier kann man exemplarisch studieren, was passiert, wenn
"total veraltete und unmoderne" SPS-Leute mit "modernen,
leistungsfaehigen, zukunftstraechtigen" µC-Leuten aneinander
vorbei reden.
In einer auch nur mittelgroszen Maschinensteuerung kommen
leicht dutzende bis hunderte "Zeitueberwachungen" vor.
Und eine SPS hat damit keine nennenswerten Probleme; das
schafft die in Echtzeit (=Laufzeitgarantie).
Die "Zeitueberwachungen" heissen im SPS-Slang uebrigens
"Timer" :)
> Und wenn man das alles auch noch in einer einzigen ISR> abarbeiten will - von welcher Steuerungstechnik redest> du, wem willst du so etwas andrehen ?
Das wiederum ist richtig. Eine klassische SPS macht fast
nichts im Interrupt - schon gar nicht das Pruefen der
Timeouts. Das wird in der Hauptschleife gemacht.
Possetitjel schrieb:>> Reset heisst, dass der Anfangszustand unbekannt ist.>> Das heisst es wohl beim Mikrocontroller. Bei der SPS - die> je offenbar nach (hier) herrschender Meinung hoffnungslos> veraltet und dem Mikrocontroller hoffnungslos unterlegen> ist - heisst es das nicht.
Hier ist aber nicht von PLC die Rede.
>> Das schliesst alle Parameter die sich im RAM befinden,>> ein.>> Mal was von gestuetztem RAM gehoert?
Ja, warum ?
> Wieso sollte man sich denn NICHT darauf verlassen, dass> remanente Merker nach dem RESET noch da sind? Deswegen> heissen sie ja "remanent".
Ich habe 5 Mal diese Seite mit Suchfunktion nach "SPS", "PLC",
"remanent" und "Marker" durchsucht.
Ausser in deinem Beitrag ist auf dieser Seite nichts davon zu finden.
Was wolltest du eigentlich sagen ?
Marc V. schrieb:> Possetitjel schrieb:>>> Reset heisst, dass der Anfangszustand unbekannt ist.>>>> Das heisst es wohl beim Mikrocontroller. Bei der SPS - die>> je offenbar nach (hier) herrschender Meinung hoffnungslos>> veraltet und dem Mikrocontroller hoffnungslos unterlegen>> ist - heisst es das nicht.>> Hier ist aber nicht von PLC die Rede.
Gewoehne Dir diesen Kasernenhof-Ton ab.
Du bestimmst nicht, wovon hier die Rede ist.
>> Wieso sollte man sich denn NICHT darauf verlassen, dass>> remanente Merker nach dem RESET noch da sind? Deswegen>> heissen sie ja "remanent".>> Ich habe 5 Mal diese Seite mit Suchfunktion nach "SPS",> "PLC", "remanent" und "Marker" durchsucht.> Ausser in deinem Beitrag ist auf dieser Seite nichts davon> zu finden.>> Was wolltest du eigentlich sagen ?
Das war ein Test. Ich habe Deine Faehigkeit zu mentalen
Transfer-Leistungen getestet.
Leider bist Du durchgefallen.
Denn Du hast weder erkannt, dass Achim offensichtlich einen
SPS-Hintergrund hat (was sich auch in seiner Wortwahl
niederschlaegt), noch, dass ich Dich subtil auf diese Tatsache
hinweisen wollte.
Marc V. schrieb:> Ich habe 5 Mal diese Seite mit Suchfunktion nach "SPS", "PLC",> "remanent" und "Marker" durchsucht.> Ausser in deinem Beitrag ist auf dieser Seite nichts davon zu finden.>> Was wolltest du eigentlich sagen ?
Er wollte sagen, dass die Methoden jeder SPS auch ihre Entsprechung und
µC-Programmen haben und dort genauso gewinnbringend genutzt werden
können, wenn es um echte Steuerungen und nichttriviale Aufgaben geht.
a) remanent = non volatile RAM (das beim erstmaligen Start eines
Programmes genullt wird, aber nicht bei jedem Reset. Ja, auch das geht
in C, **kopfschüttel**)
b) PLC: loop control, auch in µC-SW eine erfolgreiche Alternative zu
vielen Task-Konstrukten. In autosar "basic Task" genannt.
btw.: wer sich an "festen Rechenzeiten" störte: Hier ist der (konstante)
Aufwand für jeden einzelnen Timer-Decrement (oder Flag-Abfrage) in der
ISR gemeint. Dieser Aufwand ist bei 5 Timern (wie schon häufig gesagt)
vernachlässigbar. Schön ist die Vermischung von Modulen und ISR trotzdem
nicht.
Georg schrieb:> Achim S. schrieb:>> - asynchrone Schreibzugriffe (also Locks erforderlich)>> Dass hält genauerem Nachdenken nicht stand. Z.B. kann die> ISR ja nicht vom Hauptprogramm unterbrochen werden, also> ist da Read-Modify-Write safe.
In meinem Universum gibt es sowohl unterschiedliche
Interruptprioritaeten als auch nichtmaskierbare Interrupts.
In Deinem auch?
> Notfalls könnte man ja Enable/Disable Interrupt verwenden,> aber auch das ist nur in seltenen Sonderfällen notwendig.
Das waere dann genau der "notwendige Lock", von dem Achim
geredet hat.
> Aber toll wenn man den Chef mit Buzz-Wörtern wie> "notwendige Locks" beeindrucken kann.
Geht's auch etwas weniger abschaetzig? Besonders dann, wenn
Du (ungewollt) genau das bestaetigst, was Achim vor Dir
geschrieben hat?
> Dass feste Rechenzeiten notwendig wären ist sowieso nur> totaler Blödsinn.
Vielleicht TUST Du in Zukunft wenigstens mal so, als ob Du
VERSUCHEN wuerdest zu verstehen, was Dein Gegenueber gemeint
hat!?
Achim hat KRITISIERT, dass bei Deinem Vorschlag (je ein
Countdown-Zaehler pro Timeout) fuer jeden laufenden Countdown
Rechenzeit in der ISR verbraten wird.
Das ist bei der von ihm vorgeschlagenen Zeitstempel-Methode
nicht der Fall.
> Aber von deinem Standpunkt hast du schon recht, wenn du die> Software so kompliziert machst, dass sie ausser dir keiner> versteht, [...]
Einfach mal die Baelle flachhalten.
Es hat schon jeder gemerkt, dass Du Achim nicht verstehst; das
musst Du nicht noch besonders betonen.
Aber was Du nicht verstehst, muss deswegen noch lange nicht
falsch sein.
Possetitjel schrieb:> Es hat schon jeder gemerkt, dass Du Achim nicht verstehst; das> musst Du nicht noch besonders betonen.Possetitjel schrieb:> Denn Du hast weder erkannt, dass Achim offensichtlich einen> SPS-Hintergrund hat (was sich auch in seiner Wortwahl> niederschlaegt), noch, dass ich Dich subtil auf diese Tatsache> hinweisen wollte.
Macht ja nichts, Hauptsache du verstehst Achim sehr gut und
dir gefällt sein Hintergrund. Weiter so.
P.S.
Wer ist Junge und wer ist Mädchen ?