How does interrupts works exactly in microcontrollers?

 Interrupts are how a microcontroller stops what it’s doing (briefly) to handle an important event right now, then returns to exactly where it left off.

Here’s how it works “under the hood”, step by step.



The core idea

  • Your main code runs in a loop (or an RTOS task).

  • Hardware events happen asynchronously: a timer hits zero, a UART byte arrives, a GPIO edge occurs, ADC completes, etc.

  • Instead of polling (“are we there yet?”), the MCU uses an interrupt: a hardware signal that asks the CPU to run a specific function called an ISR (Interrupt Service Routine).


What happens when an interrupt occurs (exact sequence)

1) An event sets an interrupt flag

Example: a timer overflows → the timer peripheral sets a status bit like TIMERx_IF = 1.

2) The interrupt controller decides if it should fire

An interrupt triggers the CPU only if:

  • The peripheral’s interrupt is enabled (local enable bit),

  • The interrupt is unmasked/enabled in the interrupt controller (e.g., NVIC on ARM Cortex-M),

  • Global interrupts are enabled (CPU interrupt-enable state),

  • And its priority is high enough to preempt the current code.

If all conditions pass, the interrupt becomes pending and then active.

3) The CPU pauses the current code and saves context

The CPU must preserve enough state to resume later.

  • ARM Cortex-M (STM32, etc.): the CPU automatically pushes a stack frame (typically R0–R3, R12, LR, PC, xPSR) onto the current stack.

  • Other architectures save different registers, but the concept is the same.

This is the “context save”.

4) The CPU jumps to the ISR via the interrupt vector table

The vector table is a table of addresses (pointers). Each interrupt number maps to one ISR entry.

So the CPU loads the ISR address and branches there.

5) ISR runs and must “service” the interrupt

Inside the ISR you usually:

  • Read data (e.g., UART RX register),

  • Update a counter/state,

  • Start/stop something,

  • Clear the interrupt flag (very important),

  • Optionally signal the main loop (set a flag, push to a queue, give a semaphore).

Key rule: if you don’t clear the flag (or the condition), you may instantly re-enter the ISR again and again.

6) Return from interrupt restores context and resumes

The CPU executes a special return-from-interrupt instruction:

  • Restores the saved registers from the stack,

  • Re-enables preemption rules,

  • Continues exactly where the main code was interrupted.


Priorities and nesting (why one interrupt can interrupt another)

Interrupt controllers support priorities:

  • Higher priority ISRs can preempt lower priority ISRs.

  • Same priority usually means no preemption (depends on MCU).

  • This lets you keep time-critical things (motor control PWM timing) responsive even if something slow (UART printing) is happening.

On Cortex-M, there’s also a concept of a priority threshold/masking (e.g., BASEPRI) that can block interrupts below a certain priority.


Latency: what “how fast” really means

Interrupt latency is the time from event → first instruction of ISR.
It includes:

  • Peripheral flag set,

  • Interrupt controller arbitration,

  • Current instruction completion,

  • Context stacking and vector fetch.

It’s typically microseconds or less on many MCUs, but depends on clock speed, memory wait states, and whether interrupts are masked.


Common pitfalls (the stuff that makes interrupts “mysteriously broken”)

1) Doing too much work in the ISR

If the ISR is long:

  • You increase jitter,

  • You block other interrupts,

  • You risk missing real-time deadlines.

Pattern: ISR does minimal work → main loop/RTOS task does the heavy processing.

2) Forgetting volatile for shared flags

If the ISR sets a flag read by main code, mark it volatile, or the compiler may optimize the read away.

3) Race conditions on multi-byte variables

If ISR updates a 32-bit value on an 8/16-bit MCU, main code might read it mid-update.
Fix with:

  • Disable interrupts briefly around the read, or

  • Use atomic operations / critical sections.

4) Not clearing the interrupt source

You must clear the correct status bit (and sometimes read a register to clear it) per datasheet.

5) Priority inversion / wrong priorities

A low-priority ISR holding a resource can indirectly delay a high-priority task (especially with RTOS).


A practical example

UART receive interrupt

  • Byte arrives → UART sets RXNE flag.

  • UART interrupt pending → CPU jumps to UART ISR.

  • ISR reads UART_DR (data register), which also clears RXNE.

  • ISR stores byte in a ring buffer and returns.

  • Main loop later parses complete messages.

That’s the classic “ISR = capture data fast, main = process later.”

评论

此博客中的热门博文

Detailed Explanation of STM32 HAL Library Clock System

How to remove write protection of STM32 chip?

The automatic white balance algorithm of Raspberry Pi