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...

  • 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. – bigjosh Apr 18 '17 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... – Em Ka Apr 18 '17 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? – bigjosh Apr 19 '17 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. – bigjosh Apr 19 '17 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. – Gerben Apr 19 '17 at 8:41
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;
    }
  • Accept your own answer to close this question. – user31481 Nov 16 '17 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();

}
  • 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 Apr 18 '17 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 Apr 18 '17 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. – Gerben Apr 19 '17 at 8:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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