1

I have a project in which I am putting my ATtiny85 to sleep. Here is the watchdog configuration (in the setup) that works just fine:

void setup()
{
 /* Setup the LCD */
 lcd.init(); // initialize the lcd
 lcd.backlight(); // Print a message to the LCD.
 lcd.clear();
 lcd.setCursor(0,0);
 /* Inform the user about initialization process */
 lcd.print("Initializing...");
 delay(1000);
 /* Config pins */
 pinMode( LED_PIN, INPUT );
 pinMode( STATUS_PIN, OUTPUT );
 /* Initialize pins */
 digitalWrite( STATUS_PIN, HIGH );
 /*** Setup the WDT ***/
 /* Clear the reset flag of watchdog interrupts, so that they are available again. */
 MCUSR &= ~(1<<WDRF);
 /* Enable configuration changes */
 WDTCR |= (1<<WDCE) | (1<<WDE);
/*
WDP3 WDP2 WDP1 WDP0 Number of WDT Typical Time-out at
 Oscillator Cycles VCC = 5.0V
 0 0 0 0 2K (2048) cycles 16 ms
 0 0 0 1 4K (4096) cycles 32 ms
 0 0 1 0 8K (8192) cycles 64 ms
 0 0 1 1 16K (16384) cycles 0.125 s
 0 1 0 0 32K (32768) cycles 0.25 s
 0 1 0 1 64K (65536) cycles 0.5 s
 0 1 1 0 128K (131072) cycles 1.0 s
 0 1 1 1 256K (262144) cycles 2.0 s
 1 0 0 0 512K (524288) cycles 4.0 s
 1 0 0 1 1024K (1048576) cycles 8.0 s
*/
 /* set new watchdog timeout prescaler value */
 WDTCR = 0<<WDP3 | 1<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 lcd.clear();
 lcd.print("Initialisation");
 lcd.setCursor(0,1);
 lcd.print("complete.");
 delay(500); 
}

Here is the function that actually puts my microcontroller to sleep:

void enterSleep(void)
{
// /* disable all interrupts */
// cli();
 lcd.clear();
 lcd.print("Asleep...");
 /* Enable the WD interrupt (note no reset). */
 WDTCR |= (1<<WDIE);
 set_sleep_mode( SLEEP_MODE_PWR_DOWN ); /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
 sleep_enable();
 // Status pin goes low
 digitalWrite( STATUS_PIN, LOW );
 /* Now enter sleep mode. */
 sleep_mode();
 /* The program will continue from here after the WDT timeout*/
 sleep_disable(); /* First thing to do is disable sleep. */
 // Status pin goes low
 digitalWrite( STATUS_PIN, HIGH );
 /* Re-enable the peripherals. */
 power_all_enable();
 /* Disable the WD interrupt */
 WDTCR &= ~_BV(WDIE);
}

Then I decided to do a switch case construction, so that I won't have to change manually WDTCR register each time I want to change the sleep time. So here is my new setup code:

int sleepTime = 0;
void setup()
{
 /* Setup the LCD */
 lcd.init(); // initialize the lcd
 lcd.backlight(); // Print a message to the LCD.
 lcd.clear();
 lcd.setCursor(0,0);
 /* Inform the user about initialization process */
 lcd.print("Initializing...");
 delay(1000);
 /* Config pins */
 pinMode( IN_PIN, INPUT );
 pinMode( STATUS_PIN, OUTPUT );
 pinMode( DEBUG_PIN, OUTPUT );
 /* Initialize pins */
 digitalWrite( STATUS_PIN, HIGH );
 digitalWrite( DEBUG_PIN, LOW );
 /*** Setup the WDT ***/
// sleepDevice.watchdogInit();
 /*** Setup the WDT ***/ 
 /* Clear the reset flag of watchdog interrupts, so that they are available again. */
 MCUSR &= ~(1<<WDRF);
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 /*
 WDP3 WDP2 WDP1 WDP0 Number of WDT Typical Time-out at
 Oscillator Cycles VCC = 5.0V
 0 0 0 0 2K (2048) cycles 16 ms
 0 0 0 1 4K (4096) cycles 32 ms
 0 0 1 0 8K (8192) cycles 64 ms
 0 0 1 1 16K (16384) cycles 0.125 s
 0 1 0 0 32K (32768) cycles 0.25 s
 0 1 0 1 64K (65536) cycles 0.5 s
 0 1 1 0 128K (131072) cycles 1.0 s
 0 1 1 1 256K (262144) cycles 2.0 s
 1 0 0 0 512K (524288) cycles 4.0 s
 1 0 0 1 1024K (1048576) cycles 8.0 s
 */
 switch( sleepTime ){
 case 0:
 WDTCR = 0<<WDP3 | 1<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 case 1:
 WDTCR = 0<<WDP3 | 1<<WDP2 | 0<<WDP1 | 1<<WDP0 ;
 break;
 default:
 WDTCR = 0<<WDP3 | 0<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 }
 lcd.clear();
 lcd.print("Initialisation");
 lcd.setCursor(0,1);
 lcd.print("complete.");
 delay(500); 
}

The enterSleep function is not changed. But this time sleeping does not work at all! In fact, ATtiny sort of freezes. Does any one has any idea why it is so? To me it looks like the Arduino IDE is confused with switch case instruction, but maybe there is something wrong with my code...

