Embedded & Bare-Metal

an interrupt service routine (ISR)

/ EYE-ess-ar /

Picture yourself reading a book when the doorbell rings. You bookmark your page, get up to answer the door, deal with whoever is there as quickly as you can, then come back and resume reading exactly where you left off. An interrupt service routine, or ISR, is the function the processor runs when 'the doorbell rings' — when a piece of hardware demands immediate attention — after which it returns to whatever it was doing.

Mechanically: when an interrupt fires, the hardware automatically saves enough of the current state (on Cortex-M it pushes several registers onto the stack), looks up the handler in the vector table, and jumps to it. Your ISR runs, does its small job — read the received byte, clear the timer flag, copy a sample — and then executes a special return that restores the saved state, and the interrupted code continues as if nothing happened. Two rules dominate good ISR writing. First, KEEP IT SHORT: while an ISR runs, other work (and often other interrupts) is delayed, so do the minimum and defer heavy work to the main loop. Second, BE CAREFUL WITH SHARED DATA: an ISR can fire between any two instructions of your main code, so a variable that both touch must be marked volatile (so the compiler always re-reads it) and accessed atomically or with interrupts briefly disabled, or you get a data race. A common pattern is the ISR sets a volatile flag and the main loop notices it.

It matters because ISRs are how an MCU responds to the real world promptly without burning CPU cycles constantly checking (the alternative, polling). The honest pitfalls: an ISR generally must not call functions that block or that are not re-entrant (printf(), malloc(), long delays) — doing so can deadlock or corrupt state; you must clear the hardware's interrupt flag or the ISR will re-fire endlessly; and a variable shared with the ISR that is NOT volatile may be cached in a register, so the main loop never sees the update. 'It worked in testing' is especially dangerous here, because races and missed-flag bugs are timing-dependent and intermittent.

volatile uint32_t ticks = 0; // shared with main: must be volatile void SysTick_Handler(void) { // fires every 1 ms ticks++; // tiny, fast, no blocking calls } // hardware auto-clears SysTick flag // main loop just reads it: uint32_t now = ticks; // always re-read thanks to volatile

A 1 ms tick ISR: short, sets a volatile counter, no printf or malloc. The main loop reads the counter through that volatile variable.

Keep ISRs tiny and non-blocking, clear the hardware flag, and mark any variable shared with the main code volatile — and even volatile is not enough for a multi-byte value that the ISR might split: protect those with a brief interrupts-off section.

Also called
ISRinterrupt handler中斷處理常式中斷處理函式