In short: An interrupt lets hardware yank the CPU away from its current work to run a short, prioritized routine (the ISR) that services an event with predictable latency — so the golden rule is to keep handlers tiny, defer the heavy lifting, and understand exactly how long the detour takes.
Polling a sensor in a tight loop wastes cycles and still misses fast events. Interrupts solve both problems: the peripheral raises a request, the processor finishes the current instruction, saves context, and jumps to a dedicated Interrupt Service Routine (ISR). When the ISR returns, the interrupted program resumes as if nothing happened. This article walks through the mechanism, the numbers that matter, and the discipline that separates a robust firmware from one that mysteriously drops bytes under load.
The anatomy of an interrupt
On a Cortex-M microcontroller the flow is handled by the NVIC (Nested Vectored Interrupt Controller). Each interrupt source has a slot in the vector table — a simple array of function pointers at the start of flash. When a UART receives a byte, the NVIC looks up the ISR address, pushes eight registers onto the stack automatically (R0-R3, R12, LR, PC, xPSR), and begins executing the handler. This hardware-assisted stacking is why Cortex-M ISRs can be written as ordinary C functions with no special prologue.
Interrupt latency: the number that matters
Latency is the delay from the event asserting its request to the first useful instruction of the ISR executing. It is the sum of several contributions, and each one is attackable:
- Detection and synchronization — the peripheral flag is sampled, often costing a clock or two.
- Instruction completion — the core finishes the current instruction; a multi-cycle
LDM/STMcan stall entry. - Context stacking — fixed ~12 cycles on Cortex-M for the automatic register push.
- Higher-priority or disabled windows — time spent with interrupts masked (critical sections) adds directly to worst-case latency.
Concrete figure: A Cortex-M4 at 80 MHz has a deterministic 12-cycle interrupt entry — about 150 ns — plus tail-chaining that skips redundant unstack/restack between back-to-back interrupts, saving roughly 6 cycles each. For a deeper breakdown of each term, see our companion piece interrupt latency explained.
Priorities and preemption
Not all events are equal. A motor over-current fault must be serviced before a periodic UI refresh. Priority levels let the controller decide. On Cortex-M, priority is split into preemption (group) bits and sub-priority bits. A higher preemption priority will interrupt a running lower-priority ISR; equal preemption priorities never preempt each other and use sub-priority only to break simultaneous-arrival ties.
| Priority value | Meaning | Typical use |
|---|---|---|
| 0 (highest) | Preempts everything | Safety fault, watchdog |
| 1–3 | Hard real-time | ADC sample, motor commutation |
| 4–7 | Soft real-time | UART/SPI, timer ticks |
| 8+ (lowest) | Background | Logging, housekeeping |
A classic mistake: assigning a chatty UART the same or higher priority than a control loop. The control loop then jitters every time a character arrives. Lay out your priority map on paper first and verify worst-case blocking.
Nesting: interrupts inside interrupts
Nesting means a higher-priority interrupt preempts a running ISR, the way a function call interrupts a function. It bounds worst-case latency for the most urgent events but costs stack depth — every nesting level stacks another frame. On Cortex-M nesting is automatic and cannot deadlock the core, but you must size the stack for the deepest plausible nest. The animation below shows priority 0 cutting in front of a priority-5 handler already in progress.
Keep handlers short — and why
The single most repeated best practice in embedded firmware is keep the ISR short. The reason is latency propagation: while you sit in one handler, every equal-or-lower-priority event is blocked. A 200 µs ISR on a system that must answer another event within 100 µs has already failed, no matter how fast the silicon. The pattern that scales is top-half / bottom-half:
- Top half (ISR): acknowledge the hardware, clear the flag, grab the one byte or sample, push it to a ring buffer, and set a flag or post to a queue. Done in microseconds.
- Bottom half (task/main loop): parse the packet, run the filter, update the display — at normal priority where it can be preempted safely.
Practical rules: never call blocking APIs (printf, malloc, busy-wait delays) inside an ISR; mark shared variables volatile and access multi-byte state inside a short critical section; and always clear the peripheral interrupt flag, or the ISR re-fires forever. Floating-point in an ISR forces extra FPU-register stacking — prefer fixed-point or defer it.
Verifying timing in the real world
Reasoning about latency is necessary but not sufficient — measure it. Toggle a GPIO at ISR entry and exit and watch it on a logic analyzer or scope; the pulse width is your real service time and the gap to the trigger edge is real latency. For static worst-case analysis, cycle-accurate instruction-set simulators and commercial tooling from vendors such as Synopsys, Cadence and Siemens EDA model core pipelines and interrupt entry precisely, which matters when a design is heading toward silicon rather than a dev board. Teams building custom SoCs often validate interrupt controllers in simulation long before the first chip — the kind of work handled by a dedicated semiconductor design-services team.
Want the full toolchain and hands-on labs on timers, DMA, and RTOS integration? Explore our structured embedded courses, and if you are new to the timing math, start with embedded systems fundamentals before the advanced modules.
Ready to build real firmware? CourseTron's Embedded Systems track takes you from bare-metal GPIO to RTOS-driven interrupt architectures with graded lab projects — enroll today and practice the top-half/bottom-half pattern on real hardware.
Frequently asked questions
What is the difference between an interrupt and polling?
Polling repeatedly checks a status flag in software, wasting CPU cycles and reacting only as fast as the loop period. An interrupt is hardware-driven: the peripheral signals the CPU the instant an event occurs, giving far lower and more predictable latency with no wasted cycles.
Why must an Interrupt Service Routine be kept short?
Because time spent in an ISR blocks all equal- or lower-priority interrupts and starves the main loop. A long handler inflates worst-case latency for every other event, causing missed deadlines, dropped data, and jitter. Defer heavy work to a task or the background loop.
What is interrupt nesting?
Nesting is when a higher-priority interrupt preempts an ISR that is already executing, much like one function calling another. It bounds latency for the most urgent events but consumes additional stack per nesting level, so the stack must be sized for the deepest expected nest.
What is a typical interrupt latency figure?
On a Cortex-M core the hardware entry sequence is a fixed ~12 cycles — roughly 150 ns at 80 MHz — plus any time the core spends finishing the current instruction or running with interrupts masked in a critical section.