The Embedded New Testament

The "Holy Bible" for embedded engineers


Project maintained by theEmbeddedGeorge Hosted on GitHub Pages — Theme by mattgraham

πŸ”Œ External Interrupts

Quick Reference: Key Facts

Mastering External Interrupt Handling for Responsive Embedded Systems
Learn to implement edge/level triggered interrupts, debouncing techniques, and interrupt-driven designs


πŸ“‹ Table of Contents


🎯 Overview

External interrupts allow embedded systems to respond immediately to external events without polling. They are essential for real-time applications, user interfaces, and efficient system design.

Concept: Edges vs levels, and clearing the source

Choose edges to capture transitions; levels to detect sustained conditions. Always clear the source appropriately and consider masking during long handlers.

Key Concepts


πŸ” Visual Understanding

Edge vs Level Triggered Interrupts

Edge-Triggered Interrupts
Input Signal
   ^
   β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚    β”‚                 β”‚
   β”‚    β”‚                 β”‚
   β”‚    β”‚                 β”‚
   +──────────────────────────-> Time
   β–²         β–Ό
Rising    Falling
 Edge      Edge

Interrupt Triggers
   ^
   β”‚    β”‚         β”‚
   β”‚    β”‚         β”‚
   β”‚    β”‚         β”‚
   +──────────────────────────-> Time
   β”‚<->β”‚ Interruptβ”‚<->β”‚ Interrupt
       β”‚  Trigger β”‚   β”‚  Trigger

Level-Triggered Interrupts
Input Signal
   ^
   β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚    β”‚                 β”‚
   β”‚    β”‚                 β”‚
   β”‚    β”‚                 β”‚
   +──────────────────────────-> Time
   β”‚<->β”‚ High Level β”‚<->β”‚ Low Level
       β”‚  Trigger   β”‚   β”‚  Trigger

Interrupt Processing Flow

Interrupt Processing Pipeline
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    External Event                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Hardware  │───▢│ Interrupt   │───▢│   CPU Core  β”‚   β”‚
β”‚  β”‚   Detection β”‚    β”‚ Controller  β”‚    β”‚  Response   β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚         β”‚                   β”‚                   β”‚         β”‚
β”‚         β–Ό                   β–Ό                   β–Ό         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Signal    β”‚    β”‚   Priority  β”‚    β”‚   Context   β”‚   β”‚
β”‚  β”‚  Condition  β”‚    β”‚  Resolution β”‚    β”‚  Switching  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚         β”‚                   β”‚                   β”‚         β”‚
β”‚         β–Ό                   β–Ό                   β–Ό         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   ISR       β”‚    β”‚   Return    β”‚    β”‚   Resume    β”‚   β”‚
β”‚  β”‚ Execution   β”‚    β”‚   to ISR    β”‚    β”‚   Main      β”‚   β”‚
β”‚  β”‚             β”‚    β”‚             β”‚    β”‚   Program   β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Interrupt Priority and Nesting

Interrupt Priority Levels
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Priority Hierarchy                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚   High      β”‚ β”‚   Medium    β”‚ β”‚    Low      β”‚         β”‚
β”‚  β”‚ Priority    β”‚ β”‚  Priority   β”‚ β”‚  Priority   β”‚         β”‚
β”‚  β”‚ (Level 0)   β”‚ β”‚ (Level 1)   β”‚ β”‚ (Level 2)   β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚         β”‚               β”‚               β”‚                 β”‚
β”‚         β–Ό               β–Ό               β–Ό                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚   Can       β”‚ β”‚   Can       β”‚ β”‚   Cannot    β”‚         β”‚
β”‚  β”‚ Interrupt   β”‚ β”‚ Interrupt   β”‚ β”‚ Interrupt   β”‚         β”‚
β”‚  β”‚   All       β”‚ β”‚   Lower     β”‚ β”‚   Higher    β”‚         β”‚
β”‚  β”‚   Levels    β”‚ β”‚   Levels    β”‚ β”‚   Levels    β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🧠 Conceptual Foundation

