Forum: Mikrocontroller und Digitale Elektronik ATtiny24 mit Watchdog + Sleep


von Rudolf F. (vbxler)


Lesenswert?

Hallo Leute!

Ich habe ein Problem mit dem Sleep-Mode bei einem Attiny24.

Ich will folgendes machen:
Der Attiny24 soll im Schlafmodus verharren und wird durch den Watchdog 
alle x Sekunden aufgeweckt, um das was zu machen (Messung ...) um 
anschließend wieder in den Schlafmode geschickt zu werden.

Watchdog läuft bereits, habe ich so gelöst:
  //Bit 7 –   WDIF: Watchdog Timeout Interrupt Flag
  //Bit 6 –   WDIE: Watchdog Timeout Interrupt Enable
  //Bit 5 –   WDP   Watchdog Timer Prescaler 3
  //Bit 4 –   WDCE: Watchdog Change Enable
  //Bit 3 –   WDE:  Watchdog Enable
  //Bit 2:0 – WDP   Watchdog Timer Prescaler 2, 1, and 0
  //-----------------------------------
  WDTCSR    = 0b01000101;

Nach der vorgegebenen Zeit wird ISR aufgerufen und eine LED blinkt kurz 
-- das funktioniert:
//--------------------------------------------------------------
// Interruptvector Watchdog Time-out
//--------------------------------------------------------------
ISR(WATCHDOG_vect)
{
  PORTB |= (1 << DO_PB0);
  _delay_ms(1);
  PORTB &= ~(1 << DO_PB0);
  WDTCSR |= (1<<WDIE);
}

Sleep funktioniert nur teilweise - das MCUCR habe ich so beschalten:
  //Bits 1:0 –  ISC01, ISC00: Interrupt Sense Control 0 Bit 1 and Bit 0
  //Bit 2 –     BODSE: BOD Sleep Enable
  //Bits 4:3 –  SM1, SM0: Sleep Mode Select Bits 1:0
  //Bit 5 –     SE: Sleep Enable
  //Bit 6 –     PUD: Pull-up Disable
  //Bit 7 –     BODS: BOD Sleep
  //-----------------------------------
  MCUCR    = 0b10110100;

In den Schlafmodus schicke ich den Attiny nach dem ersten ISR-WATCHDOG:
//--------------------------------------------------------------
// Interruptvector Watchdog Time-out
//--------------------------------------------------------------
ISR(WATCHDOG_vect)
{
  cli();
  sleep_disable();

  PORTB |= (1 << DO_PB0);
  _delay_ms(1);
  PORTB &= ~(1 << DO_PB0);

  WDTCSR |= (1<<WDIE);
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  sleep_enable();
  sleep_mode();
  sei();
}

Das ganze verhält sich jetzt so, dass der ISR-WATCHDOG aufgerufen wird, 
die
LED blinkt und der Stromverbrauch sinkt von ca. 4mA auf 1,5mA ab, aber 
der ISR-WATCHDOG kommt dann nicht mehr. Erst nach einem Reset kommt der 
ISR-WATCHDOG wieder einmal und strom geht von 4mA auf 1,5mA.

Meine Fragen:
a) wie kann ich den ISR-WATCHDOG erneut aktivieren?
b) warum sinkt der Stromverbrauch nur so gering ab?
(Es gibt keine Analogverarbeitung)

Danke für eure Hilfe!

von HildeK (Gast)


Angehängte Dateien:

Lesenswert?

Du könntest der Vollständigkeit halber dein ganzes Programm anhängen.

Ich habe dir ein funktionierendes Beispiel für den Tiny25/45/85 
angehängt, dort habe ich eine Ruhestromaufnahme von <10µA.
Ich toggle in der IRQ einen Port, aber genauso kannst du dort ein Flag 
(volatile!) setzen, um in der main eine Aktion zu starten.

von Rudolf F. (vbxler)


Angehängte Dateien:

Lesenswert?

Vielen dank für das Beispiel 'HildeK'!
Ich hatte da einen Denkfehler, ich dachte ich muss jedesmal starten und 
stoppen.

Habe es an den Attiny angepasst, dass sieht so aus:

// WD-Timer mit Sleep
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/delay.h>

#define WD_PRESCALE 7  //  8:=> 4s


ISR(WATCHDOG_vect)
{
  PORTB |= (1 << PB0);
  _delay_ms(1);
  PORTB &= ~(1 << PB0);
}


