3
\$\begingroup\$

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?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ How do you know that you're actually waking up and not just resetting from the watchdog while still asleep? Also, what is the system clock speed? \$\endgroup\$ Oct 30, 2014 at 20:34
  • \$\begingroup\$ Because the function "Make_other_things()" has a discernably different effect from the function "Do_some_stuff()" so I know which one is executed (The former is executed in a loop and blinks leds, the latter only once and makes a tone). The frequency is 8Mhz but that shouldn't make any difference at all. \$\endgroup\$
    – con-f-use
    Oct 30, 2014 at 21:03

2 Answers 2

2
\$\begingroup\$

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?

\$\endgroup\$
3
  • \$\begingroup\$ WDIE does not get cleared, when the watchdog fires! Sorry, but that's not the answer but thanks for your time! \$\endgroup\$
    – con-f-use
    Jan 31, 2015 at 8:58
  • \$\begingroup\$ Answer updated. Let me know what you find! \$\endgroup\$
    – bigjosh
    Jan 31, 2015 at 19:06
  • \$\begingroup\$ This is great. After trying lots of things and wasting half a day this works perfectly. Thanks! \$\endgroup\$ Jul 14, 2017 at 14:19
0
\$\begingroup\$

Thanks for the code it helped me a lot after hours, of trying, to realize, that it will always reset if wdton is fused to on. However I think, at least for the header files, I have, you need to redefine WDTO_8s and 4s since the MSB is shifted to the left for the sleep time (WDP3) Datasheet Page 45

WDTCR: WDIF WDIE WDP3 WDCE WDE WDP2 WDP1 WDP0

I have added this after the AVR headers to use it as bit pattern which is more convenient

#undef WDTO_4S
#undef WDTO_8S
#define WDTO_4S 0x20
#define WDTO_8S 0x21
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.