Forum: Mikrocontroller und Digitale Elektronik AVR: Watchdog löst keinen Interrupt aus


von StefanK (stefanka)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich glaube, mein Watchdog muss mal in die Hundeschule und ein bisschen 
nachsitzen. Er löst keinen Interrupt aus, obwohl (1<<WDIE) und sei().

Ich versuche meinem Arduino Uno R3 mit ATmega328P beizubringen, die LED 
nur dann ein- oder auszuschalten, wenn eine gewisse Anzahl WD-Timeouts 
erreicht ist. Zwischen den WD-Timeouts soll der uC im Power-down-Modus 
schlafen (macht er) und beim WD-Timeout wieder aufwachen. Wacht er aber 
nicht. Die Hauptschleife wird genau einmal durchlaufen. Danach liegt der 
uC im Koma.

Warum bellt der faule Hund nicht? Hat jemand einen Tipp?

von S. L. (sldt)


Lesenswert?

Auf die Schnelle (und ohne Garantie, ist schon länger her): bei mir lief 
(in Assembler)
1
 puti  WDTCSR,(1<<WDE)+(1<<WDCE)
2
 puti  WDTCSR,(0<<WDE)+(1<<WDIE)+(1<<WDP3)  ; 4 s, Interrupt-Mode

von Rolf (rolf22)


Lesenswert?

StefanK schrieb:
> Warum bellt der faule Hund nicht? Hat jemand einen Tipp?

Zeig den Code.

von StefanK (stefanka)


Lesenswert?

Sieht bei mir so aus. Eigentlich dasselbe, nur, dass ich noch WDIE 
setze, in der Hoffnung, dass der WD-Timeout auch einen Interrupt 
auslöst. Nur warum nicht?
WDTCSR |=  (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDIE) | (1<<WDCE) | (0<<WDE) | (1<<WDP2) | (1<<WDP1);

von StefanK (stefanka)


Lesenswert?

Rolf schrieb:
> Zeig den Code.
Habe ihn oben angehängt.

von S. L. (sldt)


Lesenswert?

> Eigentlich dasselbe

Und wenn Sie, dauert ja nur 30", meine Version einfach ausprobieren?

von StefanK (stefanka)


Lesenswert?

StefanK schrieb:
> WDTCSR |=  (1<<WDCE) | (1<<WDE);
> WDTCSR = (1<<WDIE) | (1<<WDCE) | (0<<WDE) | (1<<WDP2) | (1<<WDP1);

Korrektur:
...nur, dass ich noch WDCE  setze...

Beitrag #7999432 wurde vom Autor gelöscht.
von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> Und wenn Sie, dauert ja nur 30", meine Version einfach ausprobieren?

Mache ich, bin nur gerade nicht am richtigen PC.

von Oliver S. (oliverso)


Lesenswert?

Da wird wohl das erforderliche Timing nicht eingehalten werden. Dafür 
gibts passende Macros in der avrlibc, die das sicherstellen.

https://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html

Oliver

von Rolf (rolf22)


Lesenswert?

Was auf jeden Fall nicht ok ist:
-- wdt_ticks muss volatile sein

-- Es ist nicht gut, in der Hauptschleife wdt_ticks als Zählerstand 
abzufragen.
Besser: In der ISR einen bool setzen, wenn es soweit ist, und den in der 
Hauptschleife abfragen. Der Vergleich ==0 kann auch mal schief gehen.

Ansonsten: Die seriellen while-Sachen vorübergehend rauswerfen, um 
sicher zu sein, dass es da keine Endlosschleifen gibt.
Und: Die LED in die ISR einbauen, damit man sicher sehen kann, ob die 
ISR durchlaufen wird.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Warum bellt der faule Hund nicht?

Deine wdt ticks sollten atomar abgehandelt werden.
Auch eine Memory Barrier kann wunder bewirken..

Tipp:
Verwende die Atomic Makros aus atomic.h
https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
Die regeln das für dich.

von Mario M. (thelonging)


Lesenswert?

Aber selbst wenn wdt_ticks komplett weg optimiert würde, muss wenigstens 
der Text seriell ausgegeben werden.

von S. L. (sldt)


Angehängte Dateien:

Lesenswert?

StefanK schrieb:
> ...nur, dass ich noch WDCE  setze...

Gerade ausprobiert: das zweite Setzen von WDCE scheint keinen Einfluß zu 
haben.
  Also den Ratschlägen der anderen Teilnehmer folgen ...

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

Minimalprogramm nachgereicht. Ist in Assembler, vielleicht bringt es 
trotzdem  einen Erkenntnisgewinn.

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

Danke schon mal für die Anregungen! Ich sehe mir das mal genauer an.

LED in der ISR ansteuern:
Probiere ich zu debug-Zwecken gern mal aus. Sobald der WDT-timeout 
Mechanismus funktioniert soll im HP ein umfangreicher Code ausgeführt 
werden, gefühlt eher zuviel für die ISR.

Zu den uint16_t wdt_ticks:
Volatile probiere ich mal aus. Meinem Verständnis nach ist wdt_ticks 
eine globale Variable, auf die sowohl die WD-ISR, als auch das 
Hauptprogramm etc zugreifen können. Nur tun sie das erwartungsgemäß 
nicht gleichzeitig,  denn der WDT sollte den Interrupt nur alle paar 
Sekunden auslösen. Bis dahin hat das Hauptprogramm alles erledigt, 
längst geschlafen und wertet wdt_ticks an der Stelle des Aufwachens 
nicht aus. Nach dem Aufwachen muss das HP ohnehin noch auf die 
Abarbeitung der ISR warten, bevor es dran ist. So sollte es keine 
Konflikte zwischen ISR und HP geben.

Zu atomar:
Das ist sinnvoll, wenn es mehrere Interrupts gibt und man nicht weiß 
wann einer auftritt, das stimmt. Im Moment gibt es nur den WD-Interrupt. 
Es sei denn, dass das Arduino spezifische Serial.print UART-Interrupts 
im Gepäck hat. Aber auch die serielle Übertragung ist in sehr viel 
kürzerer Zeit (<1ms) und längst erledigt bevor der WDT-timeout im 
Sekundenbereich wieder erwartet wird.

Die while (UCSR0A...) Schleifen:
Bewirken nur, dass die Übertragung nicht bereits nach dem ersten 
übertragenen Zeichen vom Powerdown Schlaf abgewürgt werden. Das ist eh 
nur zum debuggen und fliegt wieder raus, sobald der WDT Mechanismus 
funktioniert.

In der WD-ISR ein Flag setzen:
Generell eine gute Idee und häufige Praxis. Mir ist in meinem Fall nur 
nicht wirklich klar, warum die Abfrage isr_flag == TRUE etwas 
grundsätzlich anderes sein soll als wdt_ticks == 0?

von Mario M. (thelonging)


Lesenswert?

StefanK schrieb:
> Meinem Verständnis nach ist wdt_ticks eine globale Variable, auf die
> sowohl die WD-ISR, als auch das Hauptprogramm etc zugreifen können.

Aber der Optimizer weiß nicht, dass die Variable außerhalb der Main-Loop 
geändert wird. So kann es sein, dass sie ganz weg optimiert wird oder in 
einem Register zwischengespeichert und nie wieder gelesen wird.

