Skip to content

Interrupts — ISR Basics

Interrupt cho phép hardware ngắt CPU để xử lý events quan trọng ngay lập tức.

Analogy: Chuông cửa

┌─────────────────────────────────────────────────────────────────┐
│                    DOORBELL ANALOGY                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   POLLING (Không có interrupt):                                 │
│   ──────────────────────────────                                │
│   Bạn đang nấu ăn → đi ra cửa kiểm tra → không có ai           │
│   Quay lại nấu → 5 phút sau → đi ra kiểm tra → không có ai     │
│   → Mất thời gian, có thể bỏ lỡ khách!                         │
│                                                                 │
│   INTERRUPT (Có interrupt):                                     │
│   ─────────────────────────                                     │
│   Bạn đang nấu ăn                                               │
│   🔔 DING DONG! (Chuông reo)                                    │
│   → Dừng nấu → Mở cửa → Xử lý khách → Quay lại nấu            │
│                                                                 │
│   CPU = Bạn                                                     │
│   Nấu ăn = Main program                                         │
│   Chuông = Interrupt signal                                     │
│   Mở cửa = ISR (Interrupt Service Routine)                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

How Interrupts Work

┌─────────────────────────────────────────────────────────────────┐
│                    INTERRUPT FLOW                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   main() running                                                │
│   │                                                             │
│   │  ←──── 🔔 Hardware event (button press, timer, UART)       │
│   │                                                             │
│   ├─────► CPU saves context (registers, PC)                    │
│   │                                                             │
│   │       ┌─────────────────────┐                               │
│   │       │ ISR (short & fast!) │                               │
│   │       │ • Read hardware     │                               │
│   │       │ • Set flag          │                               │
│   │       │ • Clear interrupt   │                               │
│   │       └─────────────────────┘                               │
│   │                                                             │
│   ├─────► CPU restores context                                  │
│   │                                                             │
│   ▼  main() continues from where it stopped                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

ISR Example

cpp
#include <cstdint>

// Shared flag - MUST be volatile!
volatile bool buttonPressed = false;
volatile uint32_t systemTicks = 0;

// Button interrupt handler
extern "C" void EXTI0_IRQHandler() {
    // 1. Check if our interrupt
    if (EXTI->PR & (1 << 0)) {
        
        // 2. Do minimal work
        buttonPressed = true;
        
        // 3. Clear interrupt flag
        EXTI->PR = (1 << 0);
    }
}

// System tick interrupt (every 1ms)
extern "C" void SysTick_Handler() {
    systemTicks++;
}

int main() {
    setupButton();
    setupSysTick();
    
    while (true) {
        // Main loop - process flags set by ISR
        if (buttonPressed) {
            buttonPressed = false;
            handleButtonPress();  // Heavy work here, not in ISR!
        }
        
        // Use tick count for timing
        static uint32_t lastTick = 0;
        if (systemTicks - lastTick >= 1000) {
            lastTick = systemTicks;
            blinkLED();
        }
    }
}

ISR Rules — Keep It Short!

⚠️ GOLDEN RULES

  1. Keep ISRs SHORT — Microseconds, not milliseconds!
  2. No blocking — No delays, no waiting for I/O
  3. Volatile variables — For data shared with main
  4. Clear interrupt flag — Or interrupt fires again!
  5. No heap allocation — No new, malloc, std::vector
cpp
// ❌ BAD ISR - Too long!
extern "C" void UART_IRQHandler() {
    char buffer[100];
    readString(buffer);        // Blocking!
    processString(buffer);     // Takes too long!
    sendResponse(buffer);      // More blocking!
}

// ✅ GOOD ISR - Just set flag
volatile bool dataReady = false;
volatile uint8_t rxByte;

extern "C" void UART_IRQHandler() {
    rxByte = UART->DR;         // Read byte
    dataReady = true;          // Set flag
    UART->SR &= ~UART_SR_RXNE; // Clear flag
}

Disabling Interrupts

Đôi khi cần disable interrupts để protect shared data:

cpp
// Critical section - no interrupts
inline void disableInterrupts() {
    __disable_irq();  // ARM Cortex-M intrinsic
}

inline void enableInterrupts() {
    __enable_irq();
}

// RAII pattern
class CriticalSection {
    uint32_t primask_;
public:
    CriticalSection() {
        primask_ = __get_PRIMASK();
        __disable_irq();
    }
    ~CriticalSection() {
        __set_PRIMASK(primask_);
    }
};

// Usage
void safeIncrement() {
    CriticalSection cs;
    sharedCounter++;  // Safe from interrupts
}

Common Interrupt Sources

SourceTypical Use
TimerPeriodic tasks, PWM, delays
UART/USARTSerial communication
GPIO/EXTIButton press, sensors
ADCAnalog conversion complete
DMAData transfer complete
I2C/SPICommunication events

Interrupt Priority

cpp
// Higher priority = handles first
// Lower number = higher priority (ARM Cortex-M)

// Set priority: UART is more important than Timer
NVIC_SetPriority(UART1_IRQn, 1);   // High priority
NVIC_SetPriority(TIM2_IRQn, 3);    // Lower priority

// Enable interrupts
NVIC_EnableIRQ(UART1_IRQn);
NVIC_EnableIRQ(TIM2_IRQn);
┌─────────────────────────────────────────────────────────────────┐
│                    NESTED INTERRUPTS                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   main()                                                        │
│   │                                                             │
│   │ ←── Timer interrupt (priority 3)                           │
│   │     │                                                       │
│   │     │ ←── UART interrupt (priority 1) ← Preempts Timer!    │
│   │     │     │                                                 │
│   │     │     └─ UART ISR finishes                             │
│   │     │                                                       │
│   │     └─── Timer ISR finishes                                │
│   │                                                             │
│   ▼ main() continues                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

📚 Tổng kết

AspectRule
ISR LengthAs short as possible
Shared VariablesMust be volatile
Heavy WorkDo in main(), not ISR
Interrupt FlagAlways clear it
Critical SectionsDisable interrupts briefly

🎯 Module Complete!

Bạn đã hoàn thành Module 8: Embedded Programming:

  • ✅ Memory Constraints
  • ✅ Bit Manipulation
  • ✅ volatile Keyword
  • ✅ Smart Pointers in Embedded
  • ✅ Interrupts (ISR)

Welcome to the world of embedded systems! 🔌