The Interrupt-Driven Paradigm

External interrupts represent a fundamental shift from polling-based to event-driven system design. Instead of continuously checking for external conditions, the system waits for events and responds immediately when they occur.

Key Characteristics:

Why External Interrupts Matter

External interrupts are essential for modern embedded systems:

The Interrupt Design Challenge

Designing effective interrupt systems involves balancing multiple competing requirements:

πŸ”„ Interrupt Types

1. Edge-Triggered Interrupts

Triggered when signal transitions from one state to another.

// Edge-triggered interrupt configuration
typedef enum {
    RISING_EDGE,    // 0 β†’ 1 transition
    FALLING_EDGE,   // 1 β†’ 0 transition
    BOTH_EDGES      // Both transitions
} edge_type_t;

typedef struct {
    uint8_t pin;
    edge_type_t edge;
    uint8_t priority;
    void (*handler)(void);
} external_interrupt_config_t;

2. Level-Triggered Interrupts

Triggered when signal remains at a specific level.

// Level-triggered interrupt configuration
typedef enum {
    HIGH_LEVEL,     // Trigger when high
    LOW_LEVEL       // Trigger when low
} level_type_t;

typedef struct {
    uint8_t pin;
    level_type_t level;
    uint8_t priority;
    void (*handler)(void);
} level_interrupt_config_t;

⚑ Edge vs Level Triggered

Edge-Triggered Advantages

Edge-Triggered Disadvantages

Level-Triggered Advantages

Level-Triggered Disadvantages


βš™οΈ Interrupt Configuration

1. GPIO Interrupt Setup

// Configure GPIO for external interrupt
void configure_external_interrupt(uint8_t pin, edge_type_t edge) {
    // Enable GPIO clock
    RCC->AHB1ENR |= (1 << GPIO_PORT(pin));
    
    // Configure GPIO as input with pull-up
    GPIO_TypeDef *port = GPIO_BASE(pin);
    uint8_t pin_num = GPIO_PIN_NUM(pin);
    
    // Set as input
    port->MODER &= ~(3 << (pin_num * 2));
    
    // Configure pull resistors per board design (pull-up OR pull-down)
    port->PUPDR &= ~(3 << (pin_num * 2));
    if (edge == FALLING_EDGE) {
        port->PUPDR |= (1 << (pin_num * 2)); // pull-up
    } else if (edge == RISING_EDGE) {
        port->PUPDR |= (2 << (pin_num * 2)); // pull-down
    }
    
    // Configure interrupt trigger
    configure_interrupt_trigger(pin, edge);
    
    // Enable interrupt in NVIC with appropriate priority
    enable_nvic_interrupt(EXTI_IRQn);
}

2. Interrupt Trigger Configuration

// Configure interrupt trigger type
void configure_interrupt_trigger(uint8_t pin, edge_type_t edge) {
    uint8_t pin_num = GPIO_PIN_NUM(pin);
    uint8_t exti_line = pin_num;
    
    // Enable SYSCFG clock
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // Connect GPIO to EXTI line
    SYSCFG->EXTICR[exti_line / 4] &= ~(0xF << ((exti_line % 4) * 4));
    SYSCFG->EXTICR[exti_line / 4] |= (GPIO_PORT(pin) << ((exti_line % 4) * 4));
    
    // Configure trigger type
    switch (edge) {
        case RISING_EDGE:
            EXTI->RTSR |= (1 << exti_line);
            EXTI->FTSR &= ~(1 << exti_line);
            break;
        case FALLING_EDGE:
            EXTI->FTSR |= (1 << exti_line);
            EXTI->RTSR &= ~(1 << exti_line);
            break;
        case BOTH_EDGES:
            EXTI->RTSR |= (1 << exti_line);
            EXTI->FTSR |= (1 << exti_line);
            break;
    }
    
    // Enable interrupt
    EXTI->IMR |= (1 << exti_line);
}

