Friday, November 1, 2013

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!
This is the technique I used for my lost R/C airplane model alarm. I also used it last night for my Halloween glowing, blinky eyes. Here's how.
Two sources of documentation are your friend. Your AVR's datasheet and App Note AVR132 [pdf], "Using the Enhanced Watchdog Timer". Without these documents you will miss the subtle differences between the Watchdog Timers on different devices.

The Watchdog Timer on newer AVR devices can be configured to either reset, fire an interrupt, or both, upon timeout of the Watchdog Timer. We want interrupt mode, only. Let's talk about implementing the specifics of the ATiny13A [datasheet.pdf] since that's what I've used so far.

Overview

You need a sleep function to put the microcontroller to sleep for a specified time period. The Watchdog Timer runs in interrupt mode so it only wakes up the sleeping microcontroller periodically. And an Interrupt Service Routine keeps track of elapsed time.

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 bits to be set are all found in the Watchdog Timer Control Register (WDTCR).

Interrupt Mode

You disable reset mode by clearing the Watchdog Enable (WDE). Enable interrupt mode by setting the Watchdog Timer Iterrupt Enable bit (WDTIE).

WDT Prescaler

The Watchdog Timer's 128kHz internal oscillator can timeout anywhere from every 16ms to every 8 seconds by changing the Watchdog Prescale bits, WDP[3:0].

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 Enable

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

Code

You 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 AVRs

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

No comments:

Post a Comment