Yesterday's classic-ARM interrupts needed hand-written assembly wrappers to save state. Cortex-M throws all of that away. Its built-in NVIC does the vectoring, the register saving, and the prioritising in hardware — so an interrupt handler becomes nothing more than a normal C function. This is the single biggest reason Cortex-M is so beginner-friendly.
The NVIC — Nested Vectored Interrupt Controller — is a standard block inside every Cortex-M core. "Nested" = interrupts can interrupt each other by priority. "Vectored" = the hardware fetches the right handler address directly. It manages dozens to hundreds of interrupt sources plus core system exceptions.
Unlike classic ARM (where each vector was a branch instruction), a Cortex-M vector table is a plain array of function-pointer addresses. Slot 0 is even special: it holds the initial stack pointer, loaded automatically at reset before any code runs.
When an interrupt fires, the core automatically pushes 8 registers onto the stack: the AAPCS caller-saved set r0–r3, r12, LR, PC, and xPSR. On return it pops them back. Because these are exactly the registers a C function is allowed to clobber, the handler doesn't need any special wrapper:
Classic ARM made you save and restore registers in assembly. The NVIC is like a valet: it parks all the volatile state for you on entry and brings it back on exit. You just write the handler logic — in C.
Every interrupt gets a priority number, and the golden rule is counter-intuitive: lower number = higher priority. A higher-priority interrupt can preempt a running lower-priority handler, nesting on top of it; equal-or-lower ones wait.
NVIC_SetPriority(TIM2_IRQn, 2); then NVIC_EnableIRQ(TIM2_IRQn);The NVIC has two clever latency optimisations:
| Feature | What it does |
|---|---|
| Tail-chaining | If another interrupt is pending when a handler ends, the core skips the pop+push of the stack frame and jumps straight into the next handler. |
| Late arrival | If a higher-priority interrupt arrives during the stacking of a lower one, the core re-targets to the higher handler without redoing the stacking. |
Both eliminate redundant memory traffic, giving Cortex-M its famously deterministic, low interrupt latency — typically a fixed 12 cycles to enter a handler.
The NVIC moves interrupt plumbing into hardware: it vectors directly to your handler, auto-stacks the caller-saved registers (so an ISR is plain C), supports numbered priorities with preemption (lower = higher), and uses tail-chaining for back-to-back interrupts. That's why Cortex-M interrupt handling is both fast and easy.
The Nested Vectored Interrupt Controller built into Cortex-M — it handles vectoring, auto-stacking and prioritised, nestable interrupts in hardware.
The hardware auto-saves the caller-saved registers (r0–r3, r12, LR, PC, xPSR), so no assembly wrapper is needed.
Skipping the unstack/restack between back-to-back interrupts, lowering latency.