πŸ”„ Debouncing Techniques

1. Software Debouncing

// Software debouncing with timer
typedef struct {
    uint8_t pin;
    uint32_t last_trigger_time;
    uint32_t debounce_delay;
    bool last_state;
} debounce_config_t;

debounce_config_t debounce_configs[MAX_INTERRUPTS];

// Debounced interrupt handler
void debounced_interrupt_handler(uint8_t pin) {
    uint32_t current_time = get_system_tick();
    debounce_config_t *config = &debounce_configs[pin];
    
    // Check if enough time has passed since last trigger
    if (current_time - config->last_trigger_time < config->debounce_delay) {
        return; // Ignore this trigger
    }
    
    // Read current pin state
    bool current_state = read_gpio_pin(pin);
    
    // Only process if state actually changed
    if (current_state != config->last_state) {
        config->last_state = current_state;
        config->last_trigger_time = current_time;
        
        // Process the actual interrupt
        process_interrupt_event(pin, current_state);
    }
}

2. Hardware Debouncing

// Hardware debouncing circuit analysis
/*
    Hardware debouncing options:
    
    1. RC Filter:
       Switch --[R]--+--[C]-- GND
                     |
                   Input Pin
    
    2. Schmitt Trigger:
       Switch --[R]-- Schmitt Trigger -- Input Pin
    
    3. Bounce Suppression IC:
       Switch -- Bounce Suppression IC -- Input Pin
*/

// Calculate RC values for debouncing
void calculate_debounce_values(uint32_t bounce_time_ms, uint32_t *r_value, uint32_t *c_value) {
    // Typical bounce time: 1-50ms
    // RC time constant should be > bounce_time/3
    
    uint32_t rc_time = bounce_time_ms * 3; // 3x bounce time for safety
    
    // Choose standard values
    *c_value = 0.1; // 0.1ΞΌF
    *r_value = (rc_time * 1000) / (*c_value * 1000); // R = t/(C*1000) for ΞΌF
}

3. Advanced Debouncing

// State machine debouncing
typedef enum {
    DEBOUNCE_IDLE,
    DEBOUNCE_WAIT,
    DEBOUNCE_CONFIRMED
} debounce_state_t;

typedef struct {
    debounce_state_t state;
    uint32_t start_time;
    uint32_t debounce_time;
    bool stable_state;
} debounce_state_machine_t;

void debounce_state_machine(debounce_state_machine_t *sm, bool current_input) {
    uint32_t current_time = get_system_tick();
    
    switch (sm->state) {
        case DEBOUNCE_IDLE:
            if (current_input != sm->stable_state) {
                sm->state = DEBOUNCE_WAIT;
                sm->start_time = current_time;
            }
            break;
            
        case DEBOUNCE_WAIT:
            if (current_time - sm->start_time >= sm->debounce_time) {
                if (current_input != sm->stable_state) {
                    sm->stable_state = current_input;
                    sm->state = DEBOUNCE_CONFIRMED;
                    // Process state change
                    process_state_change(sm->stable_state);
                } else {
                    sm->state = DEBOUNCE_IDLE;
                }
            }
            break;
            
        case DEBOUNCE_CONFIRMED:
            sm->state = DEBOUNCE_IDLE;
            break;
    }
}

🎯 Interrupt Service Routines

1. Basic ISR Structure

// External interrupt service routine
void EXTI0_IRQHandler(void) {
    // Check if interrupt is pending
    if (EXTI->PR & (1 << 0)) {
        // Clear pending bit
        EXTI->PR = (1 << 0);
        
        // Process interrupt
        process_external_interrupt(0);
    }
}

// Generic external interrupt handler
void process_external_interrupt(uint8_t pin) {
    // Read pin state
    bool pin_state = read_gpio_pin(pin);
    
    // Process based on application
    switch (pin) {
        case BUTTON_PIN:
            handle_button_press(pin_state);
            break;
        case SENSOR_PIN:
            handle_sensor_interrupt(pin_state);
            break;
        default:
            // Unknown pin
            break;
    }
}

