Interrupts, Concurrency, and Critical Sections
Interrupts let firmware respond quickly to hardware events, but they also create concurrency. Main code and interrupt handlers can touch the same data at different times, so correctness depends on ownership, atomicity, and bounded latency.
Learning Objectives
By the end of this lesson, you should be able to explain interrupt latency, protect shared data, choose critical sections carefully, and debug common race conditions in bare-metal firmware.
Interrupt Flow
A good ISR captures the event, clears the hardware condition, stores minimal data, and lets main code do the longer work.
Shared Data
If an ISR writes a variable read by main code, mark it volatile so the compiler reloads it.
static volatile bool rx_ready;
void UART_IRQHandler(void)
{
rx_ready = true;
}
Volatile does not make multi-byte updates atomic and does not protect compound operations.
Critical Sections
uint32_t flags_snapshot;
disable_interrupts();
flags_snapshot = event_flags;
event_flags = 0;
enable_interrupts();
Keep critical sections short. Long interrupt masking increases latency and can lose hardware events.
Latency Budget
$$
T_{response}=T_{mask}+T_{higherISR}+T_{other}
$$
$$
T_{other}=T_{entry}+T_{handler}
$$
Compare response time to the hardware deadline. A UART receive interrupt must service data before the receive FIFO overflows.
Worked Example: Button Event
Bad design: debounce inside the GPIO ISR with a 20 ms delay. Better design: GPIO ISR records timestamp and sets button_edge; a main-loop or timer task waits for stable state and generates a debounced event.
Practical Checks
- List every variable shared between ISR and main code.
- Confirm which operations are atomic on the target MCU.
- Measure longest ISR time with a GPIO toggle or trace.
- Avoid blocking calls, logging, and dynamic allocation in ISRs.
- Use ring buffers for byte streams.
Common Mistakes
- Thinking
volatileprevents races. - Clearing interrupt flags in the wrong order.
- Calling slow protocol stacks from ISRs.
- Disabling interrupts around large computations.
- Forgetting priority inversion between interrupts and RTOS tasks.
Summary
Interrupts are necessary for responsive firmware, but they require clear rules. Keep ISRs short, protect shared data, understand atomicity, and measure latency against hardware deadlines.
Further Reading
- Arm, "Cortex-M Interrupts and Exception Handling."
- FreeRTOS documentation on ISRs and interrupt priorities.
- Jack Ganssle, "Interrupts" and debounce articles.