AVR Watchdog as Sleep Delay
Use the AVR Watchdog Timer as a sleep delay. Put your ATtiny / ATmega to sleep and the Watchdog Timer will wake it up after a specified interval.
|oh no, it's the glowing, blinking eyes of a chupacabra!|
To enable interrupt mode and disable reset mode, you must:
- Disable the WDT Always On fuse on your microcontroller
- Within software, define a Watchdog Timer interrupt service routine (ISR)
- Define an init routine to:
- Disable interrupts
- Set the Watchdog Change Enable bit and within 4 clock cycles...
- Set the new Prescaler Select bits
- Set Watchdog Timer Interrupt Enable bit and unset Watchdog Enable (WDE) bit
- Enable interrupts again
The ATtiny13A datasheet, table 8-2 lists the prescaler bits and their corresponding prescale values. I chose a /64 value, corresponding to 1 second, with bits WDP2 and WDP1 set.
Watchdog Change EnableTo prevent accidentally disabling the Watchdog Timer, one must first set the Watchdog Change Enable (WDCE) bit and then make changes to WDE and/or WDP[3:0] within 4 clock cycles.
CodeYou can find the full source here.
Please note that, to conserve battery power, I am running the clock at 150kHz scaled down from 9.6MHz. The slow clock will cause problems flashing the chip. In AVR Studio 4, I set the ISP frequency very low so I could continue to flash the chip using my JTAG ICE MkII or AVR Dragon. If you're using avrdude you might be able to use either -B or -i flags. Since originally posting this article I've also added a delay before slowing down the clock which hopefully alleviates the problem but I haven't personally tested it. If you have working solutions please post to the comments.
My WDT initialization is as follows.
Every second after calling this routine, the Watchdog Timer will time out and call the ISR. The ISR counts the number of times the interrupt has fired in the variable sleep_interval.
According to the datasheet for the Tiny13A, the WDTIE bit is cleared after the interrupt fires and the Watchdog Timer goes into reset mode, apparently for increased reliability. To remain in interrupt mode, set WDTIE after the interrupt fires. If you're using the WDT to reset the MCU, then this shouldn't be done in the ISR. I'm not. So I did.
A sleep routine, which takes as an argument, the number of seconds to sleep repeatedly puts the microcontroller to sleep until sleep_interval matches the number of seconds of sleep requested.
The main routine calls init_wdt() then when it needs to sleep it calls sleep(n) where n is the number of seconds. The MCU remains in low power sleep, waking every second until the sleep time has elapsed.
The rand() stuff came out of the candle flicker code I grabbed and adds some psuedo-randomness to the various blinking intervals.
Watchdog on Other AVRsThe register names, interrupt names, and some behavior differs between AVR microcontrollers.
On ATtiny13A, WDTCR is used but it's WDTCSR on ATtiny84A and ATtiny2313 and other devices.
The Watchdog Timer interrupt vector name differs. On ATtiny13, ATtiny85, ATtiny328P it's WDT_vect. On the ATtiny84 family it's WATCHDOG_vect. The ATtiny2313 uses WDT_OVERFLOW_vect.
In terms of behavior, from AVR132, "If the WDTON fuse is unprogrammed on ATtiny13 and ATtiny2313, it is possible to change the WDT timeout period without..." setting WDCE and then setting the WDE bit.