Embedded

Interrupts

A mechanism for the hardware to force the CPU to stop what it's doing and run a specific handler in response to an event — a timer tick, an incoming UART byte, a GPIO edge — without the main loop having to poll for it.

Quick reference
What it is
Hardware forcing the CPU to pause the main code and jump to a handler in response to an event, instead of software polling for it.
Mental model
A tap on the shoulder that interrupts whatever you're doing — the CPU finishes its current instruction, handles the tap, then resumes exactly where it left off.
Key numbers
Latency = time to first ISR instruction; jitter = variation in that latency, usually the number that matters more for control loops.
Reach for it when
An event happens unpredictably or rarely and polling for it would waste CPU cycles — a button press, a byte arriving, a timer tick.

The basic flow

  1. A peripheral (or the core itself) asserts an interrupt request.
  2. The interrupt controller (e.g. NVIC on Cortex-M) checks it's enabled and its priority beats what's currently running.
  3. The CPU finishes its current instruction, saves context (registers, return address), and jumps to the handler address in the vector table.
  4. The ISR (Interrupt Service Routine) runs.
  5. On return, the CPU restores context and resumes exactly where it left off.

Priority and nesting

Most microcontroller interrupt controllers support priority levels: a higher-priority interrupt can preempt a lower-priority one already running (nesting), while equal-or-lower priority interrupts wait. Getting priority assignment wrong is a common source of missed real-time deadlines — e.g. a low-priority ISR that runs too long can delay a time-critical one if priorities aren't set correctly.

Latency and jitter

Latency
Time from the triggering event to the first instruction of the ISR — includes any currently-running higher/equal priority code, plus fixed hardware entry overhead (register stacking, pipeline flush).
Jitter
Variation in that latency between occurrences — often the more important number for control loops, since a fixed delay can be compensated for but variance can't.

Shared state: the classic bug class

An ISR and the main loop (or two ISRs of different priority) can run "at the same time" from the main loop's perspective, so any data they both touch is a race condition waiting to happen.

// BUG: main loop reads a 32-bit counter the ISR increments.
// On an 8-bit core, the read isn't atomic — the ISR can fire
// between reading the low and high half, producing a torn value.
volatile uint32_t tick_count;

void TIMER_IRQHandler(void) {
    tick_count++;
}

uint32_t safe_read_tick_count(void) {
    uint32_t v;
    __disable_irq();     // or a narrower critical section
    v = tick_count;
    __enable_irq();
    return v;
}
Field notes

Related buses

Peripherals like SPI and I2C commonly use an interrupt (transfer-complete, or a dedicated data-ready pin serviced by a GPIO edge interrupt) instead of polling a status register in a busy loop.

Real-world applications