What Is the Purpose of an Interrupt in an Embedded System?

What is the purpose of an interrupt in an embedded system?

I once wrote a firmware loop that constantly checked a button, over and over, thousands of times a second, just waiting for it to be pressed. It worked, technically, but it burned through the battery embarrassingly fast and made the whole system feel sluggish whenever I tried to add anything else to the main loop. A more experienced engineer took one look at my code and said, “Why are you polling? Use an interrupt.” That conversation changed how I thought about embedded programming entirely.

What Is an Interrupt?

An interrupt is a hardware mechanism that allows a peripheral or external event to immediately pause whatever the processor is currently doing, jump to a special piece of code called an Interrupt Service Routine (ISR), handle the event, and then return to exactly where it left off — all without the main program needing to constantly check for that event itself.

Think of it like a phone ringing while you’re reading a book. You don’t need to glance at your silent phone every ten seconds to check for calls — when it actually rings, you notice immediately, answer it, and go back to your page. Interrupts give processors that same “notify me when something happens” capability.

sequenceDiagram
    participant Main as Main Program Loop
    participant CPU as CPU Core
    participant Periph as Peripheral (e.g., Button/Timer)
    participant ISR as Interrupt Service Routine

    Main->>CPU: Executing normal instructions
    Periph->>CPU: Interrupt signal raised
    CPU->>CPU: Save current context (registers, PC)
    CPU->>ISR: Jump to Interrupt Service Routine
    ISR->>ISR: Handle the event
    ISR->>CPU: Return from interrupt
    CPU->>Main: Restore context, resume exactly where it left off

Why Interrupts Matter

1. Responsiveness

Interrupts let the processor react to events almost immediately, rather than waiting for the next iteration of a polling loop, which might be busy doing something else entirely.

2. Efficiency and Power Savings

Without interrupts, the processor would have to continuously poll every input it cares about, burning CPU cycles and power even when nothing is happening. With interrupts, the processor can enter a low-power sleep mode and simply wake up when an interrupt occurs — a critical capability for battery-powered devices.

3. Simplified Program Structure

Interrupts let developers write cleaner, event-driven code instead of one giant loop trying to juggle dozens of conditions at once.

Polling vs. Interrupts: A Direct Comparison

graph TD
    subgraph "Polling Approach"
    P1[Main Loop] --> P2{Check Button?}
    P2 -->|No| P1
    P2 -->|Yes| P3[Handle Button Press]
    P3 --> P1
    end

    subgraph "Interrupt Approach"
    I1[Main Loop / Sleep] -.interrupted by.-> I2[Button ISR]
    I2 --> I3[Handle Button Press]
    I3 -.resume.-> I1
    end
AspectPollingInterrupts
CPU usageHigh (constant checking)Low (idle until event)
Power consumptionHigherLower (enables sleep modes)
ResponsivenessDepends on loop timingImmediate
Code complexitySimple for single tasksRequires careful design for shared state
Best suited forVery simple, always-on tasksTime-sensitive or infrequent events

Types of Interrupts

Hardware Interrupts

Triggered by physical events: a GPIO pin changing state (like a button press), a timer reaching a certain count, a UART receiving a byte, or an ADC finishing a conversion.

Software Interrupts

Triggered deliberately by code, often used to implement system calls or trigger context switches in an RTOS.

External vs. Internal Interrupts

External interrupts originate from outside the chip (a button, an external sensor signal). Internal interrupts come from on-chip peripherals like timers or communication controllers.

The Anatomy of Handling an Interrupt

When an interrupt occurs, the processor goes through a well-defined sequence:

  1. Detection: The peripheral hardware signals the interrupt controller (e.g., the NVIC in ARM Cortex-M chips).
  2. Prioritization: If multiple interrupts are pending, the controller determines which one to service first, based on configured priority levels.
  3. Context saving: The processor automatically pushes key registers onto the stack, preserving the state of the interrupted program.
  4. Vector lookup: The processor looks up the address of the appropriate Interrupt Service Routine (ISR) from the interrupt vector table.
  5. ISR execution: The processor jumps to and executes the ISR.
  6. Context restoration: Once the ISR finishes, the processor restores the saved registers and resumes the interrupted program exactly where it left off.
graph TD
    A[Event Occurs] --> B[Interrupt Controller Signals CPU]
    B --> C[CPU Saves Context]
    C --> D[Look Up ISR Address in Vector Table]
    D --> E[Execute ISR]
    E --> F[Restore Context]
    F --> G[Resume Interrupted Program]

Code Example: A Button Interrupt