2. ISR Best Practices

// ISR with minimal processing
void EXTI15_10_IRQHandler(void) {
    // Check which pins triggered
    uint16_t pending = EXTI->PR & 0xFC00; // Pins 10-15
    
    if (pending) {
        // Clear pending bits
        EXTI->PR = pending;
        
        // Set flag for main loop processing
        for (int i = 10; i <= 15; i++) {
            if (pending & (1 << i)) {
                set_interrupt_flag(i);
            }
        }
    }
}

// Main loop processes interrupt flags
void main_loop(void) {
    while (1) {
        // Check for pending interrupts
        for (int i = 0; i < MAX_INTERRUPTS; i++) {
            if (check_interrupt_flag(i)) {
                clear_interrupt_flag(i);
                process_interrupt_event(i);
            }
        }
        
        // Other main loop tasks
        process_system_tasks();
    }
}

⏱️ Interrupt Latency

1. Latency Components

// Interrupt latency analysis
typedef struct {
    uint32_t hardware_latency;    // Time to enter ISR
    uint32_t software_latency;    // Time in ISR
    uint32_t context_switch;      // Time to save/restore context
    uint32_t total_latency;       // Total response time
} interrupt_latency_t;

// Measure interrupt latency
void measure_interrupt_latency(void) {
    uint32_t start_time, end_time;
    
    // Configure test interrupt
    configure_test_interrupt();
    
    // Start measurement
    start_time = get_high_resolution_timer();
    
    // Trigger interrupt
    trigger_test_interrupt();
    
    // Measure in ISR
    end_time = get_high_resolution_timer();
    
    // Calculate latency
    uint32_t latency = end_time - start_time;
    
    // Report results
    printf("Interrupt latency: %lu cycles\n", latency);
}

2. Latency Optimization

// Optimized ISR for minimal latency
__attribute__((interrupt)) void optimized_isr(void) {
    // Use registers directly (no function calls)
    // Minimize stack usage
    // Avoid complex operations
    
    // Quick state check
    if (GPIOA->IDR & (1 << 0)) {
        // Set flag immediately
        interrupt_flags |= (1 << 0);
    }
    
    // Clear interrupt
    EXTI->PR = (1 << 0);
}

🎯 Best Practices

1. ISR Design Guidelines

// ISR design checklist
/*
    β–‘ Keep ISRs short and fast
    β–‘ Avoid function calls when possible
    β–‘ Use volatile for shared variables
    β–‘ Clear interrupt flags early
    β–‘ Don't use blocking operations
    β–‘ Avoid floating-point operations
    β–‘ Use appropriate interrupt priorities
    β–‘ Test interrupt timing
    β–‘ Handle interrupt nesting properly
    β–‘ Document interrupt dependencies
*/

// Good ISR example
volatile uint32_t interrupt_counter = 0;

void good_isr_example(void) {
    // Clear interrupt immediately
    EXTI->PR = (1 << 0);
    
    // Simple operation
    interrupt_counter++;
    
    // Set flag for main loop
    interrupt_pending = true;
}

2. Interrupt Priority Management

// Configure interrupt priorities
void configure_interrupt_priorities(void) {
    // Set priority grouping
    NVIC_SetPriorityGrouping(3); // 4 bits for preemption, 0 for sub
    
    // Configure priorities (lower number = higher priority)
    NVIC_SetPriority(EXTI0_IRQn, 1);      // High priority
    NVIC_SetPriority(EXTI1_IRQn, 2);      // Medium priority
    NVIC_SetPriority(EXTI2_IRQn, 3);      // Low priority
    
    // Enable interrupts
    NVIC_EnableIRQ(EXTI0_IRQn);
    NVIC_EnableIRQ(EXTI1_IRQn);
    NVIC_EnableIRQ(EXTI2_IRQn);
}

⚠️ Common Pitfalls

1. Missing Interrupt Clearing

