EC-9.3 Interrupts, Timers and Keeping Time
en
What this is and why it exists
A small machine spends most of its life waiting. How it waits is what separates a design that responds in time from one that quietly misses things under load.
This topic is also where the first genuinely hard bugs live, for a reason worth stating early. The code that fails is not the code that was running when it failed. An interrupt arrives between two instructions of something unrelated, and the damage shows up somewhere else entirely.
Timers are the other half. They are how a machine with no sense of time keeps appointments.
The vocabulary
- Polling — checking a condition repeatedly in a loop.
- Interrupt — a hardware signal that diverts the processor to a handler.
- Vector table — the list of handler addresses, one per interrupt source.
- Handler — the function that runs in response to an interrupt.
- Latency — the delay between the event and the first instruction of the handler.
- Nesting — allowing a higher-priority interrupt to interrupt a handler already running.
- Prescaler — a divider placed between the clock and a timer's counter.
- Output compare — changing a pin automatically when the counter reaches a set value.
- Input capture — recording the counter value automatically when a pin changes.
The mental model
Start with polling, because it is not wrong. Checking a flag in a loop is simple, predictable and straightforward to reason about. For a machine with one job it is often the right answer.
It breaks at a specific point. The moment the longest path through your loop takes longer than the time you have to respond, polling has failed. Note what that is not: it is not about speed. A very fast processor with one slow function in the loop still misses the deadline.
An interrupt removes the problem by removing the wait. Hardware raises a request. The processor finishes the current instruction, saves enough state to come back, looks up an address in the vector table and starts running there.
The vector table is worth attention because getting it wrong fails silently. If a handler is not placed at the entry the hardware uses, the part runs whatever the default entry contains. That is usually an endless loop or a reset. There is no error message.
The rule about what a handler may do is short and worth taking literally. A handler runs while everything else is stopped, so it should set a flag, move a byte and return.
That means no long calculations, no waiting for anything at all, and no calls into code that was not written to be entered twice. The standard shape is a handler that puts one item into a buffer and sets a flag. The main loop does the real work when it sees the flag.
Priority and latency go together. Latency is the delay from the event to the first instruction of your handler. It includes finishing the current instruction, saving state, and any time spent with interrupts held off.
It also includes waiting for a handler that is already running. If a slow handler runs with interrupts held off, an urgent source waits behind it. The urgent deadline is then missed for reasons outside the urgent code entirely. This is the strongest practical argument for keeping every handler short.
Allowing nesting lets an urgent source interrupt a less urgent handler. It lowers latency for that source and complicates everything else, including stack usage, because now several handlers can be part-finished at once.
Now timers. A timer is a counter that counts edges of a clock and signals when it reaches a limit. The prescaler divides the clock first, and the two together set the period you can reach.
Choosing them is a small calculation that comes up constantly. Take the clock frequency, divide by the prescaler, and you have the counting rate. Divide the counting rate by the period you want and you have the limit to load. If the limit does not fit in the counter, choose a larger prescaler and try again.
The two capture and compare modes are what make timers powerful, because both work without the processor watching. Output compare changes a pin when the count reaches a value. Input capture records the count when a pin changes.
Output compare is how a pulse of an exact width is produced. Repeating it gives pulse width modulation, a square wave whose proportion of high time is set by a register. That is how a motor speed, a lamp brightness or a crude analog output is controlled.
Input capture is the mirror image, and it is how an interval is measured accurately. The hardware stores the count at the instant the pin changed, so the measurement does not depend on how quickly software responded. Measuring a pulse width by reading a timer inside a handler adds the handler's latency to the answer. That is precisely the error input capture removes.
What you should now be able to explain or do
- Say when polling is adequate and identify the point at which it stops being so.
- Describe the sequence from a hardware request through the vector table to a handler.
- Write a handler that does only what a handler should, and put the real work elsewhere.
- Explain how a long handler causes an unrelated urgent deadline to be missed.
- Choose a prescaler and a limit to give a required timer period.
- Choose between output compare and input capture for a given task, and say why input capture is more accurate than reading a timer in software.
Check yourself
At what point does polling stop being good enough?
When the longest path through the loop takes longer than the time available to respond. It is a question of the worst case, not of processor speed.
What may a handler safely do?
Set a flag, move a small amount of data, and return. It must not wait for anything or call code that is not safe to enter twice, because everything else is stopped while it runs.
How can a slow handler break a completely unrelated deadline?
An urgent interrupt cannot start while another handler is running with interrupts held off. The urgent handler waits, and its deadline passes for reasons outside its own code.
You need a timer to overflow every ten milliseconds from a sixteen megahertz clock. How do you approach it?
Divide the clock by a prescaler to get a counting rate, then divide that rate by one hundred per second. If the resulting limit exceeds the counter width, increase the prescaler and repeat.
Why is input capture more accurate than reading the timer inside an interrupt handler?
Because the hardware stores the count at the exact instant the pin changed. Reading it in software adds the interrupt latency to every measurement.
Go deeper
We haven't checked most of these for screen reader use yet.
Back to Interrupts, Timers and Keeping Time: work through the checklist