Aufwachen aus Power-down Attiny85

#3902174
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?
#3902270
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.
Gast #3902401
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...
#3902457
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.
Gast #3902482
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.
#3903024
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...
#3903068
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.
#3903131
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.
Angehängte Dateien:
#3903240
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren