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.”

评论
发表评论