Design Article
Back to the basics: Doing Hardware Counter/Timer design using High School Science
Zane D. Purvis
8/25/2009 11:06 AM EDT
As a review, let's say you need to convert two (2) miles to meters. You recall that a "5K" race is approximately 3.1 miles, so you can use that as a conversion factor. You also know that there are 1000 meters in a kilometer.
Even though you do not know the conversion factor to go directly from miles to meters, you are still able to do the conversion because you can eliminate or "cancel out" units on intermediate conversions from miles to kilometers to meters:
* The "mile" in the denominator of 5 km/3.1 miles cancels out the
"miles" in 2 miles.
* The "km" in the denominator of 1000 meters/km cancels out the "km" in
5 km/3.1 mile.
* And, finally, you are left with the only units remaining being
meters, which are the units you wanted.
This article does not talk about a specific counter/timer peripheral, but the concepts covered will be relevant to any you will encounter. Some counter/timer peripherals may not have all the features mentioned in this article, or may have even more features.
Regardless of the features provided by your hardware, if you keep track of how various settings affect the units, you will be able to understand any counter/timer peripheral.
These same principles can be applied to other hardware peripherals as well, especially the "baud rate generator" of a UART or clock rate of a serial peripheral.
Getting down to timer basics
First of all, you may be wondering, "Why is that slash in the middle of
'counter/timer'?" A counter/timer peripheral is a hardware device that
counts events such as toggles of an input pin. That explains the
"counter" part. What about "timer"? A "timer" is just a counter that
uses a periodic clock signal as its input: it just counts time.
The rest of this article will refer to a counter/timer peripheral as just a "timer," but bear in mind that many people will use the term "counter" and others will use the term "timer" to refer to the same type of peripheral. Some developers and chip manufacturers will even mention "timer in event counter mode" to indicate the peripheral is counting an event other than a clock signal.
A timer has an input source, which is the pin monitored by the hardware before updating its internal counter. Whenever that input source signal goes from low-to-high, or vice versa, the timer will update its internal count value. Some timers count up from zero, others count down towards zero, and some have configurable directions.
Regardless of which direction your device counts, the principle of operation is the same: each time that input pin changes in the specified way, the device's internal count either increments or decrements.
Let's say you've configured your timer's input source to be your MCU's main clock which is running at 8 MHz. That means that the internal counter value will change, or tick, with a frequency of 8 MHz = 8 x 106 cycles/sec or 125 ns/cycle. That is, one (1) tick every 125 ns.
Prescaling
When looking at the datasheet's description for your timer, you will
likely notice a register for setting the divider or prescaler. That
setting allows you to slow down the ticking. If you choose a prescaler
value of 2, then for every two changes of the input source the timer
will tick only once.
If you choose a prescaler of 16, the input source would change 16 times between each tick. Think "16 MCU clock cycles per timer tick." Given the same 8 MHz MCU clock source as before, and a prescaler of 16, to calculate the number of ticks in a second, it is simply a multiplication:
Notice that the units work out to give us ticks/sec—the MCU clock cycles units "cancel out," and we are left with ticks/sec. To calculate the amount of time that has elapsed between each tick of the hardware timer, just calculate the multiplicative inverse/reciprocal so that the units become sec/tick:
Time-outs
If your timer peripheral had an internal counter with infinite range
and your peripheral gave you read access to the counter, you could
measure time between two software events by reading the counter value
at the first event, then read the value again at the second event; the
difference is the elapsed time.
However, on real devices, the internal counter has a finite range and will eventually overflow if a tick increments the value or underflow if a tick decrements the value.
In this article, I will call an overflow or underflow condition a time-out. On a time-out, the peripheral will set a flag and may trigger an interrupt. If you had an 8 bit timer, it would time-out after 28 = 256 ticks. How long before the 8 bit timer times-out with 2 microseconds between ticks?