Ich kann zwischen dem Programm und den zahlreichen Beispielen im Netz 
keinen wirklichen Unterschied erkennen. Deswegen stellt sich mir die 
Frage, wie das Programm übersetzt und hochgeladen wird.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> Gerade ausprobiert: das zweite Setzen von WDCE scheint keinen Einfluß zu haben.

Ja, hätte mich auch gewundert, ist höchstens überflüssig.

> Minimalprogramm nachgereicht. Ist in Assembler, vielleicht bringt es
> trotzdem  einen Erkenntnisgewinn.

Interessant am Minimalprogramm ist die Tatsache, dass WDCSR sofort nach 
sleep_enable gemacht wird.

reset:
    ldi     r16,(1<<SE)+(1<<SM1)                        ; power-down
    out     SMCR,r16
    ldi     r16,(1<<WDE)|(1<<WDCE)
    sts     WDTCSR,r16
    ldi     r16,(0<<WDE)|(1<<WDIE)|(1<<WDP2)|(1<<WDP1)  ; 1 s, 
Interrupt-Mode
    sts     WDTCSR,r16
    sbi     DDRB,0
    sei

Das ist ein Unterschied zu meinem Prog. Bin leider nicht so asm-fit, 
werde diese Sequenz aber mal so ausprobieren. Bei asm weiß man 
wenigstens was genau passiert. Bei den C-Funktionen sleep_enable(), 
sleep_mode(), sleep_cpu(), sleep_disable() etc. weiß ich nicht, was sich 
dahinter verbirgt.

von StefanK (stefanka)


Lesenswert?

Mario M. schrieb:
> Frage, wie das Programm übersetzt und hochgeladen wird.

Das Prog wird in der Arduino IDE2.3.7  übersetzt und (mit dem in die IDE 
integrierten AVR-dude) auf ein Arduino UNO R3 Board per USB hochgeladen.

Welche Optimierung eingestellt ist muss ich mal nachschauen. Ich tippe 
auf -Os.

von Björn W. (bwieck)


Lesenswert?

StefanK schrieb:
> Bei asm weiß man
> wenigstens was genau passiert.

Hört! Hört!

von Georg M. (g_m)


Lesenswert?

StefanK schrieb:
> Bei den C-Funktionen sleep_enable(),
> sleep_mode(), sleep_cpu(), sleep_disable() etc. weiß ich nicht, was sich
> dahinter verbirgt.

https://github.com/Patapom/Arduino/blob/master/Libraries/AVR%20Libc/avr-libc-2.0.0/include/avr/sleep.h

von StefanK (stefanka)


Lesenswert?

Mario M. schrieb:
> Aber selbst wenn wdt_ticks komplett weg optimiert würde, muss wenigstens
> der Text seriell ausgegeben werden.

Ich glaube nicht, dass wdt_ticks weg-optimiert wird, denn der Arduino 
sendet nach einem Neustart bzw Reset per Button über die Serial.print 
Funktionen einmalig folgenden Text:

wdt_ticks: 7

7 ist der Initialwert. Das zeigt, dass die Hauptschleife wenigstens 
genau einmal bis zum Sleep läuft.

Wäre auch ganz schön frech, würde wdt_ticks weg-optimiert...

Mache morgen weiter.

von Oliver S. (oliverso)


Lesenswert?

StefanK schrieb:
> Ich glaube nicht, dass wdt_ticks weg-optimiert wird, denn der Arduino
> sendet nach einem Neustart bzw Reset per Button über die Serial.print
> Funktionen einmalig folgenden Text:
>
> wdt_ticks: 7

Du unterschätzt die Compilerentwickler…
Eine einfache Konstante 7 auszugeben schaffen die tatsächlich mit 
komplett wegoptimierten Variablen.

Oliver

von Nick (b620ys)


Lesenswert?

StefanK schrieb:
> Wäre auch ganz schön frech, würde wdt_ticks weg-optimiert...

Noch frecher wäre es, wenn er das volatile ignorieren würde.

Du kannst ja mal, frecherweise, die Compiler-Optimierung auf den 
niedrigsten Level setzen. Könnte gut sein, dass es dann geht.

von S. L. (sldt)


Lesenswert?

an StefanK:

Vielleicht mache ich mich jetzt zum Affen, mit meinen rudimentären 
C-Kenntnissen, aber egal - kann es sein, dass Sie nur 65536-7+3 Sekunden 
warten müssen?

von Frank D. (Firma: LAPD) (frank_s634)


Lesenswert?

StefanK schrieb:
> Warum bellt der faule Hund nicht? Hat jemand einen Tipp?
Als ich vor Jahren mal die sleep_*() nutze funktionierten die nicht 
richtig, Bits von hand gesetzt funktionierte, vielleicht sind die immer 
noch kaputt.

von Georg M. (g_m)


Lesenswert?


von StefanK (stefanka)


Angehängte Dateien:

Lesenswert?

Leute, es läuft! Danke für die vielen hilfreichen Tipps!

Die WD-Konfiguration und sleep Sequenz waren schon richtig. Die von S.L. 
vorgeschlagene prägnante asm sleep-Sequenz probiere ich noch aus.

LED testweise in der ISR hat gezeigt, dass die WD-ISR wie erwartet 
kommt.

volatile rockt! Man kann nicht vorsichtig oder misstrauisch genug sein 
;-) Krass die Optimierung!

Die zweite while(!(UCSR0A & (1<<RXC0)) war endlos. Weg damit. Die erste 
führt wenigstens dazu, dass Serial.print eine halbwegs lesbare 
Rückmeldung liefert.

Das führt mich zu einer weiteren Frage: Wie bekomme ich es hin, dass die 
Serial.print Funktion vor dem Betreten des Schlafs ordentlich 
abgearbeitet wird? Ich brauche sie zum debuggen. Wie kann ich ihr die 
benötigte Zeit verschaffen?

von S. L. (sldt)


Lesenswert?

> Wie bekomme ich es hin ...

Abfrage auf TXC0?

von StefanK (stefanka)


Lesenswert?

Frank D. schrieb:
> richtig, Bits von hand gesetzt funktionierte, vielleicht sind die immer
> noch kaputt.

Guter Hinweis. Das probiere ich als nächstes anhand des Beispiels von 
S.L. aus. Dann weiß man genau was passiert.

@Georg M. (g_m)
An Wolle habe ich mich orientiert. Seine WD-Konfiguration und sleep_*() 
Sequenz funktionieren.

@ S.L. TXC0 wird gleich ausprobiert. Die while Schleife gibt's ja schon 
;-)

von Mario M. (thelonging)


Lesenswert?

StefanK schrieb:
> Wie bekomme ich es hin, dass die
> Serial.print Funktion vor dem Betreten des Schlafs ordentlich
> abgearbeitet wird?

Serial.flush();

von Oliver S. (oliverso)


Lesenswert?

StefanK schrieb:
> volatile rockt! Man kann nicht vorsichtig oder misstrauisch genug sein
> ;-) Krass die Optimierung!

