Interrupts & ISRs in C
How interrupt handlers are written in C, the rules they must follow, and how to share data with main safely.
An interrupt stops the main program, runs a handler (interrupt service routine, ISR), then resumes exactly where it left off. On Cortex-M an ISR is an ordinary C function with no arguments and no return value; its address sits in the vector table.
Writing an ISR
The handler's name must match the vector table entry (EXTI15_10_IRQHandler for pins 10–15 on STM32F4).
Clearing the pending flag is mandatory; otherwise the interrupt fires again the moment the handler returns.
The ISR only records the event; the slow work runs in main.
irq_save/irq_restore form a critical section that is safe even if interrupts were already off.
Shared data and atomicity
void USART2_IRQHandler(void) { char c = USART2->DR; printf("%c", c); }
Put the byte into a ring buffer; print from main printf in an ISR is slow and usually non-reentrant — it can corrupt output or deadlock if main was printing.
void EXTI0_IRQHandler(void) { handle(); } // flag never cleared
Write 1 to EXTI->PR for that line inside the handler An uncleared pending flag re-triggers the interrupt immediately; the CPU never gets back to main.
Why must a variable shared between an ISR and main be volatile?
So the compiler does not keep it in a register: main must re-read it from memory to see the ISR's changes, and the ISR's writes must actually reach memory.
What is a critical section and how long should it be?
A code region during which interrupts (or the scheduler) are disabled so a multi-step update cannot be interrupted. As short as possible — it delays every interrupt.
Is reading a 32-bit variable atomic on Cortex-M? On an 8-bit AVR?
On Cortex-M an aligned 32-bit read is a single instruction, so yes. On an 8-bit AVR it takes four byte reads, so an ISR can change it halfway — protect it.