Most timer peripherals can operate in a repeat mode where, after a time-out, they reinitialize their internal counter and start counting again. By checking the flag that is set by the time-out or enabling the interrupt, you can keep track of time.
Reading the time-out flag or the execution of the corresponding interrupt service routine (ISR) often clears the time-out flag automatically; some hardware may require an explicit "clear" of the time-out flag.
In fact, that is how systems generally keep track of time: a hardware timer times out periodically, say every millisecond, and a current_time counter variable in the program or operating system is updated. (Note that additional care is needed to ensure that the counter variable does not unexpectedly overflow.)
Configurable Time-outsIn the example we've been working with, the time-out interrupt will be triggered every 512 microseconds since the hardware times-out after 256 ticks with an 8 MHz source.
If your software needed to toggle an output pin approximately every 4 msec, you could count the number of time-outs in a variable called num_timeouts, and when num_timeouts reached the number corresponding to 4 msec, toggle the pin and reset num_timeouts. Determining how many time-outs that amounts to is simple if you keep track of units: it's just another unit conversion:
Notice the rounding up to 8 time-outs. Let's see how much time that actually is, once again by converting units:
Maybe, for your application, toggling the pin with a period of 4.096 msec, rather than 4.0 msec, is OK (2.4% error). But, if you'd like to have it closer to 4 msec, you would be better off setting your timer to time-out closer to 500 micrsoseconds rather than every 512 microseconds.
Many timer peripherals allow you to change the final/initial value of its internal counter; instead of timing out every 256 ticks, you could set an 8 bit timer to time-out after 200 ticks or 150 ticks.
Now, let's figure out how many timer ticks are required to get as
close to 500 microsecond time-outs as we can with our 8 MHz clock
source that is being prescaled by 16 (2 microseconds/tick). We want a
500 microsecond/time-out, so let's start with that and then multiply by
the number of ticks per second so that units work out to ticks/time-out:
So, if you configure your timer peripheral to time-out every 250
ticks rather than at 256 ticks, the time-out flag/interrupt will be
triggered every 500 microseconds instead of 512 microsecoonds:
Now, let's figure out how many time-outs there are in 4 msec, as
before:
There are exactly eight 8 time-outs in 4 msec. Your software could determine if 4 msec had occurred by counting the number of time-outs, and when that count reached eight (8), 4 msec had elapsed. Below, you can see a C-like ISR code that toggles the output pin every 4 msec:
If not using interrupts, this C-like code below will toggle the output pin every 4 msec:
If you need greater range out of your timer, you have several options:
* change the prescaler
* change the overflow value/start count value
* extend the timer value in software
* change the clock source
Let's revisit the example we've been using, and decide that we need a time-out every millisecond rather than every half millisecond as before. It's an 8 bit timer we're using, so the highest time-out value we can set is a time-out after 256 ticks.
With the 8 MHz MCU clock being prescaled by 16, we have already seen that it takes 512 microsecons for the hardware register to overflow (i.e., tick 256 times).
If our timer can be prescaled by 32, what's the longest time between time-outs we can reach? We'll start with 256 ticks and by keeping the units correct, we'll figure out how long that takes with the 8 MHz MCU clock being prescaled by 32:
That's pretty close to our target of one (1) msec. By doubling the prescaler, we doubled the amount of time between time-outs. By following the method in the "Configurable time-outs" section, you could get the time-out even closer to 1 msec:
Using an 8 MHz clock source prescaled by 32, one (1) millisecond is 250 ticks. If the time-out value for your timer is set to a low value, you can increase the frequency between time-outs by increasing that value.
If you needed an action to occur every 10 msec, you can configure your timer to time-out every millisecond and trigger an interrupt. In the ISR, count the number of time-outs and when the count reaches 10, execute the 10 msec action.
If you are not using interrupts, a loop in your program could poll the time-out flag, and when it has been set ten times, you know that 10 msec has elapsed. Many embedded systems include a "real time clock" crystal which oscillates at a stable 32.768 kHz.
This is the same type crystal found in watches and clocks, and is equally divisible by powers of two, which is particularly useful for timers that do not allow arbitrary time-out values. Using a slower clock source than the typical multi MHz used for MCUs will allow the time between timer ticks to be greatly increased.
Some cautions and gotchas
A common "gotcha" with beginners is setting the time-out value to
something invalid. Be sure you check your datasheet to determine how
many bits are in the time-out value register and that the value you
have calculated does not exceed the allowed range.
If it does exceed the range, you will need to change the clock source, the prescaler, or extend the timer range using a software counter. For example, if you were to set an 8-bit timer to time-out after 275 ticks, it is quite likely your compiler would not complain.
But your ISR would be executed too frequently because 275 is larger than 28 (256) and the value 19 (275 & 0xFF) would be loaded into the 8 bit time-out value register, instead.
Another common problem is for people to write an ISR that takes a long time to run. Some beginners hear the rule "keep your ISRs short" and mistakenly think that rule means it is OK to call functions from the ISR as long as the body of the ISR itself is short lines-of-code-wise. Instead, you need to keep your interrupt service code short temporally.
If your timer time-out ISR takes more than a millisecond to execute, and your timer time-outs every millisecond, you will lose interrupts, and your device will not function properly. This is true for all ISRs.
The easiest solution is: in your ISR, only perform the most time-critical operations and postpone the rest of the work to be done outside of ISRs using flags or a work queue. The same is true if you are not using interrupts, but are polling instead: you must ensure that the code between time-out flag polls takes less time to execute than the interval between time-outs.
Conclusion
A timer is a common hardware peripheral in microcontroller development.
Understanding how all the settings affect the time-out interval of the
peripheral is daunting to many beginners who often just copy sample
code without understanding what it means.
By understanding that many of those settings just affect how frequently the timer "counts" and keeping track of those effects, a timer is relatively easy to figure out. To calculate how much time elapses between timer events, just keep track of the units and do a "unit conversion" as demonstrated in the article.
Some hardware will have different settings and may have more or fewer features, but if you keep track of the units, you will have no trouble figuring out how much time elapses between events and how to configure your peripheral.
Zane D. Purvis has a Master's of Computer Engineering from N.C. State University's Center for Efficient, Scalable and Reliable Computing (CESR). He has taught as an adjunct lecturer for the NCSU Biomedical Engineering and NCSU Electrical and Computer Engineering departments and currently resides in Raleigh, NC. You can contact him at code.zane@gmail.com.