Das hat weder was mit Vorsicht noch mit Mißtrauen zu tun, sondern eher 
mit „know your tools“ (hier: C/C++), und fällt unter die Rubrik „kaum 
macht mans richtig, schon funktionierts“.

Oliver

von Arduino F. (Firma: Gast) (arduinof)


Angehängte Dateien:

Lesenswert?

StefanK schrieb:
> Das führt mich zu einer weiteren Frage: Wie bekomme ich es hin, dass die
> Serial.print Funktion vor dem Betreten des Schlafs ordentlich
> abgearbeitet wird? Ich brauche sie zum debuggen. Wie kann ich ihr die
> benötigte Zeit verschaffen?
Habe dein Programm mal "vereinfacht"


StefanK schrieb:
> volatile rockt!
Ist hier nicht nötig.
Siehe Anhang.

von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> volatile rockt!

Ist eigentlich das falsche Werkzeug für den Zweck, aber funktioniert.
Und es ist einfacher, einem Anfänger ein "Schreib halt volatile davor" 
hinzuwerfen, als Seitenweise Erklärungen zu atomarem Variablenzugriff, 
Memory- und Optimization Barriers usw. zu schreiben, die dann sowieso 
nicht verstanden werden.

Vorschlaghammer passt immer, egal ob das jetzt eine Schlitz-, Phillips-, 
oder Pozidriv-Schraube ist.

Arduino F. schrieb:
> Ist hier nicht nötig.
> Siehe Anhang.

Was ich dann zum weiteren Stromsparen oft noch drinhabe:
1
void yield() {
2
    set_sleep_mode(SLEEP_MODE_IDLE);
3
    sleep_mode();
4
}
Das überschreibt eine "weak" Funktion aus dem Framework, und längere 
delay() sparen automatisch Strom, werden aber auch ein bischen 
ungenauer, weil die erst mit dem nächsten IRQ beendet werden. Da aber im 
IDLE der Timer für millis() weiterläuft, ist der IRQ nicht soweit weg.

Damit geht dann auch das Busy-Waiting stromsparend:
1
 while(bufferSize > Serial.availableForWrite()) yield();

von Rolf (rolf22)


Lesenswert?

StefanK schrieb:
> volatile rockt! Man kann nicht vorsichtig oder misstrauisch genug sein
> ;-) Krass die Optimierung!

Ich nenne diese Strategie "defensive Codierung", zahlt sich immer aus. 
Man spart damit viel Zeit für unnötige Fehlersuche. Wenn das Programm 
einmal läuft, kann man immer noch Dinge weglassen/verbessern, um 
irgendwo 3 Byte oder 2 Mikrosekunden einzusparen.

> In der WD-ISR ein Flag setzen:
> Mir ist in meinem Fall nur nicht wirklich klar, warum die Abfrage isr_flag
> == TRUE etwas grundsätzlich anderes sein soll als wdt_ticks == 0?

Es ist grundsätzlich unsauberer Code. Auch dann, wenn es in diesem Fall 
funktioniert. Morgen ändert du irgendwas anderes und schon rächt sich 
die Schlamperei.
Wenn du den Zustand eines Zählers abfragst, und der sich per Interrupt 
oder DMA zu beliebigen Zeiten ändern kann, was kann dann passieren? Der 
Zähler erreicht irgendwann den betreffenden Zustand, aber bevor du das 
gemerkt hast, ist er womöglich schon einen Tick weiter und die Abfrage 
mit ==  geht schief.

Nebenbei: Dein µP ist ein 8-Bit-µP, für wdt_ticks hast du aber 16 bit 
genommen. Also sind z. B. für wdt_ticks++ zwei Operationen nötig: Erst 
unteres Byte inkrementieren, und im Falle eines Überlaufs dann das 
obere. Wenn zwischen diesen beiden Operationen ein Interrupt kommt und 
auf die Variable zugreift, sieht er einen falschen Wert (unteres Byte 
schon neu, oberes Byte noch alt).
In deinem Beispiel kommt das nicht vor, aber auch so etwas kann zu 
schwer erkennbaren Fehlern führen. Deswegen: Bei allen Variablen, die in 
ISRs vorkommen, dreimal hinsehen, ob der Code sauber ist.

: Bearbeitet durch User
von StefanK (stefanka)



Lesenswert?

S. L. schrieb:
> Abfrage auf TXC0?

Es geht. Mit while(!(UCSR0A & (1<<TXC0)) läuft die Serial.print Funktio 
n bis zum Ende.

@Rolf (rolf22)

Ich verstehe genau was Sie meinen und gebe Ihnen völlig recht. Ein Flag 
mit handshake ist der sichere Weg und gute, saubere Praxis, vor allem 
bei mehreren Interrupts oder Multitaskingprozessen. Hier gibt es ja nur 
einen, den WD-ISR alle 8s, der selbst nicht unterbrochen wird. Unter 
anderen Umständen hätte ich es natürlich anders gemacht.

@Arduino F.

Danke für das ATOMIC_BLOCK Beispiel. Ich sehe es mir genau an. Dass ich 
den Vorschlag ATOMIC bisher nicht berücksichtigt habe liegt zum einen an 
den oben genannten Gründen (nur ein Interrupt) und and der Tatsache, 
dass ich nicht erwartet habe, dass der Optimierer eine globale Variable 
weg optimiert, die an 2 Stellen - im HP und in der ISR - verwendet wird?

Wieso weiß der Optimierer nicht, dass eine Variable, die im HP gelesen, 
außerhalb des HP geändert wird, obwohl es offensichtlich ist?
Da liegt das Problem doch eher beim Optimierer als beim Entwickler der 
von solchen Tücken nichts ahnt. Es gibt ja nicht Mal eine Warnung.

von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> Wieso weiß der Optimierer nicht, dass eine Variable, die im HP gelesen,
> außerhalb des HP geändert wird, obwohl es offensichtlich ist?

Weil der Optimierer nicht sehen kann, wann und wie (und ob überhaupt) 
die ISR aufgerufen wird. Das macht nachher der Linker, der die 
Vektortabelle befüllt. Da ist's für den Compiler/Optimierer aber schon 
zu spät.

Deshalb gibt's "memory/optimization barriers" als Hinweis an den 
Compiler: "Schau mal lieber frisch im RAM nach, die Variable könnte sich 
geändert haben".

Die sind praktischerweise in cli()/sei() bzw. dem ATOMIC_BLOCK schon 
eingebaut.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

StefanK schrieb:
> Wieso weiß der Optimierer nicht, dass eine Variable, die im HP gelesen,
> außerhalb des HP geändert wird, obwohl es offensichtlich ist?

Der Compiler implementiert die Sprache C++ genau so, wie diese definiert 
ist. Und die Sprachdefinition setzt auf einem Modell auf, daß solche 
„Nebenkriegsschauplätze“ wie Interruptroutinen gar nicht vorsieht. Der 
Compiler darf davon ausgehen, daß er den gesamten Programmfluß sieht, 
und er kann und darf darin alles rauswerfen, was nichts erkennbares tut. 
Dazu gehören Variablen, die sich niemals ändern.

StefanK schrieb:
> Da liegt das Problem doch eher beim Optimierer als beim Entwickler der
> von solchen Tücken nichts ahnt. Es gibt ja nicht Mal eine Warnung.

Auch wenns nicht jeder hören will: programmieren lernt man nur teilweise 
durch teilweise and error.
Und auf dieses Problem stößt jeder Programmieranfänger seit Jahrzehnten, 
der mit Mikrocontrollern rumspielt. Entsprechend vielfältig ist die 
Dokumentation dazu, die sich im Netz angesammelt hat.

Oliver

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

@Ernst B
Verstehe. Das Prinzip 'in dubio pro reo' gilt zum Zeitpunkt des 
Optimierens nicht, und dem Verdacht, die ISR könnte genutzt werden wird 
später nicht mehr nachgegangen.
Anstatt das zu verbessern, legt man die Existenzgrundlage für die 
Code-Checktool Entwickler ;-)

