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

Worked example: Cortex-M latency

Given: 12-cycle entry, 72 MHz

Find: Latency.

Answer: ≈ 167 ns

Worked example: CPU load

Given: ISR 200 cycles at 1 kHz, 72 MHz CPU

Find: Load.

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

Applications

Key takeaways

Going further