The "Holy Bible" for embedded engineers
Mastering Interrupt Handling, ISR Design, and Exception Management
Learn to implement robust interrupt systems, design efficient ISRs, and handle exceptions for reliable embedded systems
Interrupts and exceptions are fundamental mechanisms that allow embedded systems to respond to external events and handle errors efficiently. Understanding interrupt handling is crucial for real-time system design and reliable embedded applications.
Normal Execution → Interrupt Occurs → Context Save → ISR Execution → Context Restore → Resume Execution
↓ ↓ ↓ ↓ ↓ ↓
Main Loop Hardware Event Save Regs Process IRQ Restore Regs Continue
Priority 0 (Highest) ──┐
├── Can interrupt any lower priority
Priority 1 ────────────┤
├── Cannot interrupt Priority 0
Priority 2 ────────────┤
└── Cannot interrupt Priority 0 or 1
Priority 3 (Lowest) ───┘
Exceptions (Internal) Interrupts (External)
↓ ↓
Hardware Faults Peripheral Events
Memory Violations GPIO Changes
Illegal Instructions Timer Expiry
Stack Overflow Communication Events
Interrupts represent a fundamental shift from polling-based systems to event-driven architectures. Instead of continuously checking for events, the system waits for hardware to signal when something needs attention. This paradigm enables:
Interrupts are the backbone of real-time embedded systems. They enable:
Designing interrupt systems involves balancing several competing concerns:
Why it matters: The vector table is the central nervous system of interrupt handling. It maps each interrupt source to its corresponding handler function, enabling the CPU to jump to the right code when interrupts occur.
Minimal example
// Basic interrupt handler registration
typedef void (*interrupt_handler_t)(void);
// Simple vector table structure
typedef struct {
interrupt_handler_t reset_handler;
interrupt_handler_t nmi_handler;
interrupt_handler_t hardfault_handler;
interrupt_handler_t irq_handlers[16]; // External interrupts
} vector_table_t;
// Register a handler for a specific interrupt
void register_interrupt_handler(uint8_t irq_number, interrupt_handler_t handler) {
if (irq_number < 16) {
vector_table.irq_handlers[irq_number] = handler;
}
}
Try it: Create a simple vector table with handlers for timer and UART interrupts.
Takeaways
Why it matters: ISR design directly impacts system responsiveness and reliability. Poorly designed ISRs can cause missed interrupts, priority inversion, and system instability.
Minimal example
// Good ISR design - minimal and fast
volatile bool data_ready = false;
volatile uint8_t received_data = 0;
void uart_rx_isr(void) {
// Clear interrupt flag immediately
UART1->SR &= ~UART_SR_RXNE;
// Minimal processing - just capture data
received_data = UART1->DR;
data_ready = true;
// Let main loop handle the data processing
}
Try it: Design an ISR for a GPIO button press that sets a flag for main loop processing.
Takeaways
Why it matters: Proper priority management ensures critical interrupts get immediate attention while preventing priority inversion and ensuring system responsiveness.
Minimal example
// Configure interrupt priorities
void configure_interrupt_priorities(void) {
// Set system timer to highest priority (lowest number)
NVIC_SetPriority(SysTick_IRQn, 0);
// Set UART to medium priority
NVIC_SetPriority(UART1_IRQn, 2);
// Set GPIO to lower priority
NVIC_SetPriority(EXTI0_IRQn, 4);
// Enable all interrupts
NVIC_EnableIRQ(SysTick_IRQn);
NVIC_EnableIRQ(UART1_IRQn);
NVIC_EnableIRQ(EXTI0_IRQn);
}
Try it: Configure three interrupts with different priorities and observe nesting behavior.
Takeaways
Why it matters: Exceptions represent system errors that must be handled gracefully. Proper exception handling prevents system crashes and enables recovery from transient faults.
Minimal example
// Basic hard fault handler
void hardfault_handler(void) {
// Capture fault information
uint32_t fault_address = SCB->MMFAR; // Memory fault address
uint32_t fault_status = SCB->CFSR; // Combined fault status
// Log fault information (if possible)
log_fault_info(fault_address, fault_status);
// Attempt recovery or reset
if (can_recover_from_fault(fault_status)) {
// Try to continue
return;
} else {
// Reset system if recovery not possible
system_reset();
}
}
Try it: Implement a fault handler that logs error information and attempts recovery.
Takeaways
Objective: Measure the time from interrupt trigger to ISR execution.
Steps:
Expected Outcome: Understanding of interrupt overhead and factors affecting latency.
Objective: Observe how interrupt priorities affect nesting behavior.
Steps:
Expected Outcome: Understanding of interrupt priority mechanisms and nesting behavior.
Objective: Implement robust exception handling for system faults.
Steps:
Expected Outcome: Practical experience with exception handling and fault recovery.
Next Topic: Reset Management → Timer/Counter Programming