Electronic – How to avoid a reset when using the watchdog in interrupt mode and changing the timeout

attinyinterruptswatchdog

I'm writing a program for an ATtiny85. I want to use the watchdog to wake up from power down in certain intervals. It all works fine as long as the intervals for a watch dog event don't change within the program (i.e. I don't change bits WDP0-3 after they're set once).

As soon as I change the timeout of the watchdog, it does an unwanted reset. I don't have the WDTON fusebit enabled, so the whactdog is not always on but rather has to be enabled by software. (In fact the fuse bits are: Low=0xe2, High=0xdf and Extended= 0xff make the controller run at 8 Mhz from the internal oscillator).

Here is a snipplet of my code:

#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

void Config_wdt(uint8_t timeout) {
    cli();
    wdt_reset();                  // reset watchdog timer
    MCUSR &= ~(1<<WDRF);          // clear reset flag
    WDTCR = (1<<WDE) | (1<<WDCE); // enable watchdog
    WDTCR = (1<<WDIE) | timeout;  // watchdog interrupt instead of reset
    //+reset, timeout can be 15,30,60,120,250,500ms or 1,2,4,8s
    sei();
}

EMPTY_INTERRUPT(WDT_vect)

void Sleep_now(uint8_t i, uint8_t timeout) {
  Config_wdt(timeout);
  for(i=i; i>0; --i) {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // set type of sleepmode
    power_all_disable();                  // disable unneeded loads
    wdt_reset();                          // timer should start at zero
    sleep_enable();                       // approach sleep mode
    sleep_mode();                         // enter sleep mode (confirm)
    sleep_disable();                      // entrance point when woken up
    // Another Config_wdt(timeout); here or re-enabling WDIE doesn't help either
    power_all_enable();                   // re-enable the loads
  }
}

int main() {
  Config_wdt(WDTO_500MS);

  Do_some_stuff();
  Sleep_now(5, WDTO_500MS); // WORKS AS EXPECTED, SLEEPS 5 TIMES 500ms = 2.5s

  while(1) {
      Make_other_things();
      Sleep_now(3, WDTO_30MS); // DOES AN UNWANTED RESET, ...
      \\+I.E. STARTS FROM THE BEGINNING BEFORE "Do_some_sfuff()"...
      \\+IF I REPLACE "WDTO_30MS" HERE by "WDTO_500MS" FROM ABOVE...
      \\+THE CODE WORKS FINE.
  }
}

The datasheet has this to say:

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.

But even if I set the WDIE bit immediately after waking up from sleep mode, it still does the unwanted reset.

So how do I change the watchdog timeout in my code, without the unwanted reset?

Best Answer

I am unable to reproduce this behavior. I am using an ATTINY85 with the fuse values given.

To test, I created Do_some_stuff() that blinks a white LED for 1 second, and Make_other_things() that blinks a red LED for 1 second.

On power-up...

  1. The white LED blinks for ~1 second as Do_some_stuff() is executed
  2. There is a ~2.5 second pause as Sleep_now(5, WDTO_500MS) is executed
  3. The red LED blinks for ~1 second as Make_other_things() is exectued
  4. There is a ~100ms pause as Sleep_now(3, WDTO_30MS) is executed
  5. Steps 3-4 repeat indefinitely

Here is a scope view after powerup...

Yellow trace=White LED
Purple trace=Red LED

Code I am using is here...

/*
 * CrazyWatchDogResetTest.c
 * Target: ATTINY85 default fuses
 * Created: 1/30/2015 12:22:44 PM
 *  Author: josh
 */ 

#define F_CPU 8000000                       // Name used by delay.h. We are running 1Mhz (default fuses)

#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>

// White LED connected to pin 5

#define WHITE_LED_PORT PORTB
#define WHITE_LED_DDR DDRB
#define WHITE_LED_BIT 0


// Red LED connected to pin 6 

#define RED_LED_PORT PORTB  
#define RED_LED_PIN PINB
#define RED_LED_DDR DDRB
#define RED_LED_BIT 1


// Blink white LED for 1 second

void Do_some_stuff() {

    WHITE_LED_DDR |= _BV(WHITE_LED_BIT);
    WHITE_LED_PORT |= _BV(WHITE_LED_BIT);   
    _delay_ms(1000);
    WHITE_LED_PORT &= ~_BV(WHITE_LED_BIT);

}


// Blink red LED for 1 second

void Make_other_things() {

    RED_LED_DDR |= _BV(RED_LED_BIT);    
    RED_LED_PORT|=_BV(RED_LED_BIT);
    _delay_ms(1000);
    RED_LED_PORT&=~_BV(RED_LED_BIT);

}

void Config_wdt(uint8_t timeout) {
    cli();
    wdt_reset();                  // reset watchdog timer
    MCUSR &= ~(1<<WDRF);          // clear reset flag
    WDTCR = (1<<WDE) | (1<<WDCE); // enable watchdog
    WDTCR = (1<<WDIE) | timeout;  // watchdog interrupt instead of reset
    //+reset, timeout can be 15,30,60,120,250,500ms or 1,2,4,8s
    sei();
}

EMPTY_INTERRUPT(WDT_vect)

void Sleep_now(uint8_t i, uint8_t timeout) {
    Config_wdt(timeout);
    for(i=i; i>0; --i) {
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // set type of sleepmode
        power_all_disable();                  // disable unneeded loads
        wdt_reset();                          // timer should start at zero
        sleep_enable();                       // approach sleep mode
        sleep_mode();                         // enter sleep mode (confirm)
        sleep_disable();                      // entrance point when woken up
        // Another Config_wdt(timeout); here or re-enabling WDIE doesn't help either
        power_all_enable();                   // re-enable the loads
    }
}


int main(void)
{

 Config_wdt(WDTO_500MS);

 Do_some_stuff();

 Sleep_now(5, WDTO_500MS); // WORKS AS EXPECTED, SLEEPS 5 TIMES 500ms = 2.5s

 while(1) {
     Make_other_things();
     Sleep_now(3, WDTO_30MS); // DOES AN UNWANTED RESET, ...
 }

}

...which I think is identical you yours except for the extra functions.

Can you try this code and see if you get unexpected results? If so, then can you think of any differences introduced by your code that could explain the different behavior?