I've been using AVR microcontrollers all summer, so at this point I'm pretty comfortable with the coding and on to writing more readable and efficient code. For a pet project, I spent part of this week learning to use the Watchdog Timer (WDT), which is in large part what I'll be writing about here.
So, here's the goal: I want to be able to control PWM with a couple of buttons to raise and lower the average voltage. I'm running the PWM on the tiny45's Timer/Counter0, an 8-bit timer/counter which can implement PWM or frequency generation. In order to free up the 16-bit Timer/Counter1 for other applications, I thought I'd try something different: waking the microcontroller from sleep using the watchdog timer, which has a much longer period.
The watchdog timer seems to be intended as a security measure: In order to prevent people from doing unwanted things with the microcontroller, and deal with accidental infinite loops, it will force a reset of the microcontroller after the timeout time unless the counter is cleared. For my uses, I'm using it as a pseudo-RTC (real-time clock), in order to poll two buttons every ~15ms to see whether either is pressed.
// Set up Watch Dog Timer for wakeup every ~15ms
WDTCR = (1 << WDIE); // Enable WDT Interrupt
CLEAR(WDTCR, 0b1111 << WDP0); // Ensure the prescaler is set to 16ms
Turning off the watchdog timer is a protected action (there are timing requirements for clearing the WDE [Watchdog Enable] bit), but turning it on is pretty easy: unless you're using it in reset mode, you just need to write a single bit and select a prescaler option.
Oh yeah, did I mention that I was trying to write readable code? This is a very bad example, as all I've done to make it better is hide some of the bit-smashing in a simple macro to write 0's to bits in a register:
#define CLEAR(register, value) register &= ~value. I wrote a similar thing for writing 1's,
In order to implement the interrupt, all I actually needed to do was add a blank ISR: in this particular program, all the WDT was doing was waking the microcontroller from sleep mode.
// Sleep until interrupted
I should mention here that the sleep code was simple, if interesting. I switched the sleep modes by directly writing the control register, MCUCR, with the right values in the Sleep Mode bits, SM[1:0]. Then, I included a small bit of code in the main loop of my program in order to make it sleep after doing its work.
All of these functions are included in the
library except for
sei(), which is the function enabling interrupts, and is found in
There is, in fact, a Watchdog Timer library in the AVR standard libraries (avr-libc),
, but it doesn't include the interrupt mode, so I couldn't really use it. A great resource that people can use is this StackExchange question,which is abnormally complete and describes the interrupt mode very well.
I think that I did have some issues the first time I used the fabISP, but I solved them by installing a signed USBtinyISP driver. I found this toolchain pretty straightforward--although I'd do well to figure out how to write a makefile that could change fuse settings, this is enough to be able to program chips easily and quickly, and is therefore good for our quick-turnaround fabrication.