// WRONG: Missing interrupt clear
void bad_isr_example(void) {
    // Process interrupt
    process_interrupt();
    // Missing: EXTI->PR = (1 << pin);
}

// CORRECT: Clear interrupt flag
void good_isr_example(void) {
    // Clear interrupt flag first
    EXTI->PR = (1 << 0);
    
    // Process interrupt
    process_interrupt();
}

2. Long ISR Execution

// WRONG: Long operations in ISR
void bad_long_isr(void) {
    EXTI->PR = (1 << 0);
    
    // Don't do this in ISR!
    for (int i = 0; i < 1000; i++) {
        process_data();
    }
}

// CORRECT: Set flag and return
void good_short_isr(void) {
    EXTI->PR = (1 << 0);
    
    // Set flag for main loop
    data_processing_pending = true;
}

3. Race Conditions

// WRONG: Race condition with shared variable
volatile bool button_pressed = false;

void isr_with_race(void) {
    EXTI->PR = (1 << 0);
    button_pressed = true; // Race condition possible
}

// CORRECT: Atomic operation
volatile uint32_t button_flags = 0;

void isr_without_race(void) {
    EXTI->PR = (1 << 0);
    __atomic_or_fetch(&button_flags, 1, __ATOMIC_RELEASE);
}

πŸ’‘ Examples

1. Button Interrupt with Debouncing

// Button interrupt implementation
#define BUTTON_PIN     0
#define DEBOUNCE_MS    50

volatile bool button_pressed = false;
volatile uint32_t last_button_time = 0;

void EXTI0_IRQHandler(void) {
    if (EXTI->PR & (1 << BUTTON_PIN)) {
        EXTI->PR = (1 << BUTTON_PIN);
        
        uint32_t current_time = get_system_tick();
        
        // Software debouncing
        if (current_time - last_button_time > DEBOUNCE_MS) {
            button_pressed = true;
            last_button_time = current_time;
        }
    }
}

// Main loop processes button press
void main_loop(void) {
    while (1) {
        if (button_pressed) {
            button_pressed = false;
            handle_button_action();
        }
        
        // Other tasks
        process_system_tasks();
    }
}

2. Sensor Interrupt with Edge Detection

// Sensor interrupt with edge detection
#define SENSOR_PIN     1
#define SENSOR_RISING  1
#define SENSOR_FALLING 0

volatile uint32_t sensor_rising_count = 0;
volatile uint32_t sensor_falling_count = 0;

void EXTI1_IRQHandler(void) {
    if (EXTI->PR & (1 << SENSOR_PIN)) {
        EXTI->PR = (1 << SENSOR_PIN);
        
        // Read current pin state
        bool current_state = (GPIOA->IDR & (1 << SENSOR_PIN)) ? 1 : 0;
        
        if (current_state == SENSOR_RISING) {
            sensor_rising_count++;
        } else {
            sensor_falling_count++;
        }
    }
}

3. Multiple Interrupt Sources

// Multiple interrupt sources with priority
#define INT_PIN_1      0
#define INT_PIN_2      1
#define INT_PIN_3      2

volatile uint32_t interrupt_flags = 0;

void EXTI0_IRQHandler(void) {
    if (EXTI->PR & (1 << INT_PIN_1)) {
        EXTI->PR = (1 << INT_PIN_1);
        interrupt_flags |= (1 << INT_PIN_1);
    }
}

void EXTI1_IRQHandler(void) {
    if (EXTI->PR & (1 << INT_PIN_2)) {
        EXTI->PR = (1 << INT_PIN_2);
        interrupt_flags |= (1 << INT_PIN_2);
    }
}

void EXTI2_IRQHandler(void) {
    if (EXTI->PR & (1 << INT_PIN_3)) {
        EXTI->PR = (1 << INT_PIN_3);
        interrupt_flags |= (1 << INT_PIN_3);
    }
}