asked Apr 18, 2017 at 15:13
6
  • 1
    Several things going on here. First, you do not need to fiddle with the change enable bit to change the prescaller. Second, you do not know the value of the watchdog timer at any point in your code, so random stuff can happen. I would (1) disable to watchdog timer completely, (2) update the prescaller bits (3) only just before sleeping (a) clear the watchdog counter with a wdt_reset(), (b) then enable to WDT and the interrupt. (c) sleep. On waking, disable the WDT immediately. Commented Apr 18, 2017 at 15:51
  • @bigjosh But why is it so, that everything works fine when I don't use switch? As soon as I use it, nothing works... Commented Apr 18, 2017 at 16:25
  • The switch statement is at least one additional compare and branch compared to the original code. This means more cycles, which may be enough for the WDT to expire. Do you get the result when you run the switch with the same prescaller? Do you get the same result when you add a few nops before the the line that sets the prescaller in the orginal version? Commented Apr 19, 2017 at 2:14
  • Also keep in mind that you are enabling config changes and then entering the switch statement, but you have no way to ensure that all cases of the switch will execute within the 4 cycle window that opens after the enable bit is written. I'd start with the simplest case and work your way back - too much unpredictable stuff going on here to be able to pin down which of the problems you are actually seeing. Commented Apr 19, 2017 at 2:17
  • 1
    Note that if the Watchdog is enabled, it keeps being enabled, even after a reset. This can be especially problematic, if you set it to a very short time. So the very first thing you should do in your setup is to disable the WDT. Commented Apr 19, 2017 at 8:41

2 Answers 2

1

Alright, thanks to you guys I fixed the issue. The main thing is that any changes in the configuration have to be made within 4 cycles after the CONFIG ENABLE bit is set. It is done with this instruciton:

/* Enable configuration 
WDTCR |= (1<<WDCE) | (1<<WDE);

Now switch works like a charm. So the final code for watchdog setup looks like this:

void Sleeper::watchdogInit( void ){
 /*** Setup the WDT ***/ 
 /* Clear the reset flag of watchdog interrupts, so that they are available again. */
 MCUSR &= ~(1<<WDRF);
 /*
 WDP3 WDP2 WDP1 WDP0 Number of WDT Typical Time-out at
 Oscillator Cycles VCC = 5.0V
 0 0 0 0 2K (2048) cycles 16 ms
 0 0 0 1 4K (4096) cycles 32 ms
 0 0 1 0 8K (8192) cycles 64 ms
 0 0 1 1 16K (16384) cycles 0.125 s
 0 1 0 0 32K (32768) cycles 0.25 s
 0 1 0 1 64K (65536) cycles 0.5 s
 0 1 1 0 128K (131072) cycles 1.0 s
 0 1 1 1 256K (262144) cycles 2.0 s
 1 0 0 0 512K (524288) cycles 4.0 s
 1 0 0 1 1024K (1048576) cycles 8.0 s
 */
 switch( sleepTime ){
 case 0:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 1<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 case MSEC_500:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 1<<WDP2 | 0<<WDP1 | 1<<WDP0 ;
 break;
 case MSEC_250:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 1<<WDP2 | 0<<WDP1 | 0<<WDP0 ;
 break;
 case MSEC_125:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 0<<WDP2 | 1<<WDP1 | 1<<WDP0 ;
 break;
 case MSEC_64:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 0<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 case MSEC_32:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 0<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 default:
 /* Enable configuration changes*/
 WDTCR |= (1<<WDCE) | (1<<WDE);
 WDTCR = 0<<WDP3 | 0<<WDP2 | 1<<WDP1 | 0<<WDP0 ;
 break;
 }
answered Apr 19, 2017 at 15:12
1
  • Accept your own answer to close this question. Commented Nov 16, 2017 at 7:14
0

Try this for your sleep function...

void enterSleep(void)
{
 lcd.clear();
 lcd.print("Asleep...");
 // Status pin goes low
 digitalWrite( STATUS_PIN, LOW );
 /* disable all interrupts, if we get interrupted we might timeout the WDT */
 cli();
 /* Reset the watchdog counter so we know that we are starting at 0 */
 wdt_reset()
 /* Enable the WD interrupt (note no reset). */
 WDTCR |= (1<<WDIE);
 set_sleep_mode( SLEEP_MODE_PWR_DOWN ); /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
 sleep_enable();
 sei();
 /* Now enter sleep mode. */
 sleep_cpu();
 cli(); // Make sure we have time to disable WDT before we get reset
 /* Disable the WD interrupt */
 WDTCR &= ~_BV(WDIE);
 /* The program will continue from here after the WDT timeout*/
 sleep_disable(); /* First thing to do is disable sleep. */
 sei(); 
 // Status pin goes low
 digitalWrite( STATUS_PIN, HIGH );
 /* Re-enable the peripherals. */
 power_all_enable();
}
answered Apr 18, 2017 at 16:16
3
  • Problem is that the whole program freezes right after I setup the watchdog prescaler with switch case. Even if I never use enterSleep function, it still does happen. Commented Apr 18, 2017 at 16:35
  • If the wdt is enabled when you change the prescaler, it is possible the whatever the current value of the counter is happens to be above the new prescaler, which would cause an instant wdt timeout. That is why it is important to always clear the counter before updating the prescaller- and to do it when no interrupts are possible so the processor doesn't get sidetracked in between the steps. Commented Apr 18, 2017 at 23:02
  • 1
    If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode. The first time-out in the Watchdog Timer will set WDIF. Executing the corresponding interrupt vector will **clear WDIE** and WDIF automatically by hardware, so calling WDTCR &= ~_BV(WDIE); is pointless. Commented Apr 19, 2017 at 8:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.