int main()
{
  uint8_t wdt_flags, wd_timer_prescale;
  DDRA    = 0b00000000;
  DDRB    = 0b00000011;

  PORTA   = 0b00001100;
  PORTB   = 0b10000100;

  // Strom sparen
  PRR |= (1 << PRUSI) | (1 << PRTIM1) | (1 << PRTIM0) | (1 << PRADC); 
// USI, Timer1/0, ADC ungenutzt: abschalten


  // Power-Down Sleep Mode einstellen
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // stromsparend, aufwecken über 
WD oder INT0
  sleep_enable();                       // Schlafmodus vorbereiten

  // Watchdog IRQ vorbereiten
  wd_timer_prescale = WD_PRESCALE;
  if (wd_timer_prescale > 9 ) wd_timer_prescale=9;
  wdt_flags=wd_timer_prescale & 7;
  if (wd_timer_prescale > 7) wdt_flags|= (1<<5);  // wdt_flags enthält 
den Prescalerwert (0 .. 9)

  WDTCSR |= (1<<WDCE) | (1<<WDE);    // Watchdog aktivieren
  WDTCSR = wdt_flags | (1<<WDIE);    // set watchdog timeout value, 
start WD-IRQ
  sei();

  while (1)
  {
    sleep_mode();   // WD-IRQ weckt, toggelt Ports und schläft weiter. 
Stromaufnahme: <10µA ohne Last
  }
}

Der ISR-WATCHDOG_vect wird alle x-Sekunden aufgerufen.
Das einzige was nicht geht ist das mit der Stromreduktion, es werden 
noch immer 1,5mA aufgenommen.
Kann es sein, dass es mit der Quelle des Taktgeben zusammen hängt?
Ich verwende den internen RC-8MHz als Taktgeber.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Rudolf F. schrieb:
> Kann es sein, dass es mit der Quelle des Taktgeben zusammen hängt?

Nein, das liegt daran, daß du keine ISR für deinen Watchdog Interrupt 
hast.

Lies die Warnings, die sind nicht zum Ignorieren da. Ein Blick ins 
Datenblatt unter "Interrupt Vectors" ist dann der nächste Schritt.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Also mit diesem Programm fließen bei meinem ATtiny84A 6.1 uA, in guter 
Übereinstimmung mit dem Datenblatt.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Zumindest beim ATtiny45 (25, 85) muß das WDIE-Flag nach jedem ISR-Aufruf 
neu gesetzt werden.
Wenn Du zuvor das WDE-Flag gesetzt hast (Dieses wird in der ISR nicht 
zurück gesetzt), löst der WatchDog beim 2.ten Versuch ein Reset aus - 
steht auch so im Datenblatt als Sicherheit, falls sich z.B. die ISR 
festhängt.

Selber habe ich den WatchDog bisher auch nur mit WDE + WDIE betrieben - 
kann mir also vorstellen, daß bei Dir das WDIE-Flag durch den Sprung in 
die ISR gelöscht wird und danach ist der WatchDog halt aus.

MfG

von S. Landolt (Gast)


Lesenswert?

Ich kann nur wiederholen: bei mir wie offenbar auch bei Rudolf F. läuft 
obiges Programm, und bei mir stimmt auch die Stromaufnahme.
Allenfalls könnte ich noch ein etwas kompakteres Programm anbieten:
1
#define  F_CPU  8000000UL
2
#define LED  0
3
4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include <avr/sleep.h>
8
9
ISR(WATCHDOG_vect)
10
{
11
PORTB |= (1<<LED);
12
_delay_ms(5);
13
PORTB &= ~(1<<LED);
14
}
15
16
int main (void)
17
{
18
  DDRB |= (1<<LED);
19
  PORTA = 0xFF;
20
  PORTB = 0xFF;
21
  PRR = 0xFF;
22
  ACSR = (1<<ACD);
23
  MCUCR = (1<<SE)+(1<<SM1);    // power-down
24
  WDTCSR = (1<<WDIE)+(1<<WDP3);  // 8 s  interrupt
25
  sei();
26
  while (1) {
27
    sleep_mode();
28
  };
29
   
30
   return 0;
31
}

von Jacko (Gast)


Lesenswert?

Ich habe einen Tiny25 mit Temperatursensor und IR-Ausgabe
so programmiert, dass er mit dem max. WD-Timing alle
8 s aufwacht. Dabei wird die Variable ABLAUF erhöht.
ABLAUF = (ABLAUF + 1) & 0x03. ABLAUF = 0, 1, 2, 3, 0, ...

 0s: ABLAUF = 0: Temperatur-Messung, (Ablauf += 1) % 4, SLEEP
 8s: ABLAUF = 1: (Ablauf += 1) % 4, SLEEP
16s: ABLAUF = 2: Daten senden, (Ablauf += 1) % 4, SLEEP
24s: ABLAUF = 3: (Ablauf += 1) % 4,  SLEEP
 0s: ...

Eckpunkte im ASM-Programm:
1
RESET:            ; * * * Program Start * * *
2
  wdr
3
  in  Tmp0, WDTCR      ; setze W-Dog auf 8 s mit IRQ-Enable
4
  ori  Tmp0, (1 << WDIF) | (1 << WDIE) | (0b00100001 << WDP0)  ; 8 s
5
  out WDTCR, Tmp0      ; Also kein Reset vom W-Dog!
6
7
  ldi Tmp0, low(RAMEND)
8
  out SPL,Tmp0      ; Stack Pointer auf RAM-Ende
9
10
  clr  Tmp0
11
  rjmp START      ; Wichtige Initialisierungen...

