Interrupts
Interrupts let hardware get the processor's attention immediately. Learn polling vs interrupts, the interrupt sequence (save context, vector, ISR, return), vectored and priority interrupts, maskable vs non-maskable, the 8085/8086 schemes, the ARM NVIC, latency, nesting, and writing safe interrupt service routines.
Without interrupts, a processor must repeatedly check (poll) every device — wasting time and missing fast events. Interrupts let a timer, UART or button trigger a short service routine, then resume the main program exactly where it left off.
An analogy
Polling is checking your phone every minute for messages; interrupts are the phone ringing — you respond only when something actually happens.
The interrupt sequence
Device raises a request → CPU finishes the current instruction → saves PC and flags (context) → looks up the ISR address (vector) → runs the ISR → returns (RETI/IRET/exception return) and restores context.
Types and priority
Maskable interrupts can be disabled; NMIs cannot (power failure, watchdog). Vectored interrupts jump directly to a known address; priorities decide which runs first. 8085: TRAP > RST 7.5 > 6.5 > 5.5 > INTR. 8086: 256 types in the IVT; 8259 PIC expands external lines.
ARM NVIC
Cortex-M's Nested Vectored Interrupt Controller supports hundreds of sources with programmable priorities, nesting (higher priority pre-empts), tail-chaining and late arrival — giving 12-cycle deterministic latency.
Writing good ISRs
Keep ISRs short (set flags, move data to buffers), avoid blocking calls and printf, use volatile and atomic access, clear the interrupt flag, consider priority inversion, and defer heavy work to the main loop or RTOS tasks.
Formulas
- Latency:
t_lat = entry cycles / f_clk (Cortex-M: 12 cycles) - CPU load from interrupts:
Load = ISR cycles × interrupt rate / f_clk - 8086 vector address:
Vector = type × 4
Worked example: Cortex-M latency
Given: 12-cycle entry, 72 MHz
Find: Latency.
- t = 12/72 × 10⁶ = 0.167 µs
Answer: ≈ 167 ns
Worked example: CPU load
Given: ISR 200 cycles at 1 kHz, 72 MHz CPU
Find: Load.
- 200 × 1000/72 × 10⁶ = 0.28 %
Answer: 0.28 % of CPU time
Timer ISR on Cortex-M (C, CMSIS style)
#include <stdint.h>
#include "stm32f4xx.h"
volatile uint32_t ms_ticks = 0; /* shared with main: volatile */
void SysTick_Handler(void) { /* ISR name from the vector table */
ms_ticks++; /* keep ISRs short */
}
int main(void) {
SysTick_Config(SystemCoreClock / 1000); /* 1 kHz interrupt */
NVIC_SetPriority(USART2_IRQn, 2); /* lower number = higher priority */
NVIC_EnableIRQ(USART2_IRQn);
while (1) {
if (ms_ticks >= 500) { /* 500 ms elapsed */
ms_ticks = 0;
GPIOA->ODR ^= (1u << 5); /* toggle LED */
}
}
}The hardware stacks registers and jumps to the handler listed in the vector table. Shared variables must be volatile, and longer multi-byte updates need critical sections.
Common mistakes
- Wrong: Long processing inside ISRs. — Right: Set flags or buffer data; process in main/tasks.
- Wrong: Shared variables not declared volatile. — Right: Use volatile and critical sections.
Applications
- Timers: Periodic ticks for scheduling and control loops.
- Communication: UART/SPI receive interrupts.
- Safety: NMI for watchdog and power-fail.
Key takeaways
- Interrupts beat polling for events.
- Save context → vector → ISR → return.
- Priorities and NMIs.
- Keep ISRs short; use volatile.
Going further
- Tail-chaining: Back-to-back ISRs without full restore.
- Priority inversion: RTOS mutex issues.
- Interrupt storms: Rate limiting and masking.