Wie sähe es denn aus, wenn in der ISR ein global definiertes handshake 
Flag gesetzt und im HP gelesen würde? Fiele das dem Optimierer dann auch 
sofort zum Opfer?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Fiele das dem Optimierer dann auch
> sofort zum Opfer?

Der Compiler versucht Variablen in Registern zu halten.
Die ISR ändert die Variable im Speicher.

Also muss man eine Memory Barrier setzen, damit der geänderte Wert 
wieder in Registern landet. Und umgekehrt.

Dazu: Der Zugriff auf solche Variablen muss atomar gestaltet werden.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> Verstehe. Das Prinzip 'in dubio pro reo' gilt zum Zeitpunkt des
> Optimierens nicht, und dem Verdacht, die ISR könnte genutzt werden wird
> später nicht mehr nachgegangen.

Weil's nicht geht, ohne komplett auf jede Art der Optimierung zu 
verzichten.

Compilieren und Linken sind zwei unterschiedliche Arbeitsschritte. Code 
kann in getrennten Source-Files liegen.
Und muss noch nichtmal auf demselben Rechner kompiliert worden sein.

Wenn ich dir ein vorkompiliertes Object-File mit der ISR schicke, und 
dein Compiler dein Hauptprogramm Kompilierst, und nachher linkst du 
beides zusammen, woher soll dein Compiler wissen, was in meiner Datei 
verändert wird? Oder "Verändert werden wird", wenn ich dir das File erst 
nächste Woche schicke?

Gedankenexperiment:
Wenn dein Compiler das könnte, machen wir einen Deal: Ich schick dir 
nächste Woche ein Object-File mit 6 Konstanten aus dem Wertebereich 
1-49.
Du teilst mir heute mit, welche Zahlen ich dann in dem File stehen haben 
werde.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Dass ich
> den Vorschlag ATOMIC bisher nicht berücksichtigt habe liegt zum einen an
> den oben genannten Gründen (nur ein Interrupt)
Ein Irrtum!
Serial verwendet ISR.
Und auch Timer0 für z.B. millis()

von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb im Beitrag
> Der Compiler implementiert die Sprache C++ genau so, wie diese definiert
> ist. Und die Sprachdefinition setzt auf einem Modell auf, daß solche
> „Nebenkriegsschauplätze“ wie Interruptroutinen gar nicht vorsieht.

Nun ja, wenn man (als Anfänger) seit Jahrzehnten auf dieselben Probleme 
stößt, wäre es dann nicht mal an der Zeit, dieselben zu beheben?

Wieso behandelt man eine ISR nicht wie eine erforderliche Routine auch 
wenn sie im Code nicht explizit aufgerufen wird?

Selbstverständlich lernt man aus solchen Fehlern. Natürlich kann man mit 
nicht idealen Umständen leben, auch wenn ich das schon etwas weniger 
selbstverständlich finde.

Wir verwenden ja schließlich den auf uC zugeschnittenen avr-gcc.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb im Beitrag
> Serial verwendet ISR.

Danke für die Bestätigung. Ich hatte es schon vermutet, aber in meinem 
Fall jedoch kein Risiko für Konflikte gesehen, da der WD-ISR im 
Sekundenbereich kommt und die serielle Kommunikation direkt nach dem 
abgelaufenen WD-ISR beginnt und lange vor dem Auftreten des nächsten 
WD-ISR erledigt ist. Inzwischen läuft mein Beispiel mit meiner Annahme.

Natürlich ist die Verwendung von ATOMIC_BLOCK die richtige Strategie für 
komplexere und weniger vorhersagbare Szenarien. Ich speichere Ihr 
Beispiel in meine Beispielsammlung. Vielen Dank nochmal dafür.

von Oliver S. (oliverso)


Lesenswert?

Εrnst B. schrieb:
> Weil's nicht geht, ohne komplett auf jede Art der Optimierung zu
> verzichten.

Natürlich ginge das alles.

Nochmal: es ist so, weil die Sprachdefinition so ist, wie sie ist. Und 
das schon seit dem letzten Jahrtausend.

StefanK schrieb:
> Natürlich ist die Verwendung von ATOMIC_BLOCK die richtige Strategie für
> komplexere und weniger vorhersagbare Szenarien.

ATOMIC_BLOCK ist genau für solche Fälle wie diesem hier gemacht, und das 
absolut vorhersehbar.

Oliver

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Inzwischen läuft mein Beispiel mit meiner Annahme.
Der Weg in die Hölle ist mit falschen Annahmen gepflastert.

Ich verstehe nicht, was dich davon abhält, es richtig zu machen, wenn du 
doch jetzt weißt wie und warum es richtig geht.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb
> Der Weg in die Hölle ist mit falschen Annahmen gepflastert.

Ich mache es! Ich will ja nicht in die Hölle!!!

von Oliver S. (oliverso)


Lesenswert?

StefanK schrieb:
> Nun ja, wenn man (als Anfänger) seit Jahrzehnten auf dieselben Probleme
> stößt, wäre es dann nicht mal an der Zeit, dieselben zu beheben?

Anfänger behebt man dadurch, daß die dazu lernen, und irgendwann keine 
Anfänger mehr sind. Anders geht es nun mal nicht.

Weils aber für jeden in diesem Umfeld immer was dazuzulernen gibt, egal, 
wie wenig Anfänger man ist, ist das eine Lebensaufgabe.

Hier mal ein schönes Beispiel für eine andere "Falle". Ist C++, geht 
aber genauso in C.
1
#include <iostream>
2
#include <cstdint>
3
#include <limits>
4
5
int main()
6
{
7
   int result = 0;
8
   uint8_t add = 0xff;
9
10
   int numIterations = std::numeric_limits<int>::max() / add;
11
   std::cout << "numIterations: " << numIterations << std::endl;
12
13
   for (int i = 0; i < numIterations; i++)
14
   {
15
      result += add;
16
   }
17
18
   std::cout << "result before last add: " << result << std::endl;
19
20
   result += add;
21
22
   if (result < 0)
23
      std::cout << "negative " << result << std::endl;
24
   else
25
      std::cout << "positive " << result << std::endl;
26
}

Ohne Optimierung kompiliert gibt das Programm "negative -2147483521" 
aus,
mit Optimierung dagegen "positive -2147483521".

Das da undefined behaviour im Spiel ist, ist klar, aber das Ergebnis 
erstaunt dann trotzdem immer wieder. Der Compiler nutzt den 
Sprachstandard gnadenlos aus. Hält man sich nicht dran, geht es schief.

