Forum: Mikrocontroller und Digitale Elektronik Attiny85 Watchdog Timer verlängern


von Senz (Gast)


Lesenswert?

Hallo!

Ich bin recht neu in der Welt der µc's, und hänge gerade an einem 
kleinen Problemchen. Gerade beschäftige ich mich mit dem Watchdog-Timer 
des Attinys. Klappt so weit auch ganz gut.

Jedoch habe ich laut Datasheet nur ein maximales Timeout von 8 Sekunden. 
Dieses versuche ich wie folgt zu verlängern:
1
#define F_CPU 1000000UL
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/sleep.h>
6
#include <avr/wdt.h>
7
#include <avr/interrupt.h>
8
9
volatile char i=0;
10
11
void WDT_Init()
12
{
13
  cli();
14
  wdt_reset();
15
  WDTCR = (1<<WDCE) | (1<<WDE);
16
  WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP3) | (1<<WDP0);
17
  sei();
18
}
19
20
ISR(WDT_vect)
21
{
22
  i++;
23
  if (i >= 2)
24
  { 
25
    i = 0;
26
    PORTB ^= (1<<PB0);
27
  }
28
}
29
30
int main()
31
{
32
  DDRB = (1<<PB0);  
33
  WDT_Init();
34
  _delay_ms(500);
35
  while(1){}
36
}

Geplant ist die LED (die an PB0 angeschlossen ist) nach ~16 Sekunden zu 
togglen. Leider resetet der Attiny an diesem Zeitpunkt nur. Selbst wenn 
ich "i" größer 2 setze, resetet der Attiny nach ca ~16 Sekunden. Mit "i 
<= 1" funktioniert alles wie gehabt, als hätte ich keine Abfrage herum 
gebaut.

Könnte mir da eventuell jemand einen Denkanstoß geben? Vielen dank 
schonmal!

Senz

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Der ATtiny hat besser geeignete Timer für Dein Problem. Der 
Watchdog-Timer ist dafür da, Deinen µC zu resetten. Um eine LED blinken 
zu lassen, ist er eigentlich nicht für gedacht.

von Senz (Gast)


Lesenswert?

Das Programm bekommt später noch einen Sleepmodus spendiert, das 
"blinken" ist nur der Ersatz für den WakeUp / SensorRead.

von (prx) A. K. (prx)


Lesenswert?

Der WDT Interrupt wird durch das Ereignis abgeschaltet und muss folglich 
in der ISR wieder eingeschaltet werden.

von Senz (Gast)


Lesenswert?

A. K. schrieb:
> Der WDT Interrupt wird durch das Ereignis abgeschaltet und muss
> folglich
> in der ISR wieder eingeschaltet werden.

Vielen dank! Mit
1
ISR(WDT_vect)
2
{
3
  i++;
4
  if (i >= 2)
5
  { 
6
    i = 0;
7
    PORTB ^= (1<<PB0);
8
  }
9
  WDT_Init();
10
}
funktioniert es einwandfrei! :)

Senz

von Peter D. (peda)


Lesenswert?

Der WDT Interrupt braucht eine spezielle Sequenz, die genau nach 
Datenblatt einzuhalten ist.
Gibt bestimmt auch ne AN dafür bei Atmel.

von Uwe (de0508)


Lesenswert?

Wie wo was ?

A. K. schrieb:
> Der WDT Interrupt wird durch das Ereignis abgeschaltet und muss folglich
> in der ISR wieder eingeschaltet werden.

das habe ich noch nie erlebt !

Ich mache das so
1
asm
2
; clear interrupt flag
3
xIn  _TMP0,SREG
4
cli
5
6
; Clear WDRF in MCUSR
7
xIn  _HA0, MCUSR
8
andi  _HA0,not (1<<WDRF)
9
xOut  MCUSR,_HA0
10
11
; clear watchdog interrupt request
12
ldi  _HA0,(1<<WDIF)
13
xOut  WDTCSR,_HA0
14
15
ldi  _HA0,byte(WDT_TIME_MASK)
16
17
; set watchdog time mode
18
; enable Watchdog
19
; enable Watchdog Timeout Interrupt
20
xIn    _HA1,WDTCSR
21
or    _HA1,_HA0
22
ori    _HA1,(1<<WDIE)
23
24
; Write signature for change enable of protected I/O register
25
ldi    _HA0,0xD8
26
xOut  CCP,_HA0
27
xOut  WDTCSR,_HA1
28
29
; restore interrupt flag
30
xOut  SREG,_TMP0
31
endasm

von (prx) A. K. (prx)


Lesenswert?

Uwe S. schrieb:
>> Der WDT Interrupt wird durch das Ereignis abgeschaltet und muss folglich
>> in der ISR wieder eingeschaltet werden.
>
> das habe ich noch nie erlebt !

Das ist nicht nur so in der Doku zum WDIE Flag dokumentiert, es ergibt 
sogar Sinn. Denn andernfalls wäre ein Controller verloren, dessen WDT 
Interrupt versagt. Mit diesem Trick geht der zweite statt dessen auf 
Reset und sorgt für klare Verhältnisse.

