Time to visit an old and sometimes unappreciated friend, timers. We use timers for just about everything. They are the only real thing that links the digital work and ours, time. Since a MCU really has no concept of time, most of the time we want things done as soon as possible, but other times we want predictable events. Most of these events happen by way of interrupts and use of precise time counting on the timer. Your probably all too familiar with timer, so I won't bore you with details. Let's go straight into looking at what it takes to create a timer interrupt on the AVR ATMega32 running an external oscillator of 8 MHz. We'll do this the traditional way.
Problem: We need a timer that runs an ISR every 10ms.
Solution:
Let's look at the Timer 0 first. It is an 8 bit timer which means its' MAX is 255. At 8 Mhz our timer would reach MAX in:
255 / 8000000 = 0.000031875 or 3.18 microseconds. Hmm, better put a pre-scaler on it. Let's jump to the largest pre-scale of 1024.
( 255 * 1024 ) / 8000000 = .03264 or 32.64 milliseconds. Great, our 10ms fits into the Timer 0 window.
( x * 1024 ) / 8000000 = .010 and solve for x. X = 78.125 which means our timer will be 9.984ms. Error of 1% is close enough
Our timer needs to match a TCNT of X - 1 = 77 and trigger the ISR. Need to dive into the datasheet to find the registers that control the Timer 0.
Looks like TCCR0 is going to be the control register and needs to be configured with a 1024 pre-scaler
TCCR0 |= ( 1 << CS00 ) | ( 1 << CS02 ); // Set to 1024 pre-scale
Next is to set the mode. We want the timer to get to our match and reset. That feature is part of the "waveform generation" section. Since our TOP value is going to be our TCNT of 77, then we need CTC mode:
TCCR0 |= ( 1 << WGM00 ); // Set mode to CTC
Let's set our top value of 77. To do that we set the OCR0 register to 77.
OCR0 = 77; // Set new TOP to 77
Last, we need to enable the interrupt. That is in the TIMSK timer interrupt mask register.
TIMSK |= ( 1 << OCIE0 ); // Enable compare match interrupt
Great. Next we need to enable global interrupts.
SREG |= ( 1 << I ); // Enable global interrupts
Finally, we need an ISR. Returning to the datasheet we need to find the interrupt vector that the timer 0 compare resides. Ah, found it, IVT_ADDR_TIMER0_COMP.
void my_cool_timer_ISR() org IVT_ADDR_TIMER0_COMP { //do something amazing every 10ms }
What a pain in the butt. This is on an 8 bit MCU that is really straight forward. Can you imagine the complexity of a 32 bit MCU with 12 timers with multiple channels and flags that need to be cleared? Children are crying now. What have you done! What if I were to make this process so easy, that you would cry and thank the digital gods? Will the real Timer Calculator please stand up.
A few years ago MikroElektronika quietly released a development tool that shook up the embedded world in a small way. Even though it is considered one of my most valuable tools, it still goes unsung by some.
Be forewarned. Once I show you this, you may never be the same. Those who are just studying computer science or embedded systems might want to turn away.
Step 1. Download, unpack, install
Step 2. Open Timer Calculator
Step 3. Select Device
You'll immediately notice that there are not only several MCU families but also families within those families. This isn't just a tool for AVR but for all. With AVR you have several choices, but in our case we have a ATMega32 with 3 timers. The correct choice is ATMega - 3 timers.
Step 4. Input MCU clock frequency
Step 5. Select which timer to use
Step 6. Interrupt time required
Step 7. Click calculate
What is this! It's a FYI window. Are you ok with the error?
Step 8. Copy / Paste
Step 9. Enjoy
Not only do you get the proper output, but you get it in 3 language flavors. Could you want more? While this is just a simple tool, it happens to be one of my most commonly used ones. I use it because it saves me time. The datasheet is fun to dive into and soak up the pages, but having to do it for everything tends to be a little nagging. This keeps my relationship with the datasheet healthy.