Hallo Leute, Ich hab ein kleines Problem mit dem Watchdog. Ich würde gerne meinen Atmel 5 Stunden lang schlafen lassen. Ich hab mir die WDT Tuts schon durchgelesen und es funktioniert auch fast alles bis auf, dass er immer zu Früh nach 5 Minuten oder so oder noch früher aufwacht. Der WDT Interrupt läuft wie folgt: ISR(WDT_vect) { Sleepsekunden++; wdt_reset(); if (Sleepsekunden >= 4500){ WDT_off(); sleep_disable(); for (uint8_t i=0;i<15;i++) { millisekunden = 0; Sleepsekunden=0; } WDT_RESET(); uart_init(); sei(); } else { Sleep(); } } Die Unter-Programme Dazu: void WDT_Init(void) { //disable interrupts cli(); //reset watchdog wdt_reset(); //set up WDT interrupt WDTCSR = (1<<WDCE)|(1<<WDE); //Start watchdog timer with 4s prescaller WDTCSR = (1<<WDIE)|(1<<WDE)|(1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0); //Enable global interrupts sei(); } void WDT_RESET(void) { //disable interrupts cli(); //reset watchdog wdt_reset(); //set up WDT interrupt WDTCSR = (1<<WDCE)|(1<<WDE); //Start watchdog timer with 4s prescaller WDTCSR = (0<<WDIE)|(1<<WDE)|(0<<WDP3) |(1<<WDP2)|(1<<WDP1); //Enable global interrupts sei(); } Ich komm gerade nicht ganz mit.
Das hab ich vergessen: void Sleep(void) { // wdt_enable(7); set_sleep_mode(SLEEP_MODE_PWR_DOWN); WDT_Init(); sleep_mode(); } void WDT_off(void) { cli(); wdt_reset(); /* Clear WDRF in MCUSR */ MCUSR &= ~(1<<WDRF); /* Write logical one to WDCE and WDE */ /* Keep old prescaler setting to prevent unintentional time-out */ WDTCSR |= (1<<WDCE) | (1<<WDE); /* Turn off WDT */ WDTCSR = 0x00; sei(); }
Der Watchdog beim AVR kann soweit ich weis nur 2048 und 4096mS. Watchdog ist dafür da, dass dein Programm so und so lange brauchen darf um den WDC_Reset auszuführen. Dauert es zu lange, weil z.B.: der uC in einer Loop hängt, so kommt der Watchdog und resetet ihn.
Danke, Aber ich würde Ihn gern missbrauchen und den UC nach 5 Stunden Reset-en. Ich erhöhe ja immer ein Incrementenzäher also bis er das erreicht soll er immer wieder Schlafen gehen. Programmtechnisch sollte es passen aber er tut es nicht wirklich :-)
T.A. schrieb: > Ich würde gerne meinen Atmel 5 Stunden lang schlafen lassen. Der Watchdog ist dafür da, wenn etwas nicht so funktioniert, wie vorgesehen. Um den Controller absichtlich aus dem Schlaf aufzuwecken ist er das falsche Tool. Dafür benutzt man Timer. Gruß Jobst
Erst mal fehlt hier eine ganze Menge: - welcher Prozessor - keine Definition der verwendeten Variablen - kein Hauptprogramm - unklar, was die 5h sollen, die einzige Abfrage die ich sehe, beziehen sich auf 4500s und das wären 1.25h - die ganze WD-Bearbeitung sieht sehr komplex und umständlich aus. Außerdem: bei den mir bekannten Tinys ist die maximale Watchdogperiode 8s, danach wacht er auf jeden Fall erst mal wieder auf. Hänge mal ein funktionsfähiges Programm an, das deine Wünsche nicht erfüllt und nenne diese Wünsche auch genau. Code als Anhang oder eingebunden in die
1 | -Tags, damit man es auch lesen kann. |
2 | |
3 | Bei mir sah die Verwendung des WD kürzlich so aus (Wechselblinken an PB3 und PB4 eines Tiny25): |
4 | [c] |
5 | #define F_CPU 1e6
|
6 | |
7 | #include <avr/io.h> |
8 | #include <avr/interrupt.h> |
9 | #include <avr/sleep.h> |
10 | #include <avr/wdt.h> |
11 | |
12 | #define WD_PRESCALE 3 // 3:=> 125ms
|
13 | |
14 | ISR(WDT_vect) |
15 | {
|
16 | PORTB ^= (1<<PB3) | (1<<PB4); // Toogle both outputs |
17 | }
|
18 | |
19 | int main() |
20 | {
|
21 | uint8_t wdt_flags, wd_timer_prescale; |
22 | |
23 | // PB3, PB4
|
24 | DDRB = 0xFF; // outputs |
25 | PORTB = 0x08; // LOW per Default, PB3 auf HIGH |
26 | |
27 | // Strom sparen
|
28 | PRR |= (1 << PRUSI) | (1 << PRTIM1) | (1 << PRTIM0) | (1 << PRADC); // USI, Timer1/0, ADC ungenutzt: abschalten |
29 | |
30 | // Power-Down Sleep Mode einstellen
|
31 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // stromsparend, aufwecken über WD oder INT0 |
32 | sleep_enable(); // Schlafmodus vorbereiten |
33 | |
34 | // Watchdog IRQ vorbereiten
|
35 | wd_timer_prescale = WD_PRESCALE; |
36 | if (wd_timer_prescale > 9 ) wd_timer_prescale=9; |
37 | wdt_flags=wd_timer_prescale & 7; |
38 | if (wd_timer_prescale > 7) wdt_flags|= (1<<5); // wdt_flags enthält den Prescalerwert (0 .. 9) |
39 | |
40 | // Watchdog aktivieren
|
41 | WDTCR |= (1<<WDCE) | (1<<WDE); |
42 | // set watchdog timeout value, start WD-IRQ
|
43 | WDTCR = wdt_flags | (1<<WDIE); |
44 | |
45 | sei(); |
46 | |
47 | while (1) |
48 | {
|
49 | // WD-IRQ weckt, toggelt Ports und schläft weiter. Stromaufnahme: <10µA ohne Last
|
50 | sleep_mode(); |
51 | }
|
52 | }
|
Bitte keine Files mit TABs drin posten. Sieht scheisse aus. Deine ISR sieht aber auch sonst höchst seltsam aus. sei() in dort meist falsch. Was soll die Schleife bewirken? Sleep() in der ISR sieht verdächtig nach Stacküberlauf aus, weil ISR von ISR unterbrochen wird, dir von einer ISR unterbochen wird, usw.
Jobst M. schrieb: > Um den Controller absichtlich aus dem Schlaf aufzuwecken ist er das > falsche Tool. Dafür benutzt man Timer. Der Watchdog-Interrupt ist dafür durchaus geeignet, wenn es auf Genauigkeit nicht ankommt. Ein Timer braucht mehr Strom, oder einen unabhängigen Takt.
:
Bearbeitet durch User
ATMEGA324PA Ich glaub auch, dass ich hier etwas falsch mache :-) sonst würde es ja gehen. Du hast recht ein sei(); in ISR ist nicht gut :-) Die Idee dahinter ist es den INterrupt des WDT so einzustellen, dass er alle 4 Sekunden ausgelöst wird. In diesem Interrupthandler dann eine Variable Hochzählen zu lassen und dann wenn die Variable einen bestimmten Wert hat den UC zu reset-en.
T.A. schrieb: > Die Idee dahinter ist es den INterrupt des WDT so einzustellen, dass er > alle 4 Sekunden ausgelöst wird. Der Hauptfehler ist, dann in der ISR auf den nächsten Interrupt zu warten. Damit stapeln sich die Interrupts auf dem Stack und irgendwann läuft der über. Das muss im Hauptprogramm passieren, nicht in der ISR. Die ISR zählt bloss die Zeit hoch. Das Hauptprogramm pennt so lange immer wieder ein, bis die Stunden rum sind.
:
Bearbeitet durch User
T.A. schrieb: > Die Idee dahinter ist es den INterrupt des WDT so einzustellen, dass er > alle 4 Sekunden ausgelöst wird.. Ok > In diesem Interrupthandler dann eine Variable Hochzählen zu lassen Ok > und dann wenn die Variable einen bestimmten Wert hat den UC zu reset-en. Warum willst du dann ein Reset machen? Ein Sprung nach 0x0000 würde es auch tun. Ansonsten musst den den WDT am Ende wieder so konfigurieren das beim nächsten Durchlauf der Reset ausgelöst wird. In der ISR brauchst du eigentlich nur die Variable hochzählen. In der Hauptschleife prüfst du ob die 4500 Durchläufe um sind, wenn nicht dann wieder Sleep. Sascha
Wird die Hauptschleife überhaupt durchgelaufen wenn ich den MCU auf Power_Down Schlafen lasse?
T.A. schrieb: > Wird die Hauptschleife überhaupt durchgelaufen wenn ich den MCU auf > Power_Down Schlafen lasse? Der Watchdog-Interrupt weckt den Prozessor auf und der läuft dann so lange weiter, bis du ihn wieder schlafen legst. Er beendet also ganz normal die ISR, landet dann im Hauptprogramm und macht dort direkt nach dem Sleep weiter.
> Ich würde gerne meinen Atmel 5 Stunden lang schlafen lassen.
Geht es um den Lerneffekt oder um eine konkrekte Anwendung?
Bei Letzterem wäre vielleicht Timer2 mit 32 KiHz-Quarz die
stromsparendere Lösung, vorausgesetzt, der Systemtakt muss nicht
quarzgenau sein.
Würde auch noch gehen, ist aber trickreich & aufwändig, und lohnte sich wohl nur, wenn es auf das letzte uA ankommt: OSCCAL per 32 KiHz-Quarz justieren.
Zu C kann&will ich nichts sagen, aber in Assembler sieht es fast noch einfacher aus als der Vorschlag von HildeK:
1 | .org $0000 |
2 | rjmp reset |
3 | |
4 | .org WDTaddr |
5 | reti |
6 | ;******************************************************* |
7 | |
8 | main: |
9 | sbi DDR_LED,LED |
10 | ldi tmp0,(1<<SE)+(1<<SM1) ; power-down |
11 | out SMCR,tmp0 |
12 | ldi tmp0,(1<<WDCE)+(1<<WDE) |
13 | sts WDTCSR,tmp0 |
14 | ldi tmp0,(1<<WDIE)+(1<<WDP3)+(1<<WDP0) ; 8 s |
15 | sts WDTCSR,tmp0 |
16 | sei |
17 | main_loop: |
18 | sleep |
19 | sbi PIN_LED,LED |
20 | |
21 | ; hier auf 2250 = 5*3600 / 8 zaehlen |
22 | |
23 | rjmp main_loop |
24 | ;******************************************************* |
25 | reset: |
26 | ... |
A. K. schrieb: > Jobst M. schrieb: >> Um den Controller absichtlich aus dem Schlaf aufzuwecken ist er das >> falsche Tool. Dafür benutzt man Timer. > > Der Watchdog-Interrupt ist dafür durchaus geeignet, wenn es auf > Genauigkeit nicht ankommt. Ein Timer braucht mehr Strom, oder einen > unabhängigen Takt. Hätte Jobst jetzt zu 100% zugestimmt. Immer wieder interessant auch mal andere Ansätze zu lesen. Danke für deinen Einwurf dieser Art!
habs geknackt. Danke Der Hinweis dass nach dem Interrupt das Programm dort weiterläut wo er im Sleep geschickt wurde war der Bringer. Danke Dir/Euch
Zum Stromverbrauch: Timer 2 oder WDT Timer unterscheiden sich im uA Bereich. Ich glaube es kommt darauf an ob man den WDT noch für Programmsicherheit benötigt oder nicht.
T.A. schrieb: > Timer 2 oder WDT Timer unterscheiden sich im uA Bereich. > Ich glaube es kommt darauf an ob man den WDT noch für Programmsicherheit > benötigt oder nicht. Der WDT ist nicht verbrannt, wenn man den "Interrupt and System Reset Mode" verwendet. Denn der WDT-Interrupt schaltet sich automatisch bei Ablauf ab. Wenn man den also nicht wieder einschaltet, dann gibts beim zweiten Ablauf einen Reset. Man muss also den WDT-Interrupt jedesmal neu einschalten. Wichtig ist hier, dass man das nicht in der WDT-ISR macht, sondern in der Mainloop. Wenn diese Mainloop nicht läuft, dann gibts trotz erfolgtem WDT-Interrupt nach zweimaligem Ablauf einen Reset.
:
Bearbeitet durch User
A. K. schrieb: > Wichtig ist > hier, dass man das nicht in der WDT-ISR macht, sondern in der Mainloop. Steht ja in so ziemlich jedem Buch, wenn man anfängt µC zu programmieren, dass man in der ISR nur das absolut Notwendige machen sollte und dann wieder in die Hauptroutine springen soll.
Danke! Wie gesagt ich bin ja jetzt wieder gescheiter geworden. Also Danke Euch
T.A. schrieb: > Zum Stromverbrauch: Der grössere Unterschied dürfte im Hauptoszillator liegen. Im Power Down ist der abgeschaltet. Wenn das ein Quarzoszillator ist, dann dauert es verdammt lang, bis der sauber läuft. In dieser Zeit frisst der µC deutlich Strom. Und das macht der alle 4 Sekunden. Wenn man als Hauptoszillator den internen R/C-Oszillator verwendet kann, dann entfällt dieser Startup und die Gesamtlaufzeit des alle 4 Sekunden auftretenden WDT-Interrupts ist viel kürzer. Ein Kompromiss ist ein Keramikoszillator. Für UART reicht er und der Startup ist viel schneller. Das Optimum bei genauem Haupttakt ist der interne R/C-Oszillator als Haupttaktquelle zusammen mit einem asynchronen quarzgenauen 32kHz Timer. Um den Haupttakt hinreichend genau zu kriegen stellt man das Kalibrier-Register des R/C-Oszillators anhand gemessener Takte für ein paar Perioden des 32kHz Timers immer wieder neu ein. Das dürfte den minimal möglichen Verbrauch ergeben. Den WDT-Interrupt braucht man dann nicht und der 32kHz Osz ist sparsamer als der WDT.
:
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.