Oliver

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb im Beitrag
> wie wenig Anfänger man ist, ist das eine Lebensaufgabe.

Das ist sehr treffend formuliert, insbesondere wenn es um ein sehr 
komplexes Thema geht wie SW-Entwicklung für uC. Das kann mitunter  sehr 
hart sein, vor allem was den C-Standard angeht.

Der Grund, warum ich weiter oben schier vom Glauben abgefallen bin, ist 
die Tatsache, dass es trotz Jahrzehnte langer Verwendung von Interrupts 
- einem der zentralsten Themen in der uC Entwicklung - im avr-gcc immer 
noch ohne jegliche Warnung zur Weg-Optimierung einer globalen Variablen 
in einer ISR kommt. Es ist m.E. einfach nicht mehr zeitgemäß, dem 
Optimierer des avr-gcc da nicht eine entsprechende Regel beizubringen.

Zurück zu Ihrem Beispiel von oben. Ich nehme an es geht um 
unterschiedliche Datentypen signed, unsigned bzw. type casts? Wie kommt 
man aus der geschilderten Falle? Würde der Compiler warnen? Würden 
Check-Tools wie z.B. MISRA auf die Falle aufmerksam machen?

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> im avr-gcc immer
> noch ohne jegliche Warnung zur Weg-Optimierung einer globalen Variablen
> in einer ISR kommt.
Falsch!

StefanK schrieb:
> vor allem was den C-Standard angeht.
C++!
Du hast ein C++ Programm gezeigt, kein C.
Wobei das in diesem Fall keinen Unterschied macht.


Egal, wem du dein anfängliches versagen in die Schuhe schieben 
möchtest...
Die Ursache liegt in deinem mangelhaftem Verständnis über die 
Zusammenhänge.

Prognose:
Dein Vorschlag, diese Optimierungen aus C/C++ zu entfernen, wird nicht 
umgesetzt werden. Auch nicht in den nächsten 30 Jahren.

Dein Meckern ist damit sinnfrei.

StefanK schrieb:
> Wie kommt
> man aus der geschilderten Falle?

Lernen, was in ein undefined behavier führt und konsequent vermeiden.
Also: Sorgfalt und Disziplin.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Dein Vorschlag, diese Optimierungen aus C/C++ zu entfernen, wird nicht
> umgesetzt werden.

Tatsächlich ist das schon seit über 10 Jahren umgesetzt.  Man muss nur 
die richtige Option setzten so dass Signed Overflow nicht mehr UB ist 
sondern Wrap Around, also Modulo 2^n im entsprechenden Restsystem.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Tatsächlich ist das schon seit über 10 Jahren umgesetzt.

Ich sprach dort über die Variablen Optimierung im Zusammenhang mit ISR.
Nicht über UB.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

StefanK schrieb:
> dass es trotz Jahrzehnte langer Verwendung von Interrupts
> - einem der zentralsten Themen in der uC Entwicklung - im avr-gcc immer
> noch ohne jegliche Warnung zur Weg-Optimierung einer globalen Variablen
> in einer ISR kommt. Es ist m.E. einfach nicht mehr zeitgemäß, dem
> Optimierer des avr-gcc da nicht eine entsprechende Regel beizubringen.

Wenn man keine Optimierung möchte, dann kann man die funktionsweise 
deaktivieren.

Hilft aber auch nicht wirklich wenn man die Sprache nicht kennt.  Bei 
jeder Wissenslücke eines x-beliebigen Programmiers einen Compiler zu 
verhundsen ist nicht wirklich zielführend.

Ist halt wie immer: Know your tools! Und dazu gehört eben auch die 
verwendete Programmiersprache.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb im Beitrag
> ...dein anfängliches versagen in die Schuhe schieben...
>Auch nicht in den nächsten 30 Jahren...
> Dein Meckern...

Es scheint, als kippe gerade die Stimmung und als verlassen wir gerade 
die sachliche und konstruktive Ebene.
Das finde ich schade. Können wir bitte wieder dazu zurückkehren und 
konziliant miteinander umgehen?
Was hat Sie verärgert?

Ich zweifle keineswegs an Ihrer Erfahrung, im Gegenteil.
Ich arbeite gerade an Ihrem Codebeispiel, für das ich Ihnen danke.

von Marci W. (marci_w)


Lesenswert?

Hallo,

habe jetzt nicht den ganzen Thread gelesen, aber mir sind folgende 
Zeilen unklar (mache allerdings aktuell auch nix mit AVR):

    ldi     r16,(1<<WDE)|(1<<WDCE)
    sts     WDTCSR,r16
    ldi     r16,(0<<WDE)|(1<<WDIE)|(1<<WDP2)|(1<<WDP1)  ; 1 s, 
Interrupt-Mode
    sts     WDTCSR,r16

WDTCSR wird einmal mit WDE gesetzt und nur zwei Befehle weiter mit WDE 
gelöscht beschrieben. Ist das OK so? Gibt es irgendwelche 
Timing-Vorgaben?
Wie gesagt, sorry, wenn mein Geschreibsel keinen Sinn macht. War nur so 
ein Bauchgefühl beim Überfliegen!

ciao

Marci

von Oliver S. (oliverso)


Lesenswert?

Marci W. schrieb:
> WDTCSR wird einmal mit WDE gesetzt und nur zwei Befehle weiter mit WDE
> gelöscht beschrieben. Ist das OK so?

Ja

> Gibt es irgendwelche
> Timing-Vorgaben?

Ja, diese WDE setzen/löschen Sequenz muß innerhalb von 4 Takten 
passieren. Was dem TO auch noch auf die Füße fallen wird, falls der mal 
sein Programm im debug-Modus baut.

Oliver

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Marci W. schrieb:
> Gibt es irgendwelche
> Timing-Vorgaben?

Ja, sh. Datenblatt:
• Bit 4 - WDCE: Watchdog Change Enable
This bit is used in timed sequences for changing WDE and prescaler bits. 
To clear the WDE bit, and/or change the prescaler
bits, WDCE must be set.
Once written to one, hardware will clear WDCE after four clock cycles.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Marci W. schrieb:
> Gibt es irgendwelche
> Timing-Vorgaben?

Ja!
4 Takte hat man Zeit.

Tipp:
Das Vorgehen ist ausführlich im Datenblatt beschrieben.

von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb:
> Marci W. schrieb:
>> WDTCSR wird einmal mit WDE gesetzt und nur zwei Befehle weiter mit WDE
>> gelöscht beschrieben. Ist das OK so?

Wenn das innerhalb von 4 Taktzyklen gemacht wird, bewirkt die Sequenz, 
dass der WD nach Ablauf des WD-Timeout nur einen Interrupt auslöst und 
keinen Systemreset.

@Oliver


>Was dem TO auch noch auf die Füße fallen wird, falls der mal
> sein Programm im debug-Modus baut.

Keine Sorge, ich bin da inzwischen einen Schritt weiter. Meine 
WD-Konfigurationssequenz sieht so aus:

