Microcontrollers & architecture 4 min read

Interrupts and the kinds you meet

An interrupt lets a pin, a peripheral, an instruction or an error pause the CPU, run a short handler and resume exactly where it left off, so nothing has to be polled. Interrupts differ by source and by behaviour: maskable or not, edge or level, own handler or shared.

Interrupt sources, shown as one parent with four children. External pin interrupts come from a button or a sensor. Peripheral interrupts come from blocks such as a UART, a timer or an ADC. Software interrupts are raised by an instruction such as SVC, ECALL or INT. Faults are raised by the CPU itself after an error such as a bad memory address.
Every chip names them differently; the sources stay the same.

What happens when one fires

Without interrupts, firmware polls: it checks a status flag in a loop and burns CPU time while nothing happens. With interrupts, the source raises a request and an interrupt controller passes it to the CPU.

The sequence is much the same on every chip. The CPU finishes its current instruction, saves its context (at least the program counter and status), finds the handler's address, usually in a vector table, and runs the interrupt service routine (ISR). Afterwards it restores the context and carries on.

On an Arm Cortex-M, for example, the core itself stacks the registers a C function may change, so an ISR is a plain C function; other cores leave more of that to the compiler. The delay from request to handler ranges from a few cycles to a few dozen, depending on the core: its manual gives the figure.

Kinds by source

Arm calls all of these exceptions. On a Cortex-M, pin and peripheral interrupts reach the core as IRQs through the NVIC, while SVCall, PendSV and HardFault are the core's own system exceptions.

Kinds by behaviour

Whatever the kind, keep handlers short: clear the flag, do the minimum and leave the real work to the main loop. A button on EXTI line 0 of an STM32F4, as an example:

volatile bool button_pressed;      // shared with the main loop

void EXTI0_IRQHandler(void) {      // name from the startup file
  EXTI->PR = EXTI_PR_PR0;          // clear the flag: write 1
  button_pressed = true;           // do the minimum
}

// in the main loop
if (button_pressed) { button_pressed = false; handle_button(); }