Ein Watchdog ist ein Wachhund. Kein Schosshündchen.

: Bearbeitet durch User
von Uwe (de0508)


Lesenswert?

Hallo A. K.,
das von Dir beschriebe Verhalten kann ich nicht bestätigen, weder auf 
eine Atmegaxy8 oder noch auf einem Attiny841, von dem der WDT-Init Code 
stammt.

der WDT im Attiny841 kann als normaler Timer benützt werden.
Im meinem Attiny841 Beispielprogramm werden alle Timer0-3 und der WDT 
als Timer (64ms) betrieben um 4 Task anzustoßen, diese lassen nach 
weiteren Verzögerungen, 4 LED mit unterschiedlichen Frequenzen blinken.

Wenn deine Annahme richtig wäre, müsste der WDT-Task nur 1x durchlaufen.
Aber dem ist nicht so.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Aus dem Datasheet des ATtiny85 Rev 04/11:

Bit 6 – WDIE: Watchdog Timeout Interrupt Enable

[...]

If WDE is set, WDIE is automatically cleared by hardware when a time-out 
occurs. This is useful for keeping the Watchdog Reset security while 
using the interrupt. After the WDIE bit is cleared, the next time-out 
will generate a reset. To avoid the Watchdog Reset, WDIE must be set 
after each interrupt.

Und dass es auch real so ist hat Sanz eindrucksvoll bewiesen.

: Bearbeitet durch User
von Uwe (de0508)


Lesenswert?

Danke A. K.,

für die Klarstellung im Modus WDE = 1.
In meinem Assembler Beispiel ist aber WDE = 0 ! und somit gilt die 
Bedingung nicht.
/If WDE is set, WDIE is automatically cleared by hardware when a 
time-out occurs. This is useful for keeping the Watchdog Reset security 
while using the interrupt. After the WDIE bit is cleared, the next 
time-out will generate a reset./
/To avoid the Watchdog Reset, WDIE must be set after each interrupt./

Deshalb läuft der WDT bei mir als normaler Timer mit groben Zeitraster.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Uwe S. schrieb:
> Deshalb läuft der WDT bei mir als normaler Timer mit groben Zeitraster.

So ergibt das einen Sinn. Im Code von Senz ist freilich WDE=1.

Ich hatte diese Eigenschaft selbst schon auf einem ATtiny841 verwendet, 
um sicherzustellen, dass es einen Reset gibt, wenn dessen I2C Slave aus 
irgend einem Grund längere Zeit mitten in einer Message hängen bleibt. 
Und das auch getestet:
1
ISR(WDT_vect)
2
{
3
    if (i2c_state == I2C_Idle) {
4
        WDTCSR |= 1<<WDIE; // re-enable the watch dog interrupt
5
        wdt_reset(); // also reset in WDT ISR
6
    }
7
}

: Bearbeitet durch User
von HildeK (Gast)


Lesenswert?

Ich habe das beim Tiny85 so gemacht:
1
ISR(WDT_vect)    // Für WD-Timer
2
{
3
 wcount--;
4
 WDTCR |=  (1<< WDIE);  // Watchdog Interrupt muss jedes mal neu aktiviert werden, sonst macht WDT Reset
5
}
In der Main wird die WD-Initialisierung einmal aufgerufen und so die 
Parameter eingestellt; so werden mit WD_PRESCALE von 0...9 die 
Timerzeiten von 16ms bis 8s gewählt - je nach Anwendung.
1
void Init_WD()
2
{
3
  uint8_t wd_timer_prescale;
4
  uint8_t wdt_flags; 
5
6
// Watchdog IRQ initialisieren
7
  wd_timer_prescale = WD_PRESCALE;
8
  if (wd_timer_prescale > 9 ) wd_timer_prescale=9;
9
  wdt_flags=wd_timer_prescale & 7;
10
  if (wd_timer_prescale > 7) wdt_flags|= (1<<5);  // wdt_flags enthält den Prescalerwert (0 .. 9)
11
  wdt_flags |= (1<<WDCE);
12
13
// Watchdoginterrupt aktivieren
14
  sei();
15
  WDTCR |= (1<<WDCE) | (1<<WDE);
16
  WDTCR = wdt_flags | (1<<WDIE);
17
}
Man muss nur nur das WDIE-Bit nach jedem Interrupt wieder neu zu setzen.
In der Main kann dann an geeigneter Stelle der Prozessor in den 
Power-Down-Schlaf gelegt werden und wird nach Ablauf der WD-Zeit wieder 
geweckt.
Mit 'wcount' wird die Anzahl der WD-Interrupts gezählt und bei Null die 
gewünschte Aktion gestartet.
Die Fuse WDTON darf nicht aktiviert sein.

So kann man die relativ lange Zeit des WD-Timers für große Zeiten 
verwenden und durch den Power-Down-Mode recht stromsparend unterwegs 
sein. Mein ATtiny85 braucht dabei ca. 5µA@4V - wenn ich es richtig weiß, 
ist das weniger als bei einer Lösung mit einem Timer, weil damit nicht 
aus dem Power-Down-Mode geweckt werden kann.

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.