Embedded Programming

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.

My test board

The test board: This is the board that I was using to test my code--it's pretty simple, just an ATtiny45 microcontroller PWMing a white LED, controlled by two buttons.

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 AVR Watchdog Timer

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, SET.

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.

Workflow and Toolchain

A screencap of Atmel Studio

Atmel Studio: I worked on my Windows laptop on Atmel Studio, Atmel's IDE for AVR. Here's a screencap of the main workflow window. Perhaps most useful is the ability to specify device settings directly in the IDE, but also important is the automatic generation of makefiles and compilation.

A screencap of compiler results

Compiler results: The makefile works well, and Atmel Studio outputs directly into this box when you hit the "Build Solution" box.

Screencap of program upload results

Avrdude: The AVR Downloader/UploaDEr is a very useful and effective tool, and its outputs are verbose enough to diagnose issues.

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.