// Process interrupts in priority order
void process_interrupts(void) {
    if (interrupt_flags & (1 << INT_PIN_1)) {
        interrupt_flags &= ~(1 << INT_PIN_1);
        process_high_priority_interrupt();
    }
    
    if (interrupt_flags & (1 << INT_PIN_2)) {
        interrupt_flags &= ~(1 << INT_PIN_2);
        process_medium_priority_interrupt();
    }
    
    if (interrupt_flags & (1 << INT_PIN_3)) {
        interrupt_flags &= ~(1 << INT_PIN_3);
        process_low_priority_interrupt();
    }
}

πŸ§ͺ Guided Labs

Lab 1: Basic External Interrupt Implementation

  1. Setup: Configure GPIO pin for external interrupt with edge detection
  2. Test: Connect a button and verify interrupt triggering on press/release
  3. Measure: Use oscilloscope to measure interrupt latency and response time
  4. Optimize: Implement debouncing and measure its effect on reliability

Lab 2: Interrupt Priority and Nesting

  1. Configure: Set up multiple interrupt sources with different priorities
  2. Test: Trigger interrupts simultaneously and observe priority handling
  3. Analyze: Measure interrupt nesting behavior and context switching overhead
  4. Validate: Verify that higher priority interrupts can preempt lower ones

Lab 3: Advanced Interrupt Techniques

  1. Implement: Level-triggered interrupts with proper source clearing
  2. Design: Interrupt-driven state machine for complex event handling
  3. Optimize: Minimize ISR execution time and measure performance impact
  4. Debug: Use logic analyzer to trace interrupt timing and identify bottlenecks

βœ… Check Yourself

Understanding Check

Application Check

Analysis Check

🎯 Interview Questions

Basic Questions

  1. What is the difference between edge-triggered and level-triggered interrupts?
    • Edge-triggered: Triggered on signal transition (0β†’1 or 1β†’0)
    • Level-triggered: Triggered when signal remains at specific level
    • Edge-triggered is more common for external interrupts
  2. How do you implement debouncing for a mechanical switch?
    • Software: Timer-based delay, state machine
    • Hardware: RC filter, Schmitt trigger, bounce suppression IC
    • Best approach depends on requirements and constraints
  3. What is interrupt latency and how do you minimize it?
    • Time from interrupt occurrence to handler execution
    • Minimize by: Short ISRs, proper priorities, avoid function calls

Advanced Questions

  1. How do you handle multiple interrupt sources with different priorities?
    • Configure NVIC priorities
    • Use interrupt nesting if supported
    • Process in priority order in main loop
  2. What are the common pitfalls when implementing external interrupts?
    • Missing interrupt flag clearing
    • Long ISR execution time
    • Race conditions with shared variables
    • Improper debouncing
  3. How do you measure and optimize interrupt latency?
    • Use high-resolution timer
    • Profile ISR execution time
    • Optimize for minimal processing in ISR

Practical Questions

  1. Design an interrupt-driven button interface with debouncing.
    // Configure button interrupt
    void configure_button_interrupt(void) {
        // GPIO as input with pull-up
        GPIO_InitTypeDef gpio_init = {0};
        gpio_init.Pin = BUTTON_PIN;
        gpio_init.Mode = GPIO_MODE_IT_FALLING;
        gpio_init.Pull = GPIO_PULLUP;
        HAL_GPIO_Init(BUTTON_PORT, &gpio_init);
           
        // Enable interrupt
        HAL_NVIC_SetPriority(BUTTON_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(BUTTON_IRQn);
    }
    
  2. Implement a sensor interrupt that counts rising edges.
    volatile uint32_t edge_count = 0;
       
    void sensor_isr(void) {
        if (EXTI->PR & (1 << SENSOR_PIN)) {
            EXTI->PR = (1 << SENSOR_PIN);
            edge_count++;
        }
    }
    

πŸ§ͺ Guided Labs

1) Debouncing comparison

2) Edge vs level triggering

βœ… Check Yourself



πŸ“š Resources

Books

Online Resources


Next Topic: Watchdog Timers β†’ Interrupts and Exceptions