Nach START geht es in die Hauptschleife:
1
MAIN:
2
mn_0:  
3
  ldi  Tmp0, 0
4
  cp  ABLAUF, Tmp0
5
  brne  mn_1
6
  rcall get_temperatur  ;  t =  0 s: messen
7
  rjmp  mn_nxt
8
9
mn_1:            ;  t =  8 s: weiterschlafen!
10
11
mn_2: 
12
  ldi  Tmp0, 2
13
  cp  ABLAUF, Tmp0
14
  brne  mn_3
15
  rcall  ir_send      ;  t = 16 s: ausgeben
16
;  rjmp  mn_nxt
17
18
mn_3:            ;  t = 24 s: weiterschlafen!
19
20
mn_nxt:
21
  inc  ABLAUF
22
  ldi  Tmp0, 4
23
  cp  ABLAUF, Tmp0
24
  brcs  mn_rdy
25
  sub  ABLAUF, Tmp0    ;   4 -> 0   Nach 8 s sleep: Neuer Zyklus mit t = 0
26
27
mn_rdy:
28
  in  Tmp0, MCUCR
29
  ori  Tmp0, (1 << SE) | (1 << SM1) | (0 << SM0)  ; Sleep-Enable, SleepMode = 2 (PwrDown)
30
  out  MCUCR, Tmp0
31
  sleep                      ; Schlafe bis Wecker vom WatchDog (8 s)
32
  
33
  rjmp  MAIN

Die IR-LED pulst mit 120 mA, der Verbrauch über 32 s liegt aber
nur bei 55 µA. Das läuft über 2 Jahre mit einer Batterie von
4,5 V / 1 Ah. (3 x AAA)

von HildeK (Gast)


Lesenswert?

Rudolf F. schrieb:
> Das einzige was nicht geht ist das mit der Stromreduktion, es werden
> noch immer 1,5mA aufgenommen.
> Kann es sein, dass es mit der Quelle des Taktgeben zusammen hängt?
> Ich verwende den internen RC-8MHz als Taktgeber.

Auch mein Beispiel wurde mit dem internen Oszillator getestet bei 1MHz 
(CKDIV8 aktiv).

Du hast in dem an meines angepassten Programm insgesamt für vier 
Portpins (A2, A3, B2, B8) den Pullup aktiv.
1
  DDRA    = 0b00000000;
2
  DDRB    = 0b00000011;
3
4
  PORTA   = 0b00001100;
5
  PORTB   = 0b10000100;
Sind die zugehörigen Eingänge außen beschaltet? So ein interner Pullup 
hat so um die 30kΩ und wenn da außen ein Pulldown oder hart GND anliegen 
sollte, dann ist der erhöhte Strom schon möglich. 5V/8k = 0,6mA ...
Auch die anderen Eingänge, die keinen Pullup definiert haben: sind die 
außen auf festem Potential? Wenn nicht, sind es die offene Eingänge, die 
zu erhöhter Stromaufnahme führen.
Ansonsten würde ich mal ein neues Device nehmen.

von S. Landolt (Gast)


Lesenswert?

Der Watch-dog hat seinen eigenen Oszillator, die Erzeugung des 
Systemtaktes spielt für die Stromaufnahme im Power-down-Modus keine 
Rolle.

von S. Landolt (Gast)


Lesenswert?

> Wenn nicht, sind es die offene Eingänge, die
> zu erhöhter Stromaufnahme führen.
Nicht im Power-down-Modus, siehe Datenblatt:
"... most of the digital inputs are disabled in the deep sleep modes 
...".

Diese 1.5 mA müssen anderswo herkommen.

von HildeK (Gast)


Lesenswert?

Patrick J. schrieb:
> Zumindest beim ATtiny45 (25, 85) muß das WDIE-Flag nach jedem ISR-Aufruf
> neu gesetzt werden.

Nein. Siehe mein funktionierendes Programm im zweiten Post. Kannst ja 
testhalber noch ein _delay_ms(1000) an den Anfang setzen, um zu sehen, 
dass es keinen WD-Reset auslöst.

S. Landolt schrieb:
> Der Watch-dog hat seinen eigenen Oszillator, die Erzeugung des
> Systemtaktes spielt für die Stromaufnahme im Power-down-Modus keine
> Rolle.

Richtig, der WD läuft mit 128kHz. Aber der Rest des Programms (könnte ja 
länger sein) läuft mit dem Systemtakt. Der Tiny25 benötigt z.B. während 
eines _delay_ms-Aufrufs die 1.4mA. Wenn er fast nichts tun muss (wie in 
meinem Programm nur die Ports toggeln), dann sinkt die Stromaufnahme auf 
sehr kleine Werte.

S. Landolt schrieb:
>> Wenn nicht, sind es die offene Eingänge, die
>> zu erhöhter Stromaufnahme führen.
> Nicht im Power-down-Modus, siehe Datenblatt:
> "... most of the digital inputs are disabled in the deep sleep modes
> ...".

Korrekt, gerade getestet am PB0 - stimmt! :-)

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.