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?
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 |
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);
> Eigentlich dasselbe
Und wenn Sie, dauert ja nur 30", meine Version einfach ausprobieren?
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.
S. L. schrieb: > Und wenn Sie, dauert ja nur 30", meine Version einfach ausprobieren? Mache ich, bin nur gerade nicht am richtigen PC.
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
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.
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.
Aber selbst wenn wdt_ticks komplett weg optimiert würde, muss wenigstens der Text seriell ausgegeben werden.
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
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?
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.
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.
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.
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
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.
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
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.
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?
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.
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?
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 ;-)
StefanK schrieb: > Wie bekomme ich es hin, dass die > Serial.print Funktion vor dem Betreten des Schlafs ordentlich > abgearbeitet wird? Serial.flush();
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
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.
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(); |
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
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.
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
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
@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?
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
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.
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()
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.
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.
Ε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
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.
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!!!
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
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
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
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
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.
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.
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.
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
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
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.
Marci W. schrieb: > Gibt es irgendwelche > Timing-Vorgaben? Ja! 4 Takte hat man Zeit. Tipp: Das Vorgehen ist ausführlich im Datenblatt beschrieben.
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
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 | }
|
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
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.?
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?
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
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.
> ... 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
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.
StefanK schrieb: > In meinem Fall bringt das aber nichts. In deinem Fall, halte ich das für Ignoranz. Aber OK, ist dein Problem.
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.
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.
Rolf schrieb: > lernen Ist ein Prozess! Keine Beleidigung. https://de.wikipedia.org/wiki/Kompetenzstufenentwicklung
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.
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.
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
Ε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 ;-)
Oliver S. schrieb: Bist Du nicht neugierig, wie das angeblich passieren soll? > Das braucht er nicht. Nee, das kann er nicht.
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...
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.
StefanK schrieb: > dass es in meinem Fall ein Problem gibt, Die Sequenz schreibt 2× das WDTCSR, und nur der erst Schrieb setzt WDCE.
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
@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.
> 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.
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.
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
an S.L. i.O, danke. Ironie des Schicksals, ich arbeite ja auch auf einem Arduino ;-)
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.
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.
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?
Ε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
StefanK schrieb: > Es gab auch > ohne ATOMIC während mehrerer Stunden keinen Glitch. Nur in deinem Testcode.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.

