Just about every microcontroller comes with one or more (sometimes many more) built-in timer/counters, and these are extremely useful to the embedded programmer - perhaps second in usefulness only to GPIO. The term timer/counter itself reflects the fact that the underlying counter hardware can usually be configured to count either regular clock pulses (making it a timer) or irregular event pulses (making it a counter). This tutorial will use the term “timer” rather than “timer/counter” for the actual hardware block, in the name of simplicity, but will try and make it clear when the device is acting as an event counter rather than a normal timer. Also note that sometimes timers are called “hardware timers” to distinguish them from software timers which are bits of software that perform some timing function.
A typical timer will consist of a prescaler, an N-bit timer/counter register (typically N is 8, 16 or 32 bits), one or more N-bit capture registers, and one or more N-bit compare registers. There will also be control and status registers to configure and monitor the timer.
Note that this section is called Timers/Counters. The fundamental hardware involved is an up-counter, which counts incoming pulses. A counter, any counter, becomes a timer when the incoming pulses are at a fixed, known frequency. I will often use the terms “counter” and “timer” interchangeably, but just keep in mind that the hardware is always a counter, and that the counter becomes a timer if we are feeding it a fixed, known frequency to count.
It is also worth noting that the size in bits of a timer is not directly related to the size in bits of the CPU architecture. Nothing prevents an 8-bit microcontroller from having 16-bit timers (most do, in fact), nor does anything prevent a 32-bit uC from having 16-bit timers (and some do).
Something else that you may see are uCs that have multiple timers of different size. For example, most AVRs have both 8-bit and 16-bit timers, and some LPC2xxx devices have both 32-bit and 16-bit timers.
The prescaler takes the basic timer clock frequency (which may be the CPU clock frequency or may be some higher or lower frequency) and divides it by some value before feeding it to the timer, according to how the prescaler register(s) are configured. The prescaler values that may be configured might be limited to a few fixed values (powers of 2), or they may be any integer value from 1 to 2^P, where P is the number of prescaler bits.
The purpose of the prescaler is to allow the timer to be clocked at the rate you desire. For shorter (8 and 16-bit) timers, there will often be a tradeoff between resolution (high resolution requires a high clock rate) and range (high clock rates cause the timer to overflow more quickly). For example, you cannot (without some tricks) get 1us resolution and a 1sec maximum period using a 16-bit timer. If you want 1us resolution you are limited to about 65ms maximum period. If you want 1sec maximum period, you are limited to about 16us resolution. The prescaler allows you to juggle resolution and maximum period to fit your needs.
As an example of a fixed selection prescaler, many AVR devices allow for fixed prescale values of 1, 8, 64, 256 and 1024. If the system clock frequency is e.g. 8MHz, this results in a timer clock period of 0.125, 1, 8, 32 or 128 microseconds per clock tick. Likewise, the 9S12 prescaler allows fixed prescale values in powers of 2: 1, 2, 4, 8, 16, 32, 64 and 128.
As an example of a fully configurable prescaler, the NXP LPC2000 family has 32-bit timers that have 32-bit prescalers, which allow for any prescale value between 1 and 2^32. Such fully configurable prescalers are of course more flexible than fixed selection prescalers, but in practice the fixed selection prescalers are usually adequate, though they may require that more thought be given to choosing a system clock frequency and other timing values.
In the prescaler section I also include any configuration logic to clock the timer with an external signal, present on an input pin of the μC. If this external signal is a fixed frequency then the counter hardware continues to act as a timer, but if the external signal consists of pulses that are not fixed and regular then the counter is simply acting as a counter, as was discussed above. When a timer/counter is configured as a counter, it is most commonly used in conjunction with some timebase (another onboard timer or an external timebase signal) to count pulses per some time interval.
The timer register itself is most typically a N-bit up-counter, with the ability to read and write the current count value, and to stop or reset the counter. As discussed, the timer is driven by the prescaler output. Any regular pulses which drive the timer, whatever their source, are often called “ticks” and that is the term which will be used here. Understand that timers do not necessarily time in seconds or milliseconds or microseconds, they time in ticks. Depending on the hardware design and software configuration, the rate of these ticks may be configured to some human-friendly value such as e.g. 1 microsecond or 1 millisecond, or they may be some “oddball” value which makes more sense for a particular system design.
The ways in which the timer can be manipulated will be discussed in the following sections on capture and compare registers.
A capture register is a register which can be automatically loaded with the current counter value upon the occurance of some event, typically a transition on an input pin. The capture register is thus used to take a “snapshot” of the timer at the moment when the event occurs. A capture event can also be configured to generate an interrupt, and the ISR can save or otherwise use the just-captured timer snapshot. Since the capture happens in hardware, there is no latency error introduced into the snapshot value as there would be if the capture was done in software.
Capture registers can be used, among other things, to time intervals between pulses, to determine the high and low times of input signals, and to time the intervals between two different input signals.
Compare registers (sometimes also called match registers) hold a value against which the current timer value is automatically and continuously compared. A compare register is used to trigger an event when the value in the timer register matches the value in the compare register. If the timer/counter is configured as a timer, using compare registers will let you generate events (output pin changes and/or interrupts and/or timer resets) at known and precise times. If the T/C is configured as a counter, the compare registers can generate events based on preset counts being achieved.
One example of the use of a compare register is to generate a timer “tick”, a fixed timer interrupt used for system software timing. For example, if a 5ms tick is desired, and the timer is configured with a 1us clock, setting a compare register to 5000 will cause a compare event after 5ms. If that compare event is configured to both generate an interrupt and to reset the timer back to 0, the result will be an endless stream of 5ms interrupts.
Another use of a compare register would be to generate a variable width pulse. Set an output high (or low) when the timer is at 0 (many timers have this capability in hardware), set the compare register to the value of the pulse width, and on the compare event set the output low (or high). Use a second compare register with a larger value to set the pulse interval by resetting the timer on compare, or just let the timer roll over from -1 to 0 and let that be the pulse interval.
Using a similar technique, but toggling an output pin on every compare, a variable frequency square wave can easily be generated with a single compare register.
Some Different Ways to Use Timers
What follows are some examples of how different ways to use timers. The examples are just a beginning, something to convey a basic understanding of how timers work and can be used.
While timers are most commonly used in conjunction with timer-generated interrupts, it is also useful to understand how timers can be used in a polling situation. Polling means sitting in a software loop, reading some register or memory location, and waiting for some value in that location.
// poll for a value in a memory location while ( Location_Im_Polling != VALUE_IM_WAITING_FOR ) ; // wait for this location to contain specified value // or poll for specific bit(s) to go high while ( !(Location_Im_Polling & MY_BITMASK) ) ; // wait for bit(s) of MY_BITMASK to go high
Polling a timer results in an approach between simple cycle-counting on the one hand, and timer-generated interrupts on the other. The advantage of polling a timer is that the accuracy of the timer is for the most part retained. The disadvantage is that the time the μC spends polling the timer is still wasted time, instruction cycles that could otherwise be doing useful work.
Polling for a Timer Value
This is a technique you probably never want to use in a serious project, but it’s useful to help you start thinking about how timers work. This technique is very similar to your earlier software delay loops, with the difference being that now, instead of timing by monitoring a variable that is incremented (or decremented) in software, you will monitor the hardware timer register. Notice the similarity:
Software delay loop (COUNT is the number of software loops to wait)
- Set variable to 0
- Increment variable
- Check if variable is at COUNT
- If not, loop to #2
- If yes, finished
Timer register polling loop (COUNT is the number of timer ticks to wait)
- Set timer register to 0
- Increment timer register (this happens in the hardware, not the software!)
- Check if timer register is equal to or greater than COUNT
- If not, loop to #3
- If yes, finished
The difference is simply in which timer variable is monitored, and in how it is incremented. The advantage of using the hardware timer is that you can do other work e.g. service interrupts and you will not lose accuracy, unless you happen to be doing the other work at the exact time the timer register reaches your desired COUNT value.
Polling for a Timer Flag
This is another technique you probably never want to use in a serious project, but it will introduce you to one of the useful flags (hardware status bits) that are associated with timers:
Timer overflow flag polling loop (COUNT is the number of timer ticks to wait)
- Set timer register to 0-COUNT
- Increment timer register (this happens in the hardware, not the software!)
- Check if timer overflow flag is set
- If not, loop to #3
- If yes, finished
This method utilizes the timer overflow flag (which most if not all timers will have), which will be set when the timer register counts over from -1 to 0. -1 would be 0xFF for an 8-bit timer, 0xFFFF for a 16-bit timer, and 0xFFFFFFFF for a 32-bit timer. Since the flag signals when the timer goes to zero, we start the timer at 0-COUNT to get COUNT ticks.
The main advantage of this method over timer register polling is that we have an unambiguous indication of when the end of the interval has occurred. There are some pathological possibilities in the timer register polling, if the COUNT value is near N-1, where one could miss the COUNT value and have the timer roll over to 0 and then we’d end up waiting another 2^N ticks. By checking instead for the overflow flag, even if we missed the exact time it was set, the flag will never unset by itself, so we will always detect it as soon as our software polling loop comes around to the next check. Another advantage is that the code to compare for a given COUNT value may be larger and slower than the code to check for a single bit flag. Slower code means less precision as to when the time interval has ended.
Keep in mind that most, if not all, timer hardware will require the software to explicitly clear the overflow flag after it has been set and detected.
Timer overflow interrupt
Now we begin to get into the natural pairing that exists between timers and interrupts. This method is similar to the timer overflow polling loop except that now the timer overflow triggers an interrupt, and the ISR can handle the end of the timing interval directly, or it can signal the main code to handle the end of the interval.
Whereas with the polling method the software had to clear the overflow flag, the hardware on many microcontrollers will automatically clear the flag when the timer is configured to generate an interrupt.
Timer compare interrupt with automatic timer clear
This method is similar to the timer overflow interrupt method, but the interrupt is generated by match between the timer register and a compare register. The compare event which triggers the interrupt also automatically clears the timer register, so for the first time we have a configuration that can generate repetitive timing intervals with no slippage or loss of accuracy between intervals. This is a major advance in our timer operation. While it is often useful to be able to achieve single timing intervals, it is much more useful to be able to achieve repetitive, constant timing intervals. This method allows us to achieve such intervals.
Note that for most devices it is also possible to configure a timer to reset on compare without generating an interrupt. This has benefits when the timer is being used to generate waveforms, but it offers no particular advantage as a means of generating repetitive intervals using software polling.
Note also that it is also possible, as was discussed above, to configure a timer to interrupt on overflow, while letting the timer continue to run. This will generate an interval every 2^N timer ticks. This is fundamentally the same as the compare interrupt with automatic clear, but it is much more limited since the timer count cannot be altered from its maximum count of 2^N. Thus the only control the programmer has over the timing interval is through adjusting the prescaler, while with the automatic clear method both the prescaler and the compare value can be adjusted, giving much more control over the timer interval.
Timer compare interrupt with leapfrogging
This method also uses a compare match to generate an interrupt, but does not configure the timer to clear when the match occurs. Thus the interrupt is triggered but the timer continues to run. It will eventually overflow, going from 2^N-1 to 0, but that overflow will not stop the timer either. So the timer register counts indefinitely, going from 0 to 2^N-1 and repeating that cycle endlessly. To generate equal intervals using this method, the compare register must be modified on each interrupt. This is actually quite simple, as the modification just involves adding a constant (the timer period, in ticks) to the compare register (as always, ignoring any overflow, as discussed in the section on leapfrogging). In case you were wondering, the term "leapfrogging" is an informal one, referring to the way in which one or more compare register values leap ahead as the compare events occur.
Using timer overflows to extend timer resolution
The simplest way to use a timer is to use it for counts that are less than the natural timer count 2^N. However, it is possible to use a timer to count numbers greater than the natural timer period by keeping track of overflows and including the overflow count in the total count calculation.
Potential problem using overflows
There is an atomicity problem when using overflows in the manner described above. This problem occurs when an overflow occurs between accessing the timer register and accessing the overflow status, and it occurs no matter in what order the register and the status are accessed. The problem is that when a timer overflow occurs between the two accesses, the data read in those two accesses becomes inconsistent. One example of this is the following sequence:
- Read timer register
- Timer overflows
- Read overflow status
An example of the opposite order is this:
- Read overflow status
- Timer overflows
- Read timer register
In both cases, the combined data of the overflow state and the timer register yield a corrupted value, much the same as the corrupted values seen in the discussion on interrupts. The timer overflow is not an interrupt (although it may generate one), but like an interrupt it results in an asynchronous modification of the data being accessed, unknown to the program.
Luckily, we can detect such an event easily and then correct for it. We can detect the event because the timer register value after an overflow is less than the value before the overflow, which is not the case in the normal act of reading the timer register two times in succession. Similarly, the overflow status after an overflow is different than the status before an overflow. We can use these facts to check if the potentially corrupting situation has occurred, and if we detect this, we can immediately access the data again. This second access of the data, assuming it is done in a timely fashion, will always be valid because the timer cannot overflow again so soon after a previous overflow. Note that what we are detecting is not a corruption of data – we cannot detect that with certainty. All we can detect is that an overflow happened at some point in the sequence of accesses. The timing of the overflow may have resulted in corruption, or there may have been no corruption, but since we cannot know that the data accesses were uncorrupted, we perform the accesses again to obtain a guarantee of uncorrupted data. This is a technique worth remembering in any case where you are accessing multiple pieces of data that can be changing as they are being accessed.
- Read timer register
- Read overflow status (timer may have overflowed after reading timer register)
- Compare current timer register with value from #1
- If current value is less than first value, an overflow occurred sometime between 1st and 2nd timer register reads
- Read overflow status again and use in conjunction with 2nd timer register value
The same technique works if the order of data accesses is reversed
- Read overflow status
- Read timer register (timer may have overflowed after reading overflow status)
- Compare current overflow status with first overflow status value
- If current overflow status is not the same as first overflow status, an overflow occurred sometime between 1st and 2nd overflow status reads
- Read timer register again and use in conjunction with 2nd overflow status value
This same problem can occur with other hardware. For example, imagine a 16-bit timer that must be read as two separate 8-bit values. Between reading the first byte and reading the second byte of the 16-bit value, an overflow from the LSB to the MSB may have occurred. This will result in a corrupted 16-bit value. The solution is to read the slower-changing byte (MSB) first, then read the faster-changing byte (LSB), then read the slower-changing byte (MSB) again. If the two MSB values are different, an LSB overflow occurred at some point in the access. If that happened, read both the MSB and the LSB again for guaranteed valid data.
Want even more bad news? The same problem can occur in writing a 16-bit value via two 8-bit writes. Example
- Write low byte
- Timer advances, overflowing low byte
- Write high byte - the 16-bit timer value is now corrupted
The good news is that if your 8-bit CPU has a 16-bit timer, the designers have probably taken that hassle out of reading the timer register (and other changing 16-bit registers) by having a way to perform the entire 16-bit read in one operation (remember, the problem only occurs if you read the two halves of the register at different times). In the AVR, for example, reading the low byte of the 16-bit register also copies the high byte into a temporary register, and when you then read the high byte you are reading the value stored in this temporary register, so you are reading an entire 16-bit value that was fetched in one operation. Likewise, to write to a 16-bit register you first write the high byte which goes into a temporary register, then write the low byte. On writing the low byte, the high byte stored in the temporary register is written out in the same operation.
It is very common to configure a hardware timer to generate a system “tick” which controls all the system timing and delays. This tick is often in the range of 1-20ms (50-1000 ticks per second). From such a tick it is easy to generate in software almost unlimited timing intervals, at the resolution of the tick period. For example, if you need to display a message for 3 seconds and your system has a 10ms tick, your program would display the message for 300 ticks. During that time your system will be available for other work, since you’re only checking the tick count at every interrupt. This strategy of extending hardware timers with software is a very common one and can greatly increase the utility of the μC hardware timers.
The system tick function is so common that some μC devices have a dedicated system tick timer, thus freeing up other onboard timers for other uses. If a μC does not have a dedicated system tick timer, and a system tick is desired, then one of the regular hardware timers must be dedicated to this task.
If a μC has multiple timers, and if some of the timers have higher resolution than others (e.g. an 8-bit timer and a 16-bit timer, or a 16-bit and a 32-bit timer), it is usually a good idea to use a lower-resolution timer for the system tick if possible, thus leaving the higher resolution timer(s) available for other uses where the higher resolution may be necessary or desirable. Proper choice of prescaler and timer count, along with suitable choice of system clock frequency, should always allow a useable system tick interval to be achieved using the lower-resolution timer. See also the discussion below about how precise a system tick interval has to be.
More on Leapfrogging
As already mentioned, this unofficial term refers to a useful technique in deriving one or multiple timing signals or interrupts, which are not simply related, from a single timer. An example might be where one needs a 10ms interrupt, and also a 42.5ms interrupt. One solution would be to find the GCD of those two periods, which in this case is 2.5ms, configure a 2.5ms interrupt, and perform the 10ms task every 4 interrupts, and the 42.5ms task every 17 interrupts. Another solution would be to use two separate timers, one for each interval. A third solution would be to use a single timer along with multiple timer compare registers and the leapfrog technique.
Leapfrogging simply means adjusting the compare register, on each timer compare event, for the next interval. This is as opposed to the more common method of leaving the compare register value set, and having a compare event automatically reset the timer. In the leapfrog technique, the timer is never reset, but runs as a simple binary timer, rolling over from its maximum count (2^N-1 for an N-bit timer) to 0. On each compare interrupt, instead of causing the timer to reset, the compare register is advanced to the next interval. This advancing is a simple addition, e.g.:
OCR1A += 42500; // leapfrogs 42.5ms, assuming a 1us timer tick
Note that this calculation will at some point overflow or wrap around. For a 16-bit compare register this example will overflow the very first time the calculation is performed. This is, perhaps surprisingly, not a problem, since the timer count register overflows or wraps around in exactly the same way. Thus, taking our example through two intervals:
OCR1A = 42500; // initial compare from 0, 0xA604 … OCR1A += 42500; // 0x14C08 doesn’t fit into 16 bits, so we get 0x4C08 with an overflow
After the timer reaches 0xA604, the first compare value, it will count 0x59FC (23036d) more counts before overflowing, and then 0x4C08 (19464d) counts at which point the compare to OCR1A happens. This gives a total of 23036+19464=42500 counts, just as we desired. The overflow in the calculation had no ill effect on the time interval.
Note that it doesn’t matter when, after the compare event, that the compare register is updated, just as long as it is updated before the next compare is scheduled. That is becuase you are not trying to do math based on an advancing timer value, but on a fixed previous compare value. This means that interrupt latency does not affect the accuracy of the compare events.
As stated, one situation where the leapfrog technique is useful is in achieving multiple non-simply-related timing intervals, either for interrupts or for driving output pins. It is also useful in obtaining non-equal intervals. Suppose e.g. a certain process required sequential intervals of 1ms, 7ms, 18ms and 3.5ms. Again, this could be done by counting 0.5ms ticks, or it could be done by leapfrogging 1, 7, 18, 3.5 and then back to 1ms. Either method requires keeping track of the current interval and determining the next interval value, so there is no clear advantage of one or the other method in that regard.
As we have said, hardware timers are an extremely useful resource, but they are also a limited resource, both in terms of the number of hardware timers a μC may have, and of the resolution of those timers. If your application needs more timers than your hardware offers, and if the timing requirements for those extra timers are fairly “slow” (relative to the processor speed), then the extra timers can easily be created in software. Likewise, the range of hardware timers can be extended through software.
Do you really need a 10.00000 ms tick?
Maybe you do, but quite often you don’t. For example, suppose you have an AVR running at 10 MHz, and you want to use one of its 8-bit timers to generate a 10ms tick. This means you want to divide the system clock frequency of 10,000,000 down to 100, for a total division of 100,000 With an 8-bit timer this requires the use of a 1024 prescale, which means our timer is being driven by a frequency of 10 MHz / 1024 or 19531.25 Hz. (If you prefer to think in terms of periods rather than frequencies, you are multiplying your system clock period of 50ns by 1024 for a timer clock period of 51.2us. to get a frequency of 100 Hz from a frequency of 19,531.25 Hz (or, equivalently, to get a period of 10ms from a period of 51.2us), you need to divide by 195.3125. Obviously this is not a value you can enter into your 8-bit timer. So what if you enter 195 instead? You get a frequency of 100.1603 Hz (period of 9.984ms).
No, it’s not the 10ms you wanted, but the chances that you can’t use this tick value are very, very small. In fact, just about the only time such a small timing error would be a problem is when the timer ticks are used to generate time values for external systems, especially " round number" time values meant for human evaluation. For most system tick uses, the precise value of the system tick doesn't matter as much as using that actual value, whatever it may turn out to be, in calculating any other time values that are based on the tick.
If, due to hardware considerations, your system tick ends up not being a nice "round number" but you still want to remove long-term drift (say e.g. you want to maintain an elapsed time for logging events), you can "slip" your system tick software accumulator to remove any long-term drift.
This section would more accurately be called Integer Arithmetic Errors Affecting Timer Use, but that’s a bit much. The problem we want to address is that, as you calculate your timer prescale and count values, you can get errors in your values due to the truncation that occurs with integer arithmetic. Even though all your math is correct, you can end up with timers that don’t time exactly what you want.
Here is a concrete example. Many microcontroller designs, including our LPC2294 device, use an “oddball” crystal of 14.7456 MHz. Actually there is nothing oddball about that value, it is one of the many values that allows for achieving exact standard UART baud rates e.g. 1200,9600,19200,115200, etc. But at the same time it can result in timer prescale and count values that are not nice exact integers. In our example our LPC2294 is configured to quadruple the crystal frequency to a CPU clock of 58.9824 MHz, and then divide that by 2 to get a peripheral clock of 29.4912 MHz. This is the clock that is fed into the prescaler. Further, let us say that we want to get timer ticks of 1us (1MHz). Well, there is no integer prescale value that will convert 29.4912 MHz into 1 MHz. When we do our integer math we will get a prescale value of 29 (we would actually load 29-1 into the prescale register, due to the design of the timer). But this value does not give us 1us ticks, it gives us 0.98334418…μs ticks. If we wanted to count ticks to get e.g. a 200ms time interval, and if we counted 200,000 ticks, we would actually get a 196.66ms interval. That’s an error of more than 1.5%, all due to the unavoidable truncation error in our prescaler calculation.
There are at least two ways to fix (or at least greatly mimimize) this problem. One is to use an exact (usually larger) prescale value that avoids integer math errors. For example, if we had a prescale of 147,456, then we would get ticks of exactly 147,456/29,491,200 or every 5 ms. If that tick rate is acceptable then our problem is solved, since 40 ticks give μs exactly our 200 ms time interval. But if we really wanted to keep our (approximately) 1 μs tick, then we need to recalculate how many such ticks make up 200 ms. With our not-quite-right prescale of 29, we have 29,491,200/29 or about 1,016,938 ticks per second, rather than our desired 1,000,000 ticks per second. Therefore, to time 200 ms we need to count not 200,000 ticks but 1,016,938/5 or 203,387 ticks (notice another integer truncation, the precise but unrepresentable tick count is 203,387.6). This timer value, 203,387, while still not exactly correct, now has an error of less than 1 part in 200,000, rather than our original error of about 1 part in 60. By taking into account our original prescale truncation error and calculating our timer value using the truncated prescale value, we have reduced our error by a factor of more than 3000. Such a reduction could of course easily make the difference between acceptable timer accuracy and unacceptable timer accuracy. The key to this improvement is to do the timer math based on the actual (truncated) prescale value, rather than on the theoretical prescale value before the truncation.
If this sounds complicated, it's not really. It's just a matter of forcing the same truncation in the calculation as happens in the hardware. For example:
// calculations for a 200ms (200000us) interval // timer counts per prescale = F_TIMER / 1000000 (truncates) // F_PRE is actual clock going INTO prescaler // F_WANT_TMR is desired clock coming OUT OF prescaler, INTO timer (desired tick rate) // F_REAL_TMR is actual clock coming out of prescaler, into timer (actual tick rate) #define F_PRE 29491200UL // from our 14.7456 MHz crystal #define F_WANT_TMR 1000000UL // we want 1us ticks #define PRESCALE (F_PRE/F_WANT_TMR) // for ~1us ticks (may truncate) #define F_REAL_TMR (F_PRE/PRESCALE) // our actual tick rate with truncated prescale #define TWO_HUN_MSa 200000UL // our intuitive but incorrect value #define TWO_HUN_MSb ((200000UL*F_REAL_TMR)/F_WANT_TMR) // gives correct value of 203387
Here, dividing by the truncated prescaler value (F_PRE / F_WANT_TMR) causes a division by the same value that is loaded in the prescaler, which is what we need in order to get the correct answer, the answer that takes into account the truncation of the prescaler value.
When timer/counter hardware is used to count external event pulses it is behaving as an event counter. Some typical examples would be counting the number of widgets that come down the assembly line in the Acme Widget plant, or the number of pulses coming off a flywheel engine speed sensor. In the latter case the goal would be to count the number of pulses in a given time period, in order to calculate an engine speed. As in that example, it is very common that an event counter is used to count not just the number of events, but the number of events in a given time interval. Thus you will usually see event counters and timers (hardware or software) used in combination.
One illustrative use of a timer in counter mode is to count the bounces of a switch. The fact that a mechanical switch or button does bounce was discussed in an earlier chapter. Connecting a counter to such a switch allows one to see that bouncing in very concrete terms.
Later when we have gotten a display up and running we will revisit some of these event counter examples.