cli();
    WDTCSR = (1 << WDCE) | (1 << WDE);
    WDTCSR =  (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // 1s
sei();

Arduino F. macht es so:
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
    wdt_reset();
    WDTCSR |= (1 << WDCE) | (1 << WDE);
    WDTCSR =  (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // 1s
}

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

StefanK schrieb:
>     WDTCSR = (1 << WDCE) | (1 << WDE);
>     WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // 1s

Weil C/C++ kein Timing garantiert, es sich aber um eine timed Sequence 
handelt, sollte das per (Inline) Asm gemacht werden:
1
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
2
    {
3
        __asm ("sts %0,%1" "\n\t"
4
               "sts %0,%2"
5
               :: "n" (&WDTCSR),
6
               "d" ((uint8_t) ((1 << WDCE) | (1 << WDE))),
7
               "d" ((uint8_t) ((1 << WDIE) | (1 << WDP2) | (1 << WDP1)))
8
               : "memory");
9
    }

von Oliver S. (oliverso)


Lesenswert?

Johann L. schrieb:
> Weil C/C++ kein Timing garantiert, es sich aber um eine timed Sequence
> handelt, sollte das per (Inline) Asm gemacht werden:

Oliver S. schrieb:
> https://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html

Oliver

von StefanK (stefanka)


Lesenswert?

Johann L. schrieb im Beitrag

> Weil C/C++ kein Timing garantiert, es sich aber um eine timed Sequence
> handelt, sollte das per (Inline) Asm gemacht werden

Danke Johann für den Hinweis. Was könnte dazwischen kommen, sofern 
Interrupts vorher deaktiviert cli() und erst danach wiedet aktiviert 
sei() werden?

Gilt Dein Einwand auch für die ATOMIC Variante von Arduino F.?

von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb im Beitrag

Meinst Du wd_enable(WDTO_1S); andtelle der inline asm Sequenz von 
Johann?
Und dann fällt mir nichts mehr auf die Füße?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

StefanK schrieb:
> Danke Johann für den Hinweis. Was könnte dazwischen kommen, sofern
> Interrupts vorher deaktiviert cli() und erst danach wieder aktiviert
> sei() werden?

Prinzipiell darf der Compiler zwischen den beiden SFR-Zugriffen andere 
Instruktionen einfügen, so dass das Timing nicht mehr hinkommt.

> Gilt Dein Einwand auch für die ATOMIC Variante von Arduino F.?

Ja. Und auch die Inline Asm Sqeuenz muss atomar sein.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Johann L. schrieb:
>> Weil C/C++ kein Timing garantiert, es sich aber um eine timed Sequence
>> handelt, sollte das per (Inline) Asm gemacht werden:
>
> Oliver S. schrieb:
>> https://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html

wdt_enable() macht aber was anderes als die obige Sequenz.

von S. L. (sldt)


Lesenswert?

> ... darf der Compiler ...

Zu Heinrich Spoerls Zeiten war freilich ein 'Compiler' unbekannt, sonst 
hätte er vielleicht seinen Bömmel sagen lassen:
  "Wo simmer denn dran? Aha, heute krieje mer de C-Compiler. Also, wat 
is en C-Compiler? Da stelle mer uns janz dumm. Und da sage mer so: En 
C-Compiler, dat is ene jroße schwarze Raum, der hat hinten un vorn e 
Loch. Dat eine Loch, dat is de Quelldatei. Und dat andere Loch, dat 
krieje mer später."

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

S. L. schrieb:

Klingt nach Feuerzangenbowle:-)))))

Ich bin langsam restlos verwirrt.......

von Georg M. (g_m)


Lesenswert?

Bei den aktuellen AVR-Mikrocontrollern wird der WDT nicht mehr 
zweckentfremdet.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb:
> StefanK schrieb:
>> Inzwischen läuft mein Beispiel mit meiner Annahme.
> Der Weg in die Hölle ist mit falschen Annahmen gepflastert.
>
> Ich verstehe nicht, was dich davon abhält, es richtig zu machen, wenn du
> doch jetzt weißt wie und warum es richtig geht.

Ich habe Dein Beispiel ausprobiert. Es funktioniert und war lehrreich. 
Sinnvoll ist die ATOMIC Methode in einem Programm, das eine 16/32bit 
Variable in einem höher prioren unterbrechenden Prozess in schneller 
Folge updated (z.B. Motordrezahlerfassung) und in einem langen 
Hauptprogramm mit vielen Abfragen der Variablen die Kohärenz erfordern. 
In meinem Fall bringt das aber nichts.
Was die Vorsicht angeht, ist es ein Unterschied, ob ich in einer 
gefährlichen Großstadt wie Minneapolis, Cali oder Medellin lebe oder auf 
dem Dorf.

Ich halte es mit George:
Der Weg zur Hölle ist mit guten Vorsätzen gepflastert, nicht mit 
schlechten.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> In meinem Fall bringt das aber nichts.

In deinem Fall, halte ich das für Ignoranz.
Aber OK, ist dein Problem.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb:
> StefanK schrieb:
>> In meinem Fall bringt das aber nichts.
>
> In deinem Fall, halte ich das für Ignoranz.
> Aber OK, ist dein Problem.

Und ich halte nichts von Beleidigungen.
Wenn Du Dir so sicher bist, dass es in meinem Fall ein Problem gibt, 
dann beweise es.

von Rolf (rolf22)


Lesenswert?

StefanK schrieb:

> Wenn Du Dir so sicher bist, dass es in meinem Fall ein Problem gibt

Ich verrate dir mal was aus der Industriepraxis: Wenn frisch 
eingestellte Absolventen ihre ersten Programme schreiben, dann brauchen 
die oft Hilfe, bis ihre Programme laufen. Und es dauert lange, bis die 
laufen.

Wenn denen ihr Fachbetreuer sagt, "man mache" bestimmte Dinge nicht so, 
sondern anders, dann kommt in sehr vielen Fällen sofort die Antwort: 
"Warum denn anders? Was ich gemacht habe, läuft doch!"
Manche von denen lernen dann in 2 Jahren oder so, dass das kein fachlich 
akzeptables Argument ist. Andere lernen das nicht, weil sie aus ihrer 
Selbstverteidigungs-Ecke nie rauskommen (können). Ist eine reine 
Charakterfrage.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Rolf schrieb:
> lernen

Ist ein Prozess!
Keine Beleidigung.


https://de.wikipedia.org/wiki/Kompetenzstufenentwicklung

von StefanK (stefanka)


Lesenswert?

Das verstehe ich ja alles. Und streiten wir auch nicht darüber was eine 
Beleidigung ist. Bleiben wir sportlich und fair.

Die Hypothese, dass ohne ATOMIC Probleme möglich sind, kennen und 
akzeptieren wir alle.

Arduino F. soll nun den Beweis liefern, was im Ausgsgangsbeispiel dazu 
führt, dass

1.) wdt_ticks außerhalb der WD-ISR verändert wird und im Augenblick der 
Änderung

2.) die Abfrage von wdt_ticks == 0 korrumpierend unterbricht.

Das interessiert mich.

von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> Arduino F. soll nun den Beweis liefern, was im Ausgsgangsbeispiel dazu
> führt, dass

