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...
2 Answers 2
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;
}
-
Accept your own answer to close this question.user31481– user314812017年11月16日 07:14:43 +00:00Commented Nov 16, 2017 at 7:14
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();
}
-
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.Em Ka– Em Ka2017年04月18日 16:35:07 +00:00Commented 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.bigjosh– bigjosh2017年04月18日 23:02:08 +00:00Commented 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 callingWDTCR &= ~_BV(WDIE);
is pointless.Gerben– Gerben2017年04月19日 08:52:38 +00:00Commented Apr 19, 2017 at 8:52
switch
with the same prescaller? Do you get the same result when you add a fewnop
s before the the line that sets the prescaller in the orginal version?setup
is to disable the WDT.