Wow! It has been a while. I finally decided to write another one of these. I promised a long time ago to write about timers. Well, almost 4 months later, here we go!
Timers are extremely useful peripherals often built into microcontrollers. Are you going to be doing anything that involves timing or delays in your microcontroller program? Are you going to:
- Increment a variable every second?
- Measure the length of time a button is pressed down?
- Wait for 20 milliseconds?
- Blink a light every half second?
If you are doing any of the above, or anything even remotely related, you are probably going to be using a timer. A lot of desktop frameworks have a similar kind of setup — Qt, for example, has the QTimer class which allows you to schedule something to happen in the future. .NET has a Timer class that does the same thing. And as you can guess, Mac OS X’s Foundation framework has the NSTimer class. These tend to use lower-level operating system routines, which in turn use hardware timers, and they will run a handler inside your event loop when they fire. You don’t have to know how to use the hardware timers though–the operating system handles it for you. Well, in a microcontroller, you will directly use a timer peripheral to do similar tasks.
Timers can generally be put into several different modes. Some microcontrollers have more complex and powerful timers than others. I’m only going to focus on basic timer concepts here. The first thing you need to know is that timers are usually memory-mapped peripherals built into the microcontroller. They have various configuration registers you can access. The most important register is the counter register. As you can guess, it counts. It holds the timer’s current value, and while the timer is running, it is constantly being incremented or decremented, based on what direction the timer counts. Some timers count up, some timers count down. Some even allow you to set which direction it counts. It doesn’t really matter what direction a timer counts, because you can do all the same things with it either way.
Anyway, so I said this counter register counts up or down, depending on the timer. Let’s pretend that it counts up, for the sake of explaining how this timer works. So the counter starts at zero, and then after a certain amount of time, it will be incremented to 1, and then 2, and so on. How often is it incremented? This is the key to how timers work! Generally the timer runs at a fraction of the system clock rate. So if your microcontroller is running at 100 MHz, the timer might run at 25 MHz. In many microcontrollers, this is configurable. You might be able to set the timer to also run at 100 MHz, or 50 MHz, or 12.5 MHz. Let’s assume that our microcontroller is running at 100 MHz and its timer runs at 25 MHz. This means that in one second, it will be incremented 25 million times. That’s a lot! Alternatively, we could say the counter will be incremented every 1/25,000,000 seconds, or every 40 nanoseconds.
My example here was a little crazy. It’s very doubtful that you need resolution that high. Generally timers, particularly fast ones such as the 25 MHz one we are using here as an example, also have a divider. The divider does exactly what it sounds like — it divides. It does it like this — it has a divider counter register which counts up every time the timer would count. After the divider counter gets high enough, the main timer counter register is finally incremented and the divider counter register is reset to zero. What this does in effect is makes the timer count slower. In this case, let’s say our divider is 25. This means that every 25 ticks of the 25 MHz timer clock, our final counter register is incremented, effectively making it into a 1 MHz clock instead of a 25 MHz clock. It’s just a nice way to slow down the counting. The value the divider will count up to is a value configurable in a register.
So let’s say we’ve configured the timer as follows: the CPU is at 100 MHz, the timer is at 25 MHz, and the timer’s divider is set to 25. Thus, the timer’s counter register is incremented at a rate of 1 MHz, or 1 million times per second. I like making my timers count at nice, even intervals like this because it makes it easier to do the math in your head to figure out how many counts it will take for a certain amount of time to pass. You can do it however you’d like, though!
The only other thing I really need to cover before we jump into the basics is: how to tell the timer to start counting. Timers generally have a configuration register of some kind, and one of the bits in it tells the timer to start counting. For this timer peripheral, let’s say if bit 0 of the timer config register is a 1, it is counting, and if it’s a zero, it’s not counting.
If the timer is configured this way, you now can do some very basic timing. Let’s say all your program does is turn on an LED for a second, then turn it off for a second, and repeat the process. So for our example, we have an LED connected to PORT A, pin 0. Here’s some sample code (keep in mind that TIMER_CONFIG, TIMER_DIVISOR, TIMER_DIVISOR_COUNTER, and TIMER_COUNTER are the registers for the timer):
// Set port A, pin 0 as an output. DDRA |= 0x01; // Turn it off. PORTA &= ~0x01; // Make sure the timer is turned off TIMER_CONFIG &= ~0x01; // Configure the timer to have a divisor of 25, and reset all the counters. TIMER_DIVISOR = 25; TIMER_DIVISOR_COUNTER = 0; TIMER_COUNTER = 0; // Start the timer (bit 0 of the timer flag register turns it on, in our example) TIMER_CONFIG |= 0x01; // Initialize this variable -- it will contain the time we last did something. uint32_t lastTimeValue = TIMER_COUNTER; // Do this forever... while (1) { // Look at the counter's value now. uint32_t nowTimeValue = TIMER_COUNTER; // Has it been 1 million ticks since the last time we toggled the LED? // (1 million ticks = 1 second) if ((nowTimeValue - lastTimeValue) >= 1000000) { // Toggle pin 0 of port A (^ is the XOR operator, // think about how it works!) PORTA ^= 0x01; // Update our "last time" variable to contain this time. lastTimeValue = nowTimeValue; } }
Okay, did you follow along? This program will do nothing except toggle that LED on and off. It will toggle it every second. A couple of notes: the data type uint32_t is brought in by including the header file stdint.h. It’s just a portable way of specifying that it’s a 32-bit unsigned value. I could have used unsigned long instead, but I like using uint8_t, uint16_t and uint32_t (and their signed equivalents — int8_t, int16_t, and int32_t) because they make it obvious how big each variable is.
Also, the XOR operator (^) will toggle bits. Remember that PORTA ^= 0x01 is equivalent to: PORTA = PORTA ^ 0x01. This will turn bit 0 on if it’s off, and off if it’s on.
So this works and is fine and dandy, but it takes up the entire main loop of the program! Well, you can put other things to do in the main loop, but if you do it that way, you may not toggle the LED after exactly 1 second, especially depending on what other CPU-intensive things you’re doing in the main loop. This may not matter for something as simple as a blinking light, but for other tasks it could be very important. If you need more precise timing, you need to set the timer to use interrupts instead.
Most timers support a mode where it’s constantly checking the counter to see if it matches another value you specify, and once the counter matches it, it interrupts. This would be the mechanism you would use to do a precise timing. In this case, you have another register called the match value register. You would set your match value register to the value you want the counter to match — 1000000 in this case. What happens after this may depend on the timer. Some timers may have an option to automatically reset the counter back down to zero (or another specified value) after it reaches the match value. In other timers, you might have to adjust the match value instead. I’ll cover both methods so you see what I mean. Assume that the timers and GPIO have been configured as I had them before.
Program 1 (assuming the timer automatically resets the counter back to zero whenever a match occurs):
int main(void) { // All the previous configuration stuff goes here, // setting up the GPIO and timer (but don't start the timer yet...) // Set the timer match value to 1000000, so it interrupts every 1 second. TIMER_MATCH = 1000000; // Start the timer TIMER_CONFIG |= 0x01; // Now our main loop literally does nothing! while (1) { } } // This is where the interrupt will jump to -- may need to specify // to your compiler that it is an interrupt handler. void TIMER_INT_HANDLER(void) { PORTA ^= 0x01; }
I may have to explain TIMER_INT_HANDLER to you. This is heavily microcontroller-dependent, but basically you need a way of specifying to the microcontroller what to do when an interrupt occurs. You do this by creating an interrupt handler, and then the address of it gets put into a table of addresses to jump to when a specific type of interrupt occurs (called a vector table). For now, just pretend that TIMER_INT_HANDLER has been properly set up as an interrupt handler for the timer peripheral. I’ll cover that later.
This is a lot simpler! You can put all kinds of other stuff in the main loop, and that interrupt will occur at almost precisely every second. It may vary just a little based on if you have disabled interrupts anywhere in your main loop for interrupt safety (as I mentioned in my last post on interrupts).
Now, here’s another code sample for if your microcontroller does not automatically reset your timer’s counter back to zero after a match occurs:
Program 2 (assuming the timer does *not* reset the counter back to zero when a match occurs):
int main(void) { // All the previous configuration stuff goes here, // setting up the GPIO and timer (but don't start the timer yet...) // Set the timer match value to 1000000, so it interrupts every 1 second. TIMER_MATCH = 1000000; // Start the timer TIMER_CONFIG |= 0x01; // Now our main loop literally does nothing! while (1) { } } // This is where the interrupt will jump to -- may need to specify // to your compiler that it is an interrupt handler. void TIMER_INT_HANDLER(void) { TIMER_MATCH += 1000000; PORTA ^= 0x01; }
The difference is in TIMER_INT_HANDLER. I added 1,000,000 to the match value, so now it will match again 1,000,000 ticks (1 second) after the last match occurred. In this case, the counter register continues counting past 1,000,000, so by moving the match value up another 1,000,000, it will catch the counter when it gets to 2,000,000. Get it? It’s nothing really difficult, but the first option I showed earlier is easier if your timer peripheral automatically resets the counter back down to zero after a match. So if you have to do it this way, this is how you do it–otherwise, I’d recommend the first approach. You may be asking me: in this method, why didn’t you just set TIMER_COUNTER to zero instead of add 1,000,000 to TIMER_MATCH? Wouldn’t that have the same effect? You may think that it would, but it actually does not. Here’s why:
Between when the timer signals that a match occurred and we actually get to execute the interrupt handler, some time passes by. If the timer itself resets the counter back to zero, this is no problem, because it can reset the counter instantly as soon as the match happens. But if we try to do it ourselves manually, we will only reset it back to zero after so much time has already passed. In other words, the counter might actually be up to 1,000,002 or so before we tell it to go back to zero. In effect, this causes us to toggle the LED every 1 second PLUS however long on average it takes to get to that first instruction that resets the counter. By adding 1,000,000 to the TIMER_MATCH register instead, it guarantees that the next match will occur exactly 1 second after the last match occurred. This may not matter for something as simple as toggling an LED, but if you add up that extra delay over time in a very time-sensitive algorithm, it would cause inaccuracy.
Note that at one point we will overflow the 32-bit TIMER_MATCH register. When the match register gets up to 4294000000, the next time we add 1000000 to it, we will get 4295000000, which is larger than the maximum 32-bit integer (4294967295). That’s okay — it will wrap around and work correctly. The C standard guarantees that unsigned integers will wrap around correctly — e.g. if you add 1 to 0xFFFFFFFF, you will get 0. So in this case, when we add 1000000 to 4294000000, we will end up with a match value of 32704 (4295000000 % 4294967296), and all will be well with the world (the timer counter itself will also overflow correctly back up to 0, and match when it reaches 32704, exactly 1000000 higher than 4294000000 after wrapping).
These are the two simplest ways to use timers. Timers also have all kinds of other crazy features; I described the most common way they are used. I hope I didn’t make that too confusing. It was a lot to digest, but basically, here’s a summary:
- Timers are memory-mapped peripherals, and you know what their clock rate is based on what you read in the microcontroller’s data sheet and how you configure it.
- Timers have divisors, so you know exactly how long it takes between increments (or decrements) of the timer’s counter.
- You can use that information alone to do time-based stuff, but if you want maximum accuracy you should use an interrupt.
- To use an interrupt, you set a match value and the timer will interrupt whenever it reaches the match value.
- Some microcontrollers will allow you to automatically reset the counter whenever the match value is reached, which is handy for periodic tasks.
- If your microcontroller doesn’t automatically reset the counter after a match, you should update the match value instead of resetting the counter value on your own, or your interval between interrupts will be slightly longer than it should be.
Ok, that’s enough for today. I’ll talk more about interrupts and interrupt handlers next time. Have fun!