Learn what an Interrupt Service Routine (ISR) is, how it works, with C examples, NVIC priorities, latency tips, Linux top/bottom halves, and common pitfalls.An Interrupt Service Routine (ISR) is a short, high-priority function that runs automatically when hardware or software triggers an interrupt. Its job is to respond fast, clear the source, and defer heavy work, so the system stays responsive and deterministic.
What Is an Interrupt Service Routine?
An Interrupt Service Routine (ISR) is a special function invoked by the CPU when an interrupt occurs—an asynchronous event like a timer tick, GPIO edge, UART byte received, or a fault condition. The CPU pauses the main thread, saves minimal context, jumps to the ISR address from the interrupt vector table, executes the routine, then returns to the pre-empted code.
Why ISRs Exist
- Responsiveness: Process time-critical events immediately.
- Efficiency: Avoid constant polling loops.
- Determinism: Bound the delay (latency) to meet real-time deadlines.
Core Concepts You Should Know
Interrupt Vector Table (IVT)
A table of addresses that map each interrupt source to its ISR entry point. On ARM Cortex-M, the IVT (vector table) starts at a fixed address (often 0x00000000 or relocated) and includes reset and exception vectors.
Maskable vs Non-Maskable
- Maskable interrupts: Can be disabled (masked) by the CPU or software.
- Non-Maskable Interrupt (NMI): Highest priority, cannot be masked, used for critical faults.
Priority and Preemption (NVIC on ARM Cortex-M)
The Nested Vectored Interrupt Controller (NVIC) assigns priorities. A higher-priority interrupt can preempt a lower one (nested interrupts). Proper priority design reduces worst-case latency for critical sources.
Latency vs Response vs Service Time
- Interrupt latency: Time from event to ISR start.
- Response time: Latency plus any hardware pipeline delays.
- Service time: Time spent inside the ISR.
Goal: Minimize latency and service time to protect real-time behavior.
Golden Rules for Writing ISRs
- Keep ISRs short and deterministic.
- Clear the interrupt source early (status/flag register).
- Never block: no delays, no busy-waits, avoid
printf
. - Avoid dynamic allocation and heavy computations.
- Use
volatile
for shared variables modified in ISR and main/threads. - Defer work to a main loop, RTOS task, or bottom half.
- Minimize critical sections (time with interrupts disabled).
- Make ISRs reentrant-safe or explicitly non-reentrant via hardware.
- Respect priority scheme; test worst-case nesting.
- Instrument and measure latency/jitter with GPIO toggles + scope/logic analyzer.
Minimal C Example (Bare-Metal, Cortex-M Style)
#include <stdint.h>
#include "stm32f4xx.h" // device header (example)
volatile uint8_t button_event = 0;
void EXTI0_IRQHandler(void) {
// Check pending flag for EXTI line 0
if (EXTI->PR & EXTI_PR_PR0) {
EXTI->PR = EXTI_PR_PR0; // 1) Clear interrupt source early
button_event = 1; // 2) Set a small flag (volatile)
// 3) Do not debounce here; defer heavy work to main/task
}
}
int main(void) {
// ... GPIO/EXTI/NVIC init: configure PA0 as input, EXTI0 on rising edge
while (1) {
if (button_event) {
button_event = 0;
// Handle button: debounce in main context or schedule a task
}
// other non-blocking work
}
}
Why this is good: short ISR, clears the flag, uses a volatile
flag, defers the work.
With an RTOS (FreeRTOS) — Deferring Work Correctly
// Assume a task waits on a notification or queue
extern TaskHandle_t buttonTaskHandle;
void EXTI0_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (EXTI->PR & EXTI_PR_PR0) {
EXTI->PR = EXTI_PR_PR0;
vTaskNotifyGiveFromISR(buttonTaskHandle, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // request context switch if needed
}
}
Notes: Use the FromISR
variants only. They are designed to be ISR-safe and avoid locking issues.
Linux Driver Perspective: Top Half vs Bottom Half
In the Linux kernel, the top half is the fast interrupt handler (ISR) that acknowledges the device and schedules a bottom half (e.g., tasklet or workqueue) to complete longer processing in process context.
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
struct mydev *d = dev_id;
u32 status = readl(d->mmio + STATUS);
if (!(status & IRQ_OCCURRED))
return IRQ_NONE;
writel(status, d->mmio + STATUS); // Ack/clear quickly
schedule_work(&d->work); // Defer heavy work
return IRQ_HANDLED;
}
This pattern mirrors the embedded “set a flag and get out quickly” philosophy.
Designing Priorities and Handling Nested Interrupts
- Put hard real-time sources (e.g., motor control, high-rate ADC, safety signals) at higher priority.
- Keep their ISRs ultra-short; move computations to lower priority tasks.
- Test nesting: Use synthetic interrupt storms to verify the system remains stable and meets deadlines.
- Beware of priority inversion with shared resources — use lock-free queues or ISR-safe ring buffers.
Reducing Interrupt Latency and Jitter
- Disable interrupts for the shortest possible time.
- Avoid large critical sections (
__disable_irq()
/__enable_irq()
sparingly). - Use branch-free, cache-friendly code in hot paths.
- Ensure vector table is in fast memory (when relocatable).
- Tune compiler optimization for ISR sections (
inline
,-O2/-O3
judiciously). - Prefer DMA + ISR for completion events instead of byte-wise ISRs.
Common Mistakes (and Fixes)
- Forgetting
volatile
: Variables changed in ISR must bevolatile
to avoid compiler reordering/optimization issues. - Not clearing the interrupt flag: Causes immediate retrigger or lockup.
- Work inside ISR too heavy: Leads to missed deadlines; always defer.
- Calling non-reentrant APIs (like
printf
, malloc) in ISR: avoid or provide ISR-safe alternatives. - Priority misconfiguration: A low-priority critical ISR getting delayed.
- Debouncing in ISR: Use timers or software debouncing in main/task context instead.
Testing & Debugging ISRs
- Oscilloscope method: Toggle a GPIO at ISR entry/exit to measure latency and service time.
- Logic analyzer: Correlate multiple events (e.g., RX line vs ISR start).
- Fault handlers: Implement HardFault/NMI handlers with minimal logging hooks.
- Stress tests: Burst interrupts, nested paths, and DMA completion storms.
- Static analysis: Check for race conditions, missing
volatile
, and reentrancy issues.
Quick ISR Checklist (Pin or print)
- ISR is short and clears the source early
- Uses
volatile
for shared flags/counters - No blocking calls, no dynamic allocation
- Work deferred to main/task/bottom half
- Priorities documented and tested for nesting
- Latency/jitter measured with GPIO or tracing
- Minimal time with interrupts disabled
- Safe access to shared peripherals/buffers
FAQ: Interrupt Service Routine
Q1. What exactly is an Interrupt Service Routine?
An ISR is a high-priority function that automatically runs when an interrupt occurs, handles the event quickly, and returns control to normal code.
Q2. What should never be inside an ISR?
Blocking calls, long loops, printf
, dynamic memory allocation, or any heavy computation. Defer to a task/bottom half.
Q3. How do I share data between ISR and main code safely?
Use volatile
for simple flags/counters. For larger data, use lock-free ring buffers or RTOS queues with FromISR
APIs.
Q4. What is interrupt latency and how do I reduce it?
Latency is the delay from the event to ISR start. Reduce by shortening disabled-interrupt regions, optimizing priorities, and keeping ISRs minimal.
Q5. Are nested interrupts good or bad?
They’re essential for critical events but must be used carefully. Keep higher-priority ISRs ultra-short and validate worst-case timing.
Conclusion
An Interrupt Service Routine is the backbone of a responsive embedded and real-time system. Design for speed, simplicity, and determinism: acknowledge the event, clear the source, and defer the heavy work. With disciplined priorities, careful sharing of data, and solid testing, your ISRs will meet deadlines and keep the whole system stable.
You can also Visit other tutorials of Embedded Prep
- Multithreading in C++
- Multithreading Interview Questions
- Multithreading in Operating System
- Multithreading in Java
- POSIX Threads pthread Beginner’s Guide in C/C++
- Speed Up Code using Multithreading
- Limitations of Multithreading
- Common Issues in Multithreading
- Multithreading Program with One Thread for Addition and One for Multiplication
- Advantage of Multithreading
- Disadvantages of Multithreading
- Applications of Multithreading: How Multithreading Makes Modern Software Faster and Smarter”
- Master CAN Bus Interview Questions 2025
- What Does CAN Stand For in CAN Bus?
- CAN Bus Message Filtering Explained
- CAN Bus Communication Between Nodes With Different Bit Rates
- How Does CAN Bus Handle Message Collisions
- Message Priority Using Identifiers in CAN Protocol

Mr. Raj Kumar is a highly experienced Technical Content Engineer with 7 years of dedicated expertise in the intricate field of embedded systems. At Embedded Prep, Raj is at the forefront of creating and curating high-quality technical content designed to educate and empower aspiring and seasoned professionals in the embedded domain.
Throughout his career, Raj has honed a unique skill set that bridges the gap between deep technical understanding and effective communication. His work encompasses a wide range of educational materials, including in-depth tutorials, practical guides, course modules, and insightful articles focused on embedded hardware and software solutions. He possesses a strong grasp of embedded architectures, microcontrollers, real-time operating systems (RTOS), firmware development, and various communication protocols relevant to the embedded industry.
Raj is adept at collaborating closely with subject matter experts, engineers, and instructional designers to ensure the accuracy, completeness, and pedagogical effectiveness of the content. His meticulous attention to detail and commitment to clarity are instrumental in transforming complex embedded concepts into easily digestible and engaging learning experiences. At Embedded Prep, he plays a crucial role in building a robust knowledge base that helps learners master the complexities of embedded technologies.
Leave a Reply