Attiny85 Watchdog Timer verlängern

Gast #4242177
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
Gast #4242195
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
#4242214
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
#4242222
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.
#4242348
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.
#4242389
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.
#4242402
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.
#4242405
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
}
Gast #4243096
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.

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