Schau ein bischen über den Tellerrand hinaus.
Der TE hat die wdt_ticks Variable als unit16_t deklariert, hat also 
ziemlich sicher vor, damit auch Zeiträume > 255 Ticks abzuzählen.
Dass er jetzt beim Debuggen/Entwickeln erstmal nur die untersten drei 
Bits von den 16 verwendet hat vermutlich einfach den Grund dass sonst 
jeder Testlauf ewig braucht.

Sowas gibt schöne Fehler... bis 255 Sekunden funktioniert es immer, bei 
größeren Zeiträumen kommt ganz selten mal eine Fehlauslösung.

von Oliver S. (oliverso)


Lesenswert?

StefanK schrieb:
> Arduino F. soll nun den Beweis liefern, was im Ausgsgangsbeispiel dazu
> führt, dass
>
> 1.) wdt_ticks außerhalb der WD-ISR verändert wird und im Augenblick der
> Änderung
>
> 2.) die Abfrage von wdt_ticks == 0 korrumpierend unterbricht.

Das braucht er nicht. Die Erfahrung zeigt, daß die Statistik das schon 
hinbekommt, mit Unterstützung vom Murphy. Der Fehler wird auftreten. 
Irgendwann. Garantiert.

Und die Erfahrung lehrt, daß solche Fehler, die nur alle paar 
Minuten/Tage/Wochen vorkommen, extrem schwierig zu finden sind. Daher 
macht man sowas von vornherein richtig.

Oliver

von StefanK (stefanka)


Lesenswert?

Εrnst B. schrieb:
> Schau ein bischen über den Tellerrand hinaus.

Mache ich, versprochen! Hand aufs Herz. Andererseits will ich mir von 
Arduino F. auch nicht einreden lassen, dass im oben angehängten 
Code-Beispiel uint16_t wdt_ticks korrumpiert wird, nur weil er 30 Jahre 
Erfahrung hat. Einfach die Behauptung in den Raum zu schmeissen und dann 
ohne Beweis drauf zu beharren reicht mir nicht. Deshalb soll er seine 
Behauptung mit einem handfesten Beweis untermauern.

> ...Bits von den 16 verwendet hat vermutlich einfach den Grund dass sonst
> jeder Testlauf ewig braucht.

Ernst, das ist brilliant! Danke! Du bringst mich auf eine Idee! Ich 
setze auf WDTO_15MS dann dauert es nicht so lang, bis z.B. 65535 
erreicht sind.

Und wenn nur ein tick fehlt, dann Asche auf mein Haupt ;-)

von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb:

Bist Du nicht neugierig, wie das angeblich passieren soll?

> Das braucht er nicht.

Nee, das kann er nicht.

von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> Code-Beispiel uint16_t wdt_ticks korrumpiert wird

Das Problem ist, dass der AVR nur 8 Bit auf einmal lesen kann.

Der check "wdt_ticks == 0" wird also in zwei Checks übersetzt, einmal 
werden die oberen 8 bits auf 0 geprüft, dann die unteren. Oder 
andersherum, die Reihenfolge ist nicht festgelegt, und liegt im ermessen 
des Compilers.

Also ist Denkbar:
-> wdt_ticks steht auf 0x00FF
erster Teil des Checks läuft, High-Byte=0x00.
  ISR triggert, wdt_ticks++, steht jetzt auf 0x0100
  Rücksprung aus der ISR in das Hauptprogram
zweiter Zeil des Checks läuft, Low-Byte=0x00.
Bedingung erfüllt, LED toggled.

Eigentlich hätte das aber erst in 3 Stunden passieren sollen...

von S. L. (sldt)


Lesenswert?

an StefanK:

Ich habe den Überblick verloren, dachte eigentlich, letztendlich sei nun 
alles klar. Könnten Sie mir in zwei oder drei einfachen Sätzen erklären, 
was noch bewiesen werden soll?

Verstehe ich es richtig:
  Im ursprünglichen Programm kann nichts schief gehen, weil das sleep 
('schlafen_gehen') unmittelbar vor dem Ende der while-Schleife steht, 
die Abfrage 'wdt_ticks == 0' direkt am Anfang, folglich der 
WDT-Interrupt mit seinem Setzen von wdt_ticks nicht in die (-se eine) 
Abfrage hineingeraten kann.
  Dann hätte ich aber, um die Intention zu verdeutlichen, sleep und 
Abfrage von wdt_ticks unmittelbar hintereinander geschrieben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

StefanK schrieb:
> dass es in meinem Fall ein Problem gibt,

Die Sequenz schreibt 2× das WDTCSR, und nur der erst Schrieb setzt WDCE.

von Roland F. (rhf)


Lesenswert?

Hallo,
StefanK schrieb:
> Deshalb soll er seine  Behauptung mit einem handfesten Beweis
> untermauern.

Stefan, ich glaube du hast nicht richtig verstanden worauf die Vielen, 
die dir geantwortet haben, hinaus wollen: es geht gar nicht so sehr um 
deinen speziellen Fall.
Es geht vielmehr darum von vorne herein Situationen...

Arduino F. schrieb:
> Lernen, was in ein undefined behavier führt und konsequent vermeiden.
> Also: Sorgfalt und Disziplin.

...zu vermeiden, dir irgend wann "auf die Füße fallen" können/werden. 
Oder mit anderen Worten:

Rolf schrieb:
> Ich nenne diese Strategie "defensive Codierung", zahlt sich immer aus.

Glaub mir, die Leute die dir fundierte Tipps gegeben haben, sind 
durchweg Leute mit profunden Kenntnissen. Du tust gut daran ihre Tipps 
zu befolgen.

rhf

von StefanK (stefanka)


Lesenswert?

@Ernst B.

Das von Dir geschilderte Szenario träfe zu, wenn die WD-ISR die Abfrage 
wdt_ticks == 0 genauso unterbricht, wie Du es skizzierst.

Die zeitliche Abfolge im Beispiel Programm ist so:

- der uC wird am Ende der Hauptschleife schlafen geschickt.
- WD-ISR weckt den uC, der WDT wird auf z.B. 1s zurückgesetzt
- die Hauptschleife wartet auf das Ende der WD-ISR
- die WD-ISR prüft und verändert wdt_ticks
- die WD-ISR ist fertig
- die Hauptschleife springt an den Anfang und prüft wdt_ticks == 0
- am Ende der Hauptschleife wird der uC schlafen geschickt

Wie könnte zum Zeitpunkt der Prüfung von wdt_ticks == 0 die WD-ISR die 
begonnene Prüfung wdt_ticks == 0 stören? Da ist die vorige ISR schon 
vorbei und die neue nocht nicht da.

von S. L. (sldt)


Lesenswert?

> Die zeitliche Abfolge im Beispiel Programm ist so ...

Okay.
  Dann stellt sich, bei genauerer Betrachtung, aber die Frage, weshalb 
