Moin,
ich überlege gerade wie ich einen µC möglichst sparsam mache.
Am einfachsten erscheint mir die Kombination aus Watchdog und Power-Down
Mode.
Wie ich das sehe gibt es die Möglichkeit den Watchdog alle 8s den µC neu
zu starten. Dann läuft aber jedes mal die ganze Init-Phase mit durch.
Es gibt ja auch die Möglichkeit nur einen Interrupt auszulösen. Aber wie
wirkt sich das aufs Programm aus? Macht er dann in der Zeile nach dem
SLEEP befehl weiter?
Das MCUSR Register informiert dich nach einem Reset, welches die Quelle
war. ein gesetztes WDRF bedeutet z.B., das es der Watchdog war. MCUSR
sollte man möglichst bald nach dem Reset Ereignis abfragen.
Fabian F. schrieb:> Es gibt ja auch die Möglichkeit nur einen Interrupt auszulösen. Aber wie> wirkt sich das aufs Programm aus? Macht er dann in der Zeile nach dem> SLEEP befehl weiter?
Der watchdog löst einen Interrupt aus, der uC startet und springt die
ISR an, und macht dann hinter dem Sleep-Befehl weiter.
Die Interrupt-Routine kann leer sein, es muß sie aber geben.
Julian Baugatz (julinho) schrieb:
> Die Interrupt-Routine kann leer sein, es muß sie aber geben.
SO EINFACH geht es wohl NICHT:
Tiny25/45/85-Datenblatt:
To avoid the Watchdog Reset, WDIE must be set after each interrupt.
Und natürlich auch vor dem ersten "sleep"-Aufruf!
Ich mache das bei Batterie-Spar-Anwendungen gleich in der
RESET-ISR. Danach habe ich 8 lange Sekunden Zeit bis zum
ersten "sleep"-Aufruf.
Also:
In der Watchdog-ISR das WDIE-Bit setzen!
Dann geht es nach "reti" zum nächsten Befehl nach "sleep".
Das MCUSR Register ist bei solchen Anwendungen eher schnurz,
da man ja gerade KEINEN Reset haben will.
1
RESET: ; * * * Programm Start * * * * * * * * * *
2
wdr ;
3
in R16 , WDTCR ; setze W-Dog auf 8 s mit IRQ-Enable
Oldie schrieb:> To avoid the Watchdog Reset, WDIE must be set after each interrupt.
Das trifft aber nur auf den Fall zu, bei dem man auch WDE gesetzt
hat, also den normalen Watchdog-Reset-Modus zusätzlich zum Interrupt
aktiviert.
Wenn man WDE nicht setzt, ist das ein ganz normaler Interrupt und
sonst nichts.
Oldie schrieb:> SO EINFACH geht es wohl NICHT:
Doch.
Das von dir beschriebene Szenario gilt, wenn der WD sowohl Reset als
auch Interrupt ausführen soll. Dann kommt zuerst der Interrupt und nach
einer weiteren WD-Time der Reset. Will man diesen Reset verhindern, muss
man WDIE setzen.
Benutzt man den WD aber nur als Timer, kann man sich das sparen. Dann
verhält der WDT sich wie ein ganz normaler Timer.
mfg.
EDIT: Zu langsam.
Ehrlich?
Habe ich wohl etwas "Angst-Code" in meinen Anwendungen!
Naja, eine Rückrufaktion ist hoffentlich nicht nötig:
Es wird 1,5 µs / 8 s = 0,00002% zu viel Strom verbraucht.
Ohne dem würde die Batterie nicht nur 1 Jahr, sondern 1 Jahr
und 6 Sekunden halten.
Oldie schrieb:> Es wird 1,5 µs / 8 s = 0,00002% zu viel Strom verbraucht.> Ohne dem würde die Batterie nicht nur 1 Jahr, sondern 1 Jahr> und 6 Sekunden halten.
Absolut kritisch. Ganz klares Kill-Kriterium.
mfg.
Vielen Dank für die Tips. Wenn man WDIE setzt kommt kein Reset, selbst
den WDE gesetzt ist:
"• Bit 6 – WDIE: Watchdog Timeout Interrupt Enable
When this bit is written to one, WDE iscleared, and the I-bit in the
Status Register is set, the Watchdog Time-out
Interrupt is enabled. In this mode the corresponding interrupt is
executed instead of a reset if a timeout in the
Watchdog Timer occurs."
Wie ich das verstanden habe braucht man dann auch kein sei()?
Das hier lässt den µC für gut 30 Sekunden wegdämmern. Im Schlaf braucht
er dann noch 30µA. Damit komm ich ein paar Jahre hin.
1
while(sleepcount<5){
2
LED_OFF
3
WDTCR=(1<<WDP3)|(1<<WDP0)|(1<<WDIE);
4
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
5
sleepcount++;
6
sleep_mode();
7
LED_ON
8
}
9
sleepcount=0;
Komischerweise scheint das hier nicht zu funktionieren:
1
WDTCR=(1<<WDP3)|(1<<WDP0)|(1<<WDIE);
2
while(sleepcount<5){
3
LED_OFF
4
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
5
sleepcount++;
6
sleep_mode();
7
LED_ON
8
}
9
sleepcount=0;
Ich kann mir nicht erklären warum, aber nachdem es funktioniert...
Fabian F. schrieb:> WDTCR=(1<<WDP3)|(1<<WDP0)|(1<<WDIE);
Das funktioniert grundsätzlich nicht.
Erstmal muss man den Watchdog einschalten:
1
WDTCSR|=(1<<WDCE)|(1<<WDE);
2
MCUSR&=~(1<<WDRF);
Und dann ändern:
1
//nur Interrupt, 8s
2
unsignedchartemp=(1<<WDIE)|(1<<WDP3)|(1<<WDP0);
3
WDTCSR|=(1<<WDCE)|(1<<WDE);
4
WDTCSR=temp;
Diese Abfolge ist wichtig, auch die temp-Variable, da es sich hierbei um
eine 'Timed Sequence' handelt. Damit wird verhindert, dass ein
wildgewordener Pointer oder ein Stack-Überlauf die
Watchdog-Einstellungen ändern kann.
mfg.
Thomas Eckmann schrieb:> Fabian F. schrieb:>> WDTCR=(1<<WDP3)|(1<<WDP0)|(1<<WDIE);>> Das funktioniert grundsätzlich nicht.
Ich kann sehen, dass es funktioniert :-p
> Erstmal muss man den Watchdog einschalten:>
1
>WDTCSR|=(1<<WDCE)|(1<<WDE);
2
>MCUSR&=~(1<<WDRF);
3
>
Hatte ich vergessen zu posten, in der Init habe ich
>
1
>WDTCR|=(1<<WDE);
2
>
Die Flag lösche ich nicht, funktioniert aber trotzdem. Laut datenblatt
muss ich WDCE nur setzen muss, wenn ich den Watchdog ausschalten möchte:
"Bit 3 – WDE: Watchdog Enable
When the WDE is written to logic one, the Watchdog Timer is enabled, and
if the WDE is written to logic zero, the
Watchdog Timer function is disabled. WDE can only be cleared if the WDCE
bit has logic level one."
Das schreiben innerhalb von 4 Zyklen muss ich laut Datenblatt nur wenn
ich den WD ausschalten möchte. Nicht beim einschalten.
Änderungen sind auch mit einer Operation möglich insofern der µC im
"Security Level 1" befindet. (s. Bild)
> Diese Abfolge ist wichtig, auch die temp-Variable, da es sich >hierbei um> eine 'Timed Sequence' handelt. Damit wird verhindert, dass ein> wildgewordener Pointer oder ein Stack-Überlauf die> Watchdog-Einstellungen ändern kann.
Pointer sind mir suspekt. Wenn man sie irgendwie vermeiden kann, fass
ich die nicht an :-)
Es gibt wohl einige unterschiede beim Watchdog-handling zwischen alten
µC (Atmega16&co) und den neueren.
Fabian F. schrieb:> Es gibt wohl einige unterschiede beim Watchdog-handling zwischen alten> µC (Atmega16&co) und den neueren.
Ja, die gibt es.
Bei den alten Controllern kann man es so, wie von dir beschrieben,
machen. Bei den neueren nicht. Da bekommt man zwar den Watchdog-Reset
und/oder den -Interrupt eingeschaltet. Aber nicht die Verzögerungszeit.
Die von mir gezeigte Methode funktioniert dagegen bei allen AVR.
mfg.