Forum: Mikrocontroller und Digitale Elektronik Aufwachen aus Power-down Attiny85


von Fabian F. (fabian_f55)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fabian F. schrieb:
> Macht er dann in der Zeile nach dem SLEEP befehl weiter?

Erstmal im Interrupt-Handler für den Watchdog-Interrupt, danach dort.

: Bearbeitet durch Moderator
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

: Bearbeitet durch User
von Julian B. (julinho)


Lesenswert?

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.

von Oldie (Gast)


Lesenswert?

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
4
  ori R16, (1 << WDIF) | (1 << WDIE) | (0b00100001 << WDP0)
5
  out WDTCR, R16          ; Also: Kein WD-Reset!
6
7
  ldi R16 ,  low(RAMEND)  ; Stack Pointer (LOW) auf RAM-Ende
8
  out SPL, R16          
9
  ldi Tmp0, high(RAMEND)  ; Bei Tiny45/85 auch noch:
10
  out SPH, R16            ; Stack Pointer (HIGH) auf RAM-Ende
11
12
  clr  R16                ; 
13
  rjmp START              ; Weiter zur Initialisierung...
14
15
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
16
17
WDT:                      ; Aufwach-ISR                   Takte
18
  in   R0, sreg           ;                                   5
19
  push R16                ;                                   7
20
21
  in   R16, WDTCR         ;                                   8
22
  ori  R16, (1 << WDIE)   ; WD-IRQ-Enable, (kein WD-Reset!)   9
23
  out  WDTCR, R16         ;                                  10
24
25
  pop  R16                ;                                  12
26
  out  sreg, R0           ;                                  13
27
  reti                    ;  < 2,5 µs @ 8 MHz                18

Nach 8 Sekunden Tiefschlaf dürften diese paar µs nicht stören...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Thomas E. (thomase)


Lesenswert?

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.

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

: Bearbeitet durch User
von Fabian F. (fabian_f55)


Lesenswert?

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...

von Thomas E. (thomase)


Lesenswert?

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
unsigned char temp = (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.

: Bearbeitet durch User
von Fabian F. (fabian_f55)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fabian F. schrieb:
> Wie ich das verstanden habe braucht man dann auch kein sei()?

Doch, wer Interrupts haben will, muss sie auch gestatten.

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.