I'm trying to wrap my head around the watchdog timer on the ATTinyX5 series. So things I've read made it seem like you could use it for making the program do something specific ever N seconds but never really showed how. Others made it seem like it would only reset the chip unless something in code reset it's count in the meantime (which seems to be the "normal" usage).
Is there any way of using the WDT like you would TIMER1_COMPA_vect or similar. I noticed that it has a 1 second timeout mode and I would really love to be able to use that to make something happen every 1 second in my code (and preferrably sleep in between).
Thoughts?
*Update: * Since it was asked, what I'm referring to is section 8.4 of the ATTinyX5 Datasheet. Not that I fully understand it, which is my problem...
Answer
You most certainly can. According to the datasheet, the watchdog timer can be setup to reset the MCU or cause an interrupt when it triggers. It seems you are more interested in the interrupt possibility.
The WDT is actually easier to setup than a normal Timer for the same reason it is less useful: fewer options. It runs on an internally calibrated 128kHz clock, meaning its timing is not effected by the main clock speed of the MCU. It can also continue to run during the deepest sleep modes to provide a wake up source.
I will go over a couple of the datasheet examples as well as some code I have used (in C).
Included Files and Definitions
To start, you will probably want to include the following two header files for things to work:
#include // Supplied Watch Dog Timer Macros
#include // Supplied AVR Sleep Macros
Also, I use the Macro <_BV(BIT)> which is defined in one of the standard AVR headers as the following (which might be more familial to you):
#define _BV(BIT) (1<
Beginning of Code
When the MCU is first started, you would typically initialize the I/O, set up timers, etc. Somewhere here is a good time to make sure the WDT didn't cause a reset because it could do it again, keeping your program in an unstable loop.
if(MCUSR & _BV(WDRF)){ // If a reset was caused by the Watchdog Timer...
MCUSR &= ~_BV(WDRF); // Clear the WDT reset flag
WDTCSR |= (_BV(WDCE) | _BV(WDE)); // Enable the WD Change Bit
WDTCSR = 0x00; // Disable the WDT
}
WDT Setup
Then, after you have setup the rest of the chip, redo the WDT. Setting up the WDT requires a "timed sequence," but it is really easy to do...
// Set up Watch Dog Timer for Inactivity
WDTCSR |= (_BV(WDCE) | _BV(WDE)); // Enable the WD Change Bit
WDTCSR = _BV(WDIE) | // Enable WDT Interrupt
_BV(WDP2) | _BV(WDP1); // Set Timeout to ~1 seconds
Of course, your interrupts should be disabled during this code. Be sure to re-enable them afterwards!
cli(); // Disable the Interrupts
sei(); // Enable the Interrupts
WDT Interrupt Service Routine The next thing to worry about is handling the WDT ISR. This is done as such:
ISR(WDT_vect)
{
sleep_disable(); // Disable Sleep on Wakeup
// Your code goes here...
// Whatever needs to happen every 1 second
sleep_enable(); // Enable Sleep Mode
}
MCU Sleep
Rather than put the MCU to sleep inside of the WDT ISR, I recommend simply enabling the sleep mode at the end of the ISR, then have the MAIN program put the MCU to sleep. That way, the program is actually leaving the ISR before it goes to sleep, and it will wake up and go directly back into the WDT ISR.
// Enable Sleep Mode for Power Down
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set Sleep Mode: Power Down
sleep_enable(); // Enable Sleep Mode
sei(); // Enable Interrupts
/****************************
* Enter Main Program Loop *
****************************/
for(;;)
{
if (MCUCR & _BV(SE)){ // If Sleep is Enabled...
cli(); // Disable Interrupts
sleep_bod_disable(); // Disable BOD
sei(); // Enable Interrupts
sleep_cpu(); // Go to Sleep
/****************************
* Sleep Until WDT Times Out
* -> Go to WDT ISR
****************************/
}
}
No comments:
Post a Comment