der Inhalt der WDT-ISR nicht herausgezogen und direkt ins Hauptprogramm 
geschrieben wird.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> an StefanK:
>
> Ich habe den Überblick verloren, dachte eigentlich, letztendlich sei nun
> alles klar. Könnten Sie mir in zwei oder drei einfachen Sätzen erklären,
> was noch bewiesen werden soll?
>
> Verstehe ich es richtig:
>   Im ursprünglichen Programm kann nichts schief gehen, weil das /sleep/
> ('schlafen_gehen') unmittelbar vor dem Ende der while-Schleife steht,
> die Abfrage 'wdt_ticks == 0' direkt am Anfang, folglich der
> WDT-Interrupt mit seinem Setzen von wdt_ticks nicht in die (-se eine)
> Abfrage hineingeraten kann.
>   Dann hätte ich aber, um die Intention zu verdeutlichen, sleep und
> Abfrage von wdt_ticks unmittelbar hintereinander geschrieben.

Die ursprünglichen Probleme sind behoben. Das waren zum einen die 
falsche WD-Konfiguration und die abgewürgte serielle Übertragung. Beide 
Hinweise zur Behebung kamen von Ihnen.

Ihre Zusammenfassung der zeitlichen Abfolge des Programmablaufs 
entspricht auch meinem Verständnis.

Verstehe ich Ihren Vorschlag richtig: sleep an den Anfang der 
Hauptschleife zu setzen? Das wäre problemlos möglich.

@Alle
Ich weiß die fundierten Tipps und Ratschläge die hier freundlicher Weise 
genannt werden sehr zu schätzen und beherzige sie wann immer es 
erforderlich ist.

Wenn mir aber wie von Arduino F. unterstellt wird, ich hätte von meinem 
eigenen Code (oder auch sonst) keine Ahnung, dann möchte ich von ihm 
schon wissen, wodurch es in meinem einfachen Code angeblich zu einem 
Problem kommt, das mit ATOMIC behoben werden muss. Daher habe ich ihn 
aufgefordert mir einen Beweis zu liefern.

von S. L. (sldt)


Lesenswert?

an StefanK:

Um Arduino F. zu verstehen, muss man weit in die Vergangenheit 
zurückgehen: damals wurde die Arduino-Welt von den Meisten noch 
belächelt, und ein 'Arduino-Fanboy' musste nicht nur Häme&Spott, sondern 
auch so manche Anfeindung ertragen - so etwas hinterlässt Spuren; 
zumindest erkläre ich mir so seinen gelegentlichen Umgangston.

Mein Rat an Sie ist, an dieser Stelle den Fall zu schließen, es ist 
alles gesagt, gute Vor- und Ratschläge gab es zuhauf.

Guten Abend wünscht

S. Landolt

von StefanK (stefanka)


Lesenswert?

an S.L.
i.O, danke. Ironie des Schicksals, ich arbeite ja auch auf einem Arduino 
;-)

von Εrnst B. (ernst)


Lesenswert?

StefanK schrieb:
> Die zeitliche Abfolge im Beispiel Programm ist so

Mag ja sein, dass im Test-Programm im aktuellen Zustand das Timing immer 
so ist, dass kein Problem auftritt.

Dann kannst du dir die 4 Taktzyklen oder so, die der ATOMIC_BLOCK 
verursacht, sparen. Aber du sammelst auf die Art "Technical Debt". 
Irgendwann stellst du was im Program um, fügst eine zweite Wakeup-Source 
hinzu, hast eine längere Debug-Ausgabe, oder du oder jemand anders 
übernimmt Code-Teile per Copy&Paste.

Dann kann dir sowas auf die Füße fallen.

Ohne Not würde ich das nicht machen, sondern direkt korrekten Code 
schreiben.

Und wenn es nötig ist, gehört da m.M.n. ein Kommentar drüber, der 
erklärt, warum der Code zwar problematisch und unsynchronisiert ist, 
aber es in diesem speziellen Fall trotzdem funktioniert, solange 
Nebenbedingungen A, B und C eingehalten werden.

Damit ist dann dem Zukunfts-Stefan in vielen Jahren auch gleich klar, 
dass das Absicht war, und nicht eine Nachlässigkeit von einem 
unerfahrenen Programmierer.

Ist natürlich alles kein Muss, nur Hinweise wie du dir Sachen 
längerfristig erleichtern kannst.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

S. L. schrieb:
> es ist
> alles gesagt, gute Vor- und Ratschläge gab es zuhauf.
Ich stimme zu.

Ob daraus irgendeine Einsicht erwächst...
Naja, ist nicht mein Problem.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb

> Ob daraus irgendeine Einsicht erwächst...

Die Einsicht musste nicht erst wachsen. Sie war auch zuvor da, und wurde 
eingesetzt,wenn es erforderlich  war.

Von mir aus können wir diesen Disput gern beilegen und uns wieder 
vertragen. Was meinst Du?

von StefanK (stefanka)


Lesenswert?

Εrnst B. schrieb:

Das sind weitsichtige und weise Worte. Um die Wogen zu glätten habe ich 
das ATOMIC inzwischen implementiert. Will ja weder mir im Jetzt und Hier 
noch der hoffentlich besseren Version meiner Selbst in der Zukunft im 
Weg stehen :-)

Da nun der WDT-Mechanismus funktioniert kommt anstatt des LED toggelns 
natürlich umfangreichere Funktionalität, die EEP-Schreiben, ADC und ev. 
INT0 enthält. Sollte ich INT0 implementieren ist ATOMIC 
selbstverständlich Pflicht;-) Um Konflikte zu vermeiden, ist das Ganze 
an den entscheidenden Stellen mit cli/sei geschützt.

Und ja, wer weiß, wann ich die Funktionalität erweitere oder auf Teile 
dieses Codes zurückgreife und sie anderswo weiterverwende.

> Ist natürlich alles kein Muss, nur Hinweise wie du dir Sachen
> längerfristig erleichtern kannst.

Das ist das schöne an z.B. Deinen und den ähnlich lautenden Beiträgen 
vieler anderer Mitglieder hier. Dem Ratsuchenden steht es frei, den 
erteilten Rat anzunehmen oder abzulehnen, ohne daß eine Ablehnung 
persönlich genommen wird.

Noch eine kurze Rückmeldung zur Test-Idee von gestern. Ich habe WDTO auf 
16ms, MAX_WDT_TICKS=65535 gesetzt und wdt_ticks wie gehabt seriell 
ausgeben lassen. Ein Durchlauf dauerte so nur ca. 40min. Es gab auch 
ohne ATOMIC während mehrerer Stunden keinen Glitch.

Schönes WE

von Björn W. (bwieck)


Lesenswert?

StefanK schrieb:
> Es gab auch
> ohne ATOMIC während mehrerer Stunden keinen Glitch.

Nur in deinem Testcode.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Björn W. schrieb:
> Nur in deinem Testcode.
Ja, bitte lass ihm seinen persönlichen Horizont/Tellerrand.
Nicht alle überleben es, wenn sie darüber hinaus reichen sollen.

von StefanK (stefanka)


Lesenswert?

Arduino F. schrieb:
> Björn W. schrieb:
>> Nur in deinem Testcode.

Genau. Und um den ging es doch ;-)

> Ja, bitte lass ihm seinen persönlichen Horizont/Tellerrand.
> Nicht alle überleben es, wenn sie darüber hinaus reichen sollen.

Geht es jetzt wirklich so weiter?
Ach komm schon Ardu, wir wollten uns doch wieder vertragen! Peace! OK?

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