Trouble using watchdog to wake on Attiny85

Hi everybody,

I'm new to the forum and still a rookie at programming microcontrollers - though I know my way around in Java. Currently I'm trying to get a watchdog to wake an Attiny85, but I simply don't get it to work. For testing purposes the code is supposed to wake the uC every two seconds, give a short blink on the LED and sleep again. I also tried to shut down as many systems as possible to save power.

The problem is: It just won't blink, the LED stays lit all the time and I just don't see why. The only thing I could imagine is that I messed up with the watchdog registers, but comparing it to some online tutorials, they seem fine to me.

I hope some of you experts can help me on this one. Thanks in advance for any suggestions.

Pete

PS: Can anyone btw explain to me the purpose of using the flag for the watchdog. I read about this one somewhere but I don't get the reason for using it.

Here's my code:

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

#define LED_PIN 4

volatile boolean wdt_flag = 1;

//Watchdog Interrupt Service
ISR(WDT_vect){
  wdt_flag = 1;  //flag signalling Watchdog Timeout
}

void setup(){
 byte i;
 // all pins to OUTPUT LOW
 /*for(i=0; i<=5 ; i++){
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
 }*/

  pinMode(LED_PIN, OUTPUT);
 
  ADCSRA = 0;           // turn off ADC
 //ADCSRA &= ~(1<<ADEN); //not sure if needed in, does previous command do the job?

 watchdogSetup();
}

void loop(){
  if (wdt_flag == 1) {  // wait for watchdog timeout flag
    wdt_flag = 0;       // reset flag
    
    //blink
    digitalWrite (LED_PIN, HIGH);
    delay (30);
    digitalWrite (LED_PIN, LOW);
    goToSleep();
  }
}

void goToSleep(){
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    //set sleep mode
    sleep_enable();
    //power_all_disable();  // disable as much as possible
    
    sleep_mode(); //sleep
    
    //-----------------ZZZ
    
    sleep_disable(); //aufwachen
    //power_timer0_enable(); // enable timers, don't need the rest anyway
    //power_timer1_enable(); // 
}

void watchdogSetup(){
  cli();  //interrupts off
  
  MCUSR &= ~(1 << WDRF);  //delete WDRF Bit
  WDTCR |= (1<<WDCE) | (1<<WDE);   //Watchdog registers: WDCE (Change Enable Bit), WDE (System Reset Enable Bit; both must be on)
  
  WDTCR = (1 << WDP0) | (1 << WDP1) | (1 << WDP2); // set WD to 2 seconds, see table
  
  WDTCR = (1<<WDIE);  //set WD to interrupt mode (WDIE)
  
  sei();  //interrupts on
}

/*
      time    const       Prescaler-Bits
                          WDP0  WDP1  WDP2  WDP3
      16 ms   WDTO_15MS   0     0     0     0
      32 ms   WDTO_30MS   1     0     0     0
      64 ms   WDTO_60MS   0     1     0     0
      0,125s  WDTO_120MS  1     1     0     0
      0,25s   WDTO_250MS  0     0     1     0
      0,5s    WDTO_500MS  1     0     1     0
      1,0s    WDTO_1S     0     1     1     0
      2,0s    WDTO_2S     1     1     1     0
      4,0s    WDTO_4S     0     0     0     1
      8,0s    WDTO_8S     1     0     0     1
      
 */

The WDT is slightly different from ATMega328 - I believe it is a slightly older version. Unless WDTON fuse is programmed the prescaler bits are not protected. This means

WDTCR = (1<<WDIE); //set WD to interrupt mode (WDIE)

clears the prescaler settings and the timeout is 16 ms. The LED is blinking but much faster than expected - so fast you cannot see it. Fix:

WDTCR = (1 << WDP0) | (1 << WDP1) | (1 << WDP2) | (1<<WDIE);

or

WDTCR = (1 << WDP0) | (1 << WDP1) | (1 << WDP2);
WDTCR |= (1<<WDIE);

Ah now I see :slight_smile: Thanks a lot for the explanation!

BTW: Do you happen to know if I call power_all_disable() in the code the delay via the timer is still possible? Or does it turn the timers off and I need to turn them back on?

And does anybody have a clue if I can possibly leave out the flag part? I don't see any reason why it needs to wait for the wdt to time out, since that same timeout wakes the system.

This section allows you to disable more peripherals

and yes, you can remove the wdtflag

ISR(WDT_vect){
 asm("NOP");
}

void loop(){
    //blink
    digitalWrite (LED_PIN, HIGH);
    delay (30);
    digitalWrite (LED_PIN, LOW);
    goToSleep();
  }
}

You can even reduce the goToSleep() function to something like

asm("NOP");
MCUCR = 1<<SE | 1<<SM1 | 0<<SM0;
asm("NOP");
asm("SLEEP");
void loop()
{
    //blink
    digitalWrite (LED_PIN, HIGH);
    delay (30);
    digitalWrite (LED_PIN, LOW);
    MCUCR = 1<<SE | 1<<SM1 | 0<<SM0;
    asm("NOP");
    asm("SLEEP");
    asm("NOP");
  }
}

Thanks a lot for the additional info! I see that I could reduce the code by using inline assembler instructions, but does that actually reduce power consumption?

Regarding the PRR: doesn't uncommenting power_all_disable() in my code do exactly that? Or is there any difference? I've come to think that I could disable almost anything except for timer0 in setup. Would you agree?

BTW: I read that in POWER_OFF state the current consumption should be around 1uA. Even with power_all_disable() and the fuses set to 0x62 (i.e. brown-out detector off) I get no lower than 4.5uA.

AFAIK the asm("NOP") instructions are not needed in the hzrnbgy's examples.

While in Power Down sleep PRR has no effect - it "only" disables clock to the selected peripherals but the clock is already disabled by the sleep. PRR is needed for Idle and Active modes only.

4.5 uA is expected value. You have <1 uA for Power Down sleep but you must add about 4 uA for running the WDT oscillator. If want even less current you must disable the WDT and use some external interrupt source.

EDIT: you don't have to wait for the flag as long as the WDT is the only wake up source. Otherwise you need a way to determine what caused the wake up interrupt.

Thanks again, guys! You helped me a lot! :slight_smile:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.