16.8.1 Overview

The device has an Enhanced Watchdog Timer (WDT). The WDT is a timer counting cycles of a separate on-chip 128kHz oscillator. The WDT gives an interrupt or a system reset when the counter reaches a given time-out value. In normal operation mode, it is required that the system uses the Watchdog Timer Reset (WDR) instruction to restart the counter before the time-out value is reached. If the system doesn't restart the counter, an interrupt or system reset will be issued.

Figure 16-7. Watchdog Timer

In Interrupt mode, the WDT gives an interrupt when the timer expires. This interrupt can be used to wake the device from sleep-modes, and also as a general system timer. One example is to limit the maximum time allowed for certain operations, giving an interrupt when the operation has run longer than expected. In System Reset mode, the WDT gives a reset when the timer expires. This is typically used to prevent system hang-up in case of runaway code. The third mode, Interrupt and System Reset mode, combines the other two modes by first giving an interrupt and then switch to System Reset mode. This mode will for instance allow a safe shutdown by saving critical parameters before a system reset.

The Watchdog always on (WDTON) fuse, if programmed, will force the Watchdog Timer to System Reset mode. With the fuse programmed the System Reset mode bit (WDE) and Interrupt mode bit (WDIE) are locked to 1 and 0 respectively. To further ensure program security, alterations to the Watchdog set-up must follow timed sequences. The sequence for clearing WDE and changing time-out configuration is as follows:

  1. In the same operation, write a logic one to the Watchdog change enable bit (WDCE) and Watchdog System Reset Enable (WDE) in Watchdog Timer Control Register (WDTCSR.WDCE and WDTCSR.WDE). A logic one must be written to WDTCSR.WDE regardless of the previous value of the WDTCSR.WDE.
  2. Within the next four clock cycles, write the WDTCSR.WDE and Watchdog prescaler bits group (WDTCSR.WDP) as desired, but with the WDTCSR.WDCE cleared. This must be done in one operation.

Assembly Code Example

The following examples show a function for turning off the Watchdog Timer. The examples assume that interrupts are controlled (e.g. by disabling interrupts globally) so that no interrupts will occur during the execution of these functions.

WDT_off:
   ; Turn off global interrupt
   cli
   ; Reset Watchdog Timer
   wdr
   ; Clear WDRF in MCUSR
   in      r16, MCUSR
   andi    r16, (0xff & (0<<WDRF))
   out     MCUSR, r16
   ; Write '1' to WDCE and WDE
   ; Keep old prescaler setting to prevent unintentional time-out
   lds     r16, WDTCSR
   ori     r16, (1<<WDCE) | (1<<WDE)
   sts     WDTCSR, r16
   ; Turn off WDT
   ldi     r16, (0<<WDE)
   sts     WDTCSR, r16
   ; Turn on global interrupt
   sei
   ret
void WDT_off(void)
{
    __disable_interrupt();
    __watchdog_reset();
    /* Clear WDRF in MCUSR */
    MCUSR &= ~(1<<WDRF);
    /* Write logical one to WDCE and WDE */
    /* Keep old prescaler setting to prevent unintentional time-out */
    WDTCSR |= (1<<WDCE) | (1<<WDE);
    /* Turn off WDT */
    WDTCSR = 0x00;
    __enable_interrupt();
}
Note: If the Watchdog is accidentally enabled, for example by a runaway pointer or brown-out condition, the device will be reset and the Watchdog Timer will stay enabled. If the code is not set up to handle the Watchdog, this might lead to an eternal loop of time-out resets. To avoid this situation, the application software should always clear the Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialization routine, even if the Watchdog is not in use.

Assembly Code Example

The following code examples shows how to change the time-out value of the Watchdog Timer.

WDT_Prescaler_Change:
   ; Turn off global interrupt
   cli
   ; Reset Watchdog Timer
   wdr
   ; Start timed sequence
   lds r16, WDTCSR
   ori r16, (1<<WDCE) | (1<<WDE)
   sts WDTCSR, r16
   ; -- Got four cycles to set the new values from here -
   ; Set new prescaler(time-out) value = 64K cycles (~0.5 s)
   ldi r16, (1<<WDE) | (1<<WDP2) | (1<<WDP0)
   sts WDTCSR, r16
   ; -- Finished setting new values, used 2 cycles -
   ; Turn on global interrupt
   sei
   ret
void WDT_Prescaler_Change(void)
{
    __disable_interrupt();
    __watchdog_reset();
    /* Start timed sequence */
    WDTCSR |= (1<<WDCE) | (1<<WDE);
    /* Set new prescaler(time-out) value = 64K cycles (~0.5 s) */
    WDTCSR = (1<<WDE) | (1<<WDP2) | (1<<WDP0);
    __enable_interrupt();
}
Note: The Watchdog Timer should be reset before any change of the WDTCSR.WDP bits, since a change in the WDTCSR.WDP bits can result in a time-out when switching to a shorter time-out period.