Guten Abend,
momentan versuche ich, einen ATtiny13 schlafen zu legen und per Watchdog
zu wecken. Im Prinzip funktioniert das auch, nur dass der Watchdog
doppelt so lange läuft, wie erwartet.
Der ATtiny13 läuft mit internem Oszillator (9,6MHz) und CLKDIV8, was für
den Watchdog aber keine Rolle spielen sollte.
Der Watchdog wird wie folgt eingstellt, wie ich es in einem Tutorial
gefunden habe:
Hallo,
eventuell solltest du den WDTIE erst aktivieren bevor du ihm die
Prescaler einstellst.
Aber bitte nicht böse sein falls es nicht stimmt, ist bei mir schon
etwas her und habe es seit damals nicht mehr verwendet (und dann auch
noch fremder, angepasster, Arduino-Code):
Beitrag "Re: selbstblinkende SMD-LED mit niedrigem Stromverbrauch"
Interessant, dass der Tiny13 WDCE braucht für den Prescaler. Ist nicht
so beim Tinyx5 - nur nebenbei.
Vielleicht stört ihn
WDTCR = (1 << WDCE);
WDTCR = 1 << WDP3;
und man sollte
WDTCR |= (1 << WDCE);
WDTCR |= (1 << WDP3);
nehmen?
Zumindest die beim Prescaler, denn ich könnte mir vorstellen, dass er
das gesetzte WDCE noch sehen will, das überschreibt man aber bei der
Zuweisung.
Rätselhaft bleibt aber, warum er dann die längste Zeit nimmt.
Klaus H. schrieb:> und man sollte
Korrekt, da (Zeile 3) gehört ein |= hin, ansonsten überschreibt man das
Register komplett, und setzt nicht nur das eine Bit, was man setzen
möchte.
Ebenso sind die Klammern in Zeile 2 ohne Funktion.
Das Tutorial ist doof.
Uwe D. schrieb:> Wirklich?
Ja! Dein Bild zeigt es doch: WDCE braucht man nur um WDE auszuschalten.
Beim Tiny13 steht zusätzlich drin, dass man es auch fürs Ändern von WDPx
setzen muss.
@Joni
Wie stellst du fest, dass der Attiny 9-10 us schläft? Der Watchdog ist
auf 4s eingestellt und du hast den Watchdog Interrupt auch aktiviert,
d.h. für einen Reset des Attinys muss der Watchdog zweimal auslösen,
sprich er läuft dann 8s nominal was mit den beobachteten 9-10s recht gut
korreliert.
Stellst du das Aufwachen über den Watchdog-Reset in deinem Test fest?
Dann würde es passen und dein Denkfehler liegt darin begründet, dass du
den Watchdog-Interrupt "vergessen" hast.
Auch denkbar, dass du im Watchdog-Interrupt einen Pin toggelst. Hast du
bedacht, dass du das Interrupt-Bit des Watchdogs nach dem Auftreten des
Interrupts erneut setzen musst? Auch dann würden nämlich die 9-10s
passen zu deiner Beobachtung.
Keks F. schrieb:> Korrekt, da (Zeile 3) gehört ein |= hin, ansonsten überschreibt man das> Register komplett, und setzt nicht nur das eine Bit, was man setzen> möchte.
Ist in dem Fall aber völlig egal. WDCE muss lediglich gesetzt sein, wenn
man die WDPx-Bits setzen muss und das ist da der Fall. Dass man dann
zeitgleich WDCE löscht ist für das Setzen der WDPx-Bits irrelevant und
es muss ja geklappt haben da, hätte es nicht geklappt, der Watchdog
sonst alle 16ms kommen würde, d.h. der Mikrocontroller müsste alle 16ms
aufwachen und nicht alle 9-10s ;)
Klaus H. schrieb:> Uwe D. schrieb:>> Wirklich?>> Ja! Dein Bild zeigt es doch: WDCE braucht man nur um WDE auszuschalten.> Beim Tiny13 steht zusätzlich drin, dass man es auch fürs Ändern von WDPx> setzen muss.
@Uwe D.: Ich habe wohl gestern Abend deinen Ausschnitt nicht voll
erfasst 🙄.
Ja, du hast recht, es steht auch beim Tinyx5 drin, trotzdem braucht man
WDCE für den WD-Timer nicht.
Z.B. das im Anhang läuft so wie es soll, für Tiny25. (Für den Tiny13
muss man WDIE durch WDTIE ersetzen) ohne WDCE zu verwenden. Und selbst
wenn man nach einigen Zyklen die WDPs auf einen neuen Prescaler-Wert
stellt, geht das ohne WDCE - gerade nochmal ausprobiert und in anderen
Programmen auch mehrfach so verwendet.
Es ist im Datenblatt unklar ausgedrückt; tatsächlich ist WDCE nur bei
aktiviertem WDE notwendig - ein Sicherheitsfeature, um weder die Zeiten
noch den WD-Reset versehentlich zu löschen. Es wird (vermutlich) beim
Tiny13 genauso sein.
Joni benötigt die zweite Zeile nicht und auch die erste ist redundant
und nur für den WD-Reset relevant.
M. K. schrieb:> WDCE muss lediglich gesetzt sein, wenn> man die WDPx-Bits setzen muss und das ist da der Fall.
Siehe oben. Das kann beim Tiny13 so sein (kann ich mangels Device nicht
testen). Der ist aber sehr ähnlich zum Tiny25, da steht es auch drin,
trotzdem braucht man es nicht für den reinen Timerinterruptbetrieb.
Und, er hat den WDE gar nicht in Verwendung, also nur Timer zum Wecken,
kein Reset. Außer er hätte die Fuse gesetzt.
Da Joni nur einen Programmausschnitt zeigt: es könnte auch ein ganz
trivialer Grund sein: Er toggelt eine LED mit dem Interrupt und dann ist
die 4s an und 4s aus und er wollte eigentlich eine Periode von 4s?
Thomas E. schrieb:> Nein. Damit versaust du die Timed Sequence.
Beim WDE in deinem Codeschnipsel braucht man die Timed Sequence, ja.
Ist jedoch nur WDIE aktiv, wie beim TO, dann nicht. Man kann dann den
Teil
WDTCR |= (1 << WDCE);
einfach weglassen!
In meinem Beispiel gibt es gar keine Timed Sequence; man setzt die
Prescalerbits und aktiviert den Interupt Enable. Man kann auch
irgendwann später die WDPx neu setzen für eine neue Zeit - alles ohne
WDCE!
Klaus H. schrieb:> Ist jedoch nur WDIE aktiv, wie beim TO, dann nicht. Man kann dann den> Teil> WDTCR |= (1 << WDCE);> einfach weglassen!
Ich zitiere aus dem Datenblatt des ATTiny13:
>• 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.
Danach muss auch zum Ändern des Prescalers das WDCE-Bit gesetzt sein,
vorher lässt sich WDPx nicht ändern.
Aber ohne konkrete Rückmeldung vom TE ist sowieso alles nur stochern im
Nebel. Für mich klingt es schwer danach, dass WDE gesetzt sit oder das
Fusebit für den Watchdog ist gesetzt. Dann macht das beobachtete
Verhalten nämlich durchaus sinn ;)
M. K. schrieb:> Ich zitiere aus dem Datenblatt des ATTiny13:
Ja, kenne ich und steht beim auch Tiny25 so drin. Es ist imho einfach
unklar im DB ausgedrückt: den Prescaler kann man trotzdem im
WDIE-Betrieb ohne weiteres verändern. Nur nicht im WDE-Betrieb.
> Danach muss auch zum Ändern des Prescalers das WDCE-Bit gesetzt sein,> vorher lässt sich WDPx nicht ändern.
Nach meinen Erfahrungen eben nur wenn WDE oder die Fuse aktiv sind. Da
ist es auch sinnvoll - aus Sicherheitsgründen.
Oben habe ich ein korrekt laufendes Beispiel für den Tiny25 angehängt
(watchdog2.c). Da wird weder WDCE noch WDE angefasst. Könnte man ja mal
ausprobieren ... (Tiny13: WDIE durch WDTIE ersetzen)
> Aber ohne konkrete Rückmeldung vom TE ist sowieso alles nur stochern im> Nebel.
Ja.
Apollo M. schrieb:> wdt Handling erledigen die 3 Macros von wdt.h> wdt_reset(), wdt_disable(), wdt_enable(timeout)>> .. und man kann es ohne oder mit wdt ISR machen.
Ich verstehe leider die Makros dort nicht gut genug, aber mir scheint,
dass wdt_enable(timeout) auch WDE aktiviert. Das will ich üblicherweise
nicht.
Auch in den Datenblattbeispielen ist immer WDE beteiligt.
Klaus H. schrieb:> Ich verstehe leider die Makros dort nicht gut genug, aber mir scheint,> dass wdt_enable(timeout) auch WDE aktiviert.
Ja, das ist richtig. wdt_enable() aktiviert auch WDE.
Klaus H. schrieb:> Das will ich üblicherweise> nicht.
Warum eigentlich nicht? Das ist doch eigentlich der übliche
Einsatzzweck: Interrupt und System-Reset nutzen. Ich frage nur aus
reinem Interesse, soll kein Vorwurf sein. Ich nutze auch idR den
Interrupt + System-Reset. Sollte ich mal hängen bleiben und das WDIE-Bit
nicht rechtzeitig wieder setzen gibts halt den Systemreset. In der ISR
des WDT mache ich üblicher Weise übrigens nichts.
M. K. schrieb:> Ich frage nur aus reinem Interesse, soll kein Vorwurf sein.
Ich fasse das auch nicht so auf.
> Warum eigentlich nicht?
Nun, ich habe mehrere Programme 24/7 am laufen, z.T. seit Jahren, die
eher weniger komplex sind. Welche, die mit 3 AAA oder einer LiIon und
welche, die mit 5V Wandwarze bzw. mit eigenem Trafonetzteil betrieben
werden.
Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset
notwendig gemacht hätte. Und wenn dem so wäre, würde ich das auf einen
HW- oder SW-Fehler zurückführen und würde die Ursache suchen.
Hardware ohne µC hat ja auch selten einen Watchdog und ist nicht
zwangsweise unempfindlicher auf externe Störungen.
Ich verwende den WD-Interrupt meist als einfachen stromsparenden Timer,
bei dem die Zeittoleranz des 128kHz Oszillators nicht stört, meist im
Zusammenhang mit sleep. Zugegeben, diese Toleranz ist manchmal der
Hauptnachteil.
Außerdem habe ich die Befürchtung, dass ich mit der Kombination aus
Interrupt und Reset mehr Chancen für Programmierfehler habe, 😀 z.B. den,
nicht rechtzeitig den WD zu quittieren oder in der ISR den WDIE neu zu
setzen (wie ich hier schon gesehen habe).
> In der ISR des WDT mache ich üblicher Weise übrigens nichts.
Ich schon, meist habe ich da eine Variable, die den Mulitplikator für
die Basiszeit darstellt und in der ISR dekrementiert wird. Wie auch in
dem obigen Beispiel. Damit kann man 'endlos' lange Zeiten (grob
natürlich) erreichen - endlos bez. der Lebenserwartung eines Menschen 😉.
Klaus H. schrieb:> Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset> notwendig gemacht hätte.
Ist auch meine Erfahrung.
Ich habe es lieber, wenn mir der MC ordentlich auf die Finger haut, wenn
ich einen SW-Fehler gemacht habe, damit ich ihn suchen und beheben kann.
Der Watchdog würde den Fehler nur verschleiern und der Kunde ärgert sich
über den Schluckauf des Gerätes.
Gegen EMI hilft der Watchdog eh nicht, da kriegt er die hängende CPU
nicht mehr raus, bzw. wenn durch EMI der Flash korrumpiert wurde.
Klaus H. schrieb:> Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset> notwendig gemacht hätte. Und wenn dem so wäre, würde ich das auf einen> HW- oder SW-Fehler zurückführen und würde die Ursache suchen.
So ähnlich gehe ich dabei vor: Ich schau erst nach wenn ich merke, dass
das Watchdog ständig das System resetet denn wenn der Watchdog mir den
Mikrocontroller resetet habe ich sicher einen Zustand erreicht, den ich
nicht bedacht hatte. Wir sind ja alle nicht fehlerfrei. Der Systemreset
ist bei mir also mehr Angst als sonst was ;)
Den Interrupt des Watchdogs nutze ich idR auch nur um den
Mikrocontroller aufzuwecken. Daher brauch ich im Interrupt auch nichts
machen. Uhren und ähnliches, dafür hat der Mikrocontroller Timer die
IMHO besser geeignet sind als der Watchdog. Klar, man kann dafür auch
den Watchdog nutzen, keine Frage. Mir wäre der Watchdog dafür aber zu
ungenau bzw. das wäre mir zu viel Gezumpel um das in adequate
Zeitintervalle zu schubsen.
@ M. K.
Ja, für Uhren ist das nicht geeignet, da braucht man Quarz und die
Timer. Selbst das stellt nicht immer zufrieden; meist ist ein RTC besser
...
Timer brauchen meist etwas mehr Strom (speziell bei Batteriebetrieb
relevant) und Funktionen wie die eines Monoflops wie z.B.
Treppenlichtautomat, limitierte Einschaltzeit für ein Spielzeug, ein
sehr langsamer astabiler MV (keep alive für eine Powerbank z.B.) u.ä.,
selbst für eine langsam blinkende Melde-LED nutze ich das, all das hat
selten hohe Anforderung an die zeitliche Präzision. Denn dann würde man
es anders lösen, klar - immer schön abhängig von den Anforderungen.
Hier ging es aber um einen reinen Interruptbetrieb (so aus dem Code des
TO zu entnehmen) und meiner Behauptung, dass man dann den WDCE nicht
bemühen muss um den Prescaler zu setzen oder zu ändern.
> Wir sind ja alle nicht fehlerfrei.
Ja leider 😉.
> Der Systemreset ist bei mir also mehr Angst als sonst was ;)
Gut, jeder hat eine andere Vorgehensweise. Und wenn es nur selten mal
rumpelt, dann sind unsere beiden Varianten schwer zu debuggen.
> das wäre mir zu viel Gezumpel um das in adequate> Zeitintervalle zu schubsen.
Ich finde die Timer anstrengender zu programmieren. Aber auch wieder:
jeder hat eine andere Vorgehensweise oder auch nur Gewohnheit ...
Klaus H. schrieb:> meist ist ein RTC besser
Ich hab auch gemerkt, daß ein Uhrenquarz am MC nicht das gelbe vom Ei
ist. Frequenzen an anderen Pins (PWM, Multiplex) lassen den Quarz
schnell ungenau werden.
Eine schöne externe RTC ist der RX8010. Quarz ist mit eingebaut, also
kaum zu stören. Im SO-8 auch für ältere Leute noch gut zu löten und bei
160nA mit nem Goldcap auch Monate zu stützen. Reichelt hat ihn lagernd.
return in einer ISR ist überflüssig. Ebenso das Flag löschen, das
passiert alles automatisch wenn die ISR implementiert ist. Es genügt ein
1
ISR(WDT_vect){
2
}
zu schreiben. Tritt nun ein Interrupt auf wird die ISR angesprungen und
die Automatismen werden die entsprechenden Flags löschen. Von Hand ist
da nichts zu machen.
Das Löschen des Watchdogs Interupt Flag in loop() ist daher auch
überflüssig und den Prescaler muss man auch nicht ständig neu setzen,
das genügt ihn einmal zu setzen.
Alles richtig, was du schreibst.
Auch die cli() und sei() Makros braucht man nicht, mit Ausnahmen eines
sei() beim Setup. Naja, kann sein, dass er ein lange laufendes Programm
hat und nur einmal für 4s schlafen lassen will. Das kann man aus dem
Programmschnipsel nicht entnehmen.
Und das WDCE, wie oben ausführlich diskutiert, auch nicht.
M. K. schrieb:> return in einer ISR ist überflüssig.
Ich bin mir nicht sicher, ob das nicht sogar schädlich ist.
Normalerweise (in ASM) steht ein 'RETI' - Interrupt Return - am Ende der
Routine.
Die macht der Compiler aber von sich aus.
-- Gerade mal getestet: nein, ist nicht schädlich; es wird vom Compiler
entfernt. Er setzt nur ein 'RETI' unabhängig ob ein "return;" da ist
oder nicht.
Der Vollständigkeit halber: Es gibt jedoch noch die Spezialvariante
"ISR(XXXX_vect, ISR_NAKED);", die braucht (in C) aber ein "reti();" am
Ende, kein "return;".
Klaus H. schrieb:> die braucht (in C) aber ein "reti();" am> Ende, kein "return;".
Kommt halt darauf an, ob man das I-Flag dabei wieder setzten will, oder
nicht.
Oliver
Oliver S. schrieb:> Kommt halt darauf an, ob man das I-Flag dabei wieder setzten will, oder> nicht.
Wo sollte es sinnvoll sein, das nicht zu wollen? Einen "deferred
handler" würde man sinnvollerweise aus der ISR aufrufen und vorher
händisch sei() aufrufen.
Und alles andere konstruiert effektiv eine Art Trap. Der unterbroche
Code (der unvorhersagbar ist) läuft weiter, aber mit gesperrten
Interrupts. Wozu sollte sowas gut sein?
Klaus H. schrieb:> Die macht der Compiler aber von sich aus.> -- Gerade mal getestet: nein, ist nicht schädlich; es wird vom Compiler> entfernt. Er setzt nur ein 'RETI' unabhängig ob ein "return;" da ist> oder nicht.
Ein return ist ja auch kein RETI. Es ist die Anweisung an den Compiler,
springe zur letzten schließenden Klammer. Und da steht dann bei einem
Interrupthandler der Epilog, der erstmal benutzte Register rettet und
den Stack aufräumt.
So tief bin ich in den Details nicht drin.
Der TO hat in seinem letzten Beispiel ein return in der ISR, was ich
bisher noch in keinem Beispiel gesehen hatte. Mir war nur nicht klar, ob
das ggf. schädlich sein könnte.
Klaus H. schrieb:> Der TO hat in seinem letzten Beispiel ein return in der ISR, was ich> bisher noch in keinem Beispiel gesehen hatte.
Es kommt durchaus vor, daß man an mehreren Stellen den Interrupt
verlassen möchte, z.B. beim I2C-Handler.
return ist ein Sprungbefehl in C, wie auch goto, break, continue.
In einer ISR geht return natürlich nur ohne Argument.
Peter D. schrieb:> Ein return ist ja auch kein RETI. Es ist die Anweisung an den Compiler,> springe zur letzten schließenden Klammer. Und da steht dann bei einem> Interrupthandler der Epilog, der erstmal benutzte Register rettet und> den Stack aufräumt.
Wie ist bitte die Anweisung "Springe zur letzten schließenden Klammer"
zu verstehen? Ein RETI wie auch ein RET (return) springt zu der letzten
Adresse die auf dem Stack steht. RETI aktiviert das globale
"Interrupt-Enable" Flag wieder, RET nicht.
Zu genauer Erklärung empfehle ich das AVR Instruction Set Manual:
https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf
Gruß
Robert
Robert G. schrieb:> Wie ist bitte die Anweisung "Springe zur letzten schließenden Klammer"> zu verstehen?
Eine Funktion in C ist durch {} gekennzeichet:
1
voidfoo(void)
2
{
3
}
C Sprunganweisungen sind goto, break, continue und return.
RET, RETI sind aber Assemblersyntax, die versteht ein Compiler nicht.