Here’s a practical example configuring an external interrupt on an STM32-style microcontroller, triggered by a button press:

#include "stm32f4xx.h"

volatile uint8_t button_pressed_flag = 0;

void EXTI0_IRQHandler(void) {
    if (EXTI->PR & (1 << 0)) {         // Check if interrupt pending on line 0
        button_pressed_flag = 1;        // Set flag for main loop to handle
        EXTI->PR |= (1 << 0);           // Clear the pending flag
    }
}

int main(void) {
    // (Clock, GPIO, and EXTI configuration omitted for brevity)
    NVIC_EnableIRQ(EXTI0_IRQn);         // Enable the interrupt in the NVIC

    while (1) {
        if (button_pressed_flag) {
            button_pressed_flag = 0;
            handle_button_press();
        }
        __WFI();  // Wait For Interrupt — sleep until next event
    }
}

Notice the pattern: the ISR itself does minimal work — just setting a flag — and the actual processing happens in the main loop. This is a deliberate and important best practice.

Best Practice: Keep ISRs Short

A common mistake, especially for beginners, is putting too much logic directly inside an ISR. This is risky because:

The standard practice is: do the minimum necessary work inside the ISR (read a value, set a flag, push data into a queue), and defer the heavier processing to the main loop or a dedicated task, especially in RTOS-based designs.

Interrupt Priorities and Nesting

Most microcontrollers support multiple interrupt priority levels, and many allow higher-priority interrupts to preempt (interrupt) lower-priority ones already being handled.

sequenceDiagram
    participant Low as Low-Priority ISR (e.g., UART data ready)
    participant High as High-Priority ISR (e.g., safety sensor trigger)
    participant CPU as CPU Core

    CPU->>Low: Start executing Low-Priority ISR
    High->>CPU: High-priority interrupt occurs
    CPU->>Low: Pause Low-Priority ISR
    CPU->>High: Execute High-Priority ISR
    High->>CPU: ISR complete
    CPU->>Low: Resume Low-Priority ISR
    Low->>CPU: ISR complete, resume main program

This prioritization ensures that truly critical events — like a safety-related sensor trigger — get handled immediately, even if a lower-priority interrupt is already being processed.

Interrupts and Race Conditions

Because ISRs can run at almost any point during normal program execution, shared variables accessed both inside an ISR and in the main loop must be handled carefully — typically declared volatile, and sometimes protected by briefly disabling interrupts during critical sections, to avoid race conditions where data is read or modified inconsistently.

void critical_section_example(void) {
    __disable_irq();       // Temporarily disable interrupts
    shared_counter++;      // Safely modify shared data
    __enable_irq();        // Re-enable interrupts
}

Interrupts in Real-Time and RTOS-Based Systems

In systems using a Real-Time Operating System, interrupts often trigger the release of a task or the setting of a semaphore, letting the RTOS scheduler decide which task should run next based on priority — a pattern that scales far better than manually managing everything inside ISRs as system complexity grows. I cover this pattern more fully in the dedicated article on RTOS.

Frequently Asked Questions

Can interrupts be disabled? Yes. Firmware can globally disable interrupts (useful briefly during critical, timing-sensitive sections of code) or selectively disable specific interrupt sources, though this should be done sparingly and for as short a duration as possible.

What happens if two interrupts occur at exactly the same time? The interrupt controller uses configured priority levels to decide which one gets serviced first. If they have equal priority, the order is typically determined by a fixed hardware precedence defined in the chip’s interrupt vector table.

Is an interrupt the same as a function call? Not quite. A function call is predictable and initiated by the program itself. An interrupt is asynchronous — it can occur at virtually any point during program execution, triggered by hardware or an external event, and the processor automatically saves and restores context around it.

Why not just make everything interrupt-driven? Excessive or poorly managed interrupts can make a system harder to reason about, introduce subtle bugs from shared-state race conditions, and increase latency for other interrupts if ISRs aren’t kept short. Good embedded design uses interrupts where responsiveness genuinely matters, and simpler polling or scheduled checks elsewhere.

Summary

Interrupts exist to let an embedded system respond immediately and efficiently to events, without wasting processing power and battery life on constant polling. When a hardware event occurs — a button press, a timer expiring, incoming data — the processor pauses its current work, runs a short interrupt service routine to handle the event, and then resumes exactly where it left off. Used well, with short ISRs, proper prioritization, and careful handling of shared data, interrupts are one of the most powerful tools in an embedded developer’s toolbox for building responsive, efficient, real-time systems.

References and Further Reading

Exit mobile version