yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
ESP32 Core Board📖
1. ESP32 Introduction
  • 1. ESP32S3 Overview
  • 2. Environment Setup Introduction
  • 3. Dev Board Introduction
2. ESP-IDF Development Tutorial
1. Before Development
  • 1. Environment Setup (Windows)
  • 2. Environment Setup (Linux)
  • 3. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. DMA
  • 8. ADC
  • 9. PWM Breathing LED
  • 10. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. PSRAM External Memory Experiment
  • 5. Flash Read and Write Experiment
  • 6. USB Emulated Mass Storage Experiment
4. FreeRTOS operating system
  • 1.FreeRTOS Core Concepts Theory
  • 2.Task Creation and Management
  • 3.Queue
  • 4.Binary Semaphore
  • 5.Mutex
  • 6.Event Groups
  • 7.Software Timer
  • 8.Task Scheduler
  • 9.Low Power Mode
  • 10.Deadlock
  • 11.Priority Inversion
5. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. Socket Programming Basics
  • 9. HTTP Client Experiment
  • 10. HTTP Server Experiment
  • 11. MQTT IoT Communication Protocol
  • 12. WIFI OTA Upgrade
  • 13. Bluetooth Fundamentals
  • 14. Bluetooth Slave Mode
  • 15. Bluetooth Master Mode
6. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
7. Xiaozhi AI Voice
  • 1. Burn Xiaozhi Firmware
  • 2. Network and Configuration
  • 3. AI Agent Settings
  • 4. Program Analysis
  • 5. Free Chat
3. Arduino Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. ADC
  • 8. PWM Breathing LED
  • 9. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication
  • 2. I2C OLED Display
  • 3. SPI Bus Communication
  • 4. Flash Read and Write
  • 5. USB Emulated Mass Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Server Experiment
  • 5. UDP Client Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT IoT Communication Protocol
  • 11. WIFI OTA Upgrade
  • 12. Bluetooth Fundamentals
  • 13. Bluetooth Slave Mode
  • 14. Bluetooth Master Mode
5. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
4. MicroPython Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
  • 3. Thonny IDE
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. TIMER_IT
  • 5. Watchdog Timer
  • 6. ADC
  • 7. PWM Breathing LED
  • 8. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. Flash Read and Write Experiment
  • 5. Print Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT
  • 11. Bluetooth Fundamentals
  • 12. Bluetooth Slave Mode
  • 13. Bluetooth Master Mode
5. Audio Applications
  • 1. I2S Audio Playback Experiment
  • 2. Repeater Experiment
Download
  • Code
  • Hardware_Info
  • 3D_Model_File
  • Software_Tools
  • VMware
  • VM_File
Download ZIP

Welcome to ESP32 Core Board repository

3. Peripheral Interrupts

Peripheral Interrupts

Peripheral Interrupts


Peripheral Interrupts1. External Interrupt Overview2. Core Concepts2.1 Interrupt Trigger Types2.2 IRAM_ATTR — Why ISR Must Be in RAM2.3 volatile — Compiler Optimization Barrier2.4 ISR Design Principles3. Project Architecture and Flow4. Hardware Dependencies5. Source Code Analysis5.1 Macro Definitions and Global Variables5.2 exti_isr_cb() — Interrupt Callback (Core)5.3 led_init() — LED Initialization5.4 exti_init() — Interrupt Initialization (Three Steps)5.5 app_main() — Main Entry6. Operation Flow6.1 Build and Flash6.2 Normal Operation6.3 Verification Testing7. Common Issues


1. External Interrupt Overview

External Interrupt (EXTI) is a hardware mechanism where MCUs respond to external asynchronous events. When a GPIO pin detects a specified edge (rising/falling/any edge), hardware automatically pauses current program execution, jumps to a preset Interrupt Service Routine (ISR) to handle the event, and returns to continue original program execution after handling.

This experiment uses GPIO0 falling-edge interrupt to detect button presses, increments trigger counter in ISR, and main loop detects counter changes to toggle LED (GPIO44).

Comparison with Polling Approach:

FeatureExternal Interrupt (this chapter)Polling (2. Basic Peripherals → 2. Button Detection)
Detection MechanismHardware auto-triggers ISRSoftware loop reads GPIO
CPU EfficiencyHigh (can sleep when idle)Low (continuous polling)
Response LatencyMicrosecond levelDepends on polling period (~20ms)
Coding ComplexityNeed to understand ISR constraintsSimple and intuitive
Applicable ScenariosLow power, real-time responseSimple applications, no power requirements

Typical Application Scenarios:

Application TypeExample
Edge TriggeringEmergency stop button, rotary speed sensor, PIR motion detection
Level TriggeringPower failure detection, overcurrent protection signal
ISR + Main LoopTouch IC status pin, encoder Z-phase home signal

2. Core Concepts

2.1 Interrupt Trigger Types

ESP32-S3 GPIO supports 5 interrupt trigger types:

typedef enum {
    GPIO_INTR_DISABLE  = 0,  // Disable interrupt
    GPIO_INTR_POSEDGE  = 1,  // Rising edge trigger (0→1)
    GPIO_INTR_NEGEDGE  = 2,  // Falling edge trigger (1→0)
    GPIO_INTR_ANYEDGE  = 3,  // Any edge trigger (0→1 or 1→0)
    GPIO_INTR_LOW_LEVEL = 4, // Low level trigger
    GPIO_INTR_HIGH_LEVEL = 5,// High level trigger
} gpio_int_type_t;
Trigger TypeTrigger ConditionThis Experiment
NEGEDGE (Falling)Level transitions from 1→0✅ Button press moment (pulled high → conducting low)
POSEDGE (Rising)Level transitions from 0→1Can be used for release detection
ANYEDGE (Any edge)0→1 or 1→0Need to determine direction by current level

Reason for selecting GPIO_INTR_NEGEDGE in this experiment: GPIO0 is configured with internal pull-up (default high), when button is pressed, level transitions from 1 to 0, forming a falling edge.

2.2 IRAM_ATTR — Why ISR Must Be in RAM

xxxxxxxxxx
static void IRAM_ATTR exti_isr_cb(void *arg)

IRAM_ATTR places function code in ESP32-S3's IRAM (internal SRAM) instead of Flash. Reason:

​x
Normal function call flow:
  CPU → Cache → Flash (SPI) → return instruction

Risk when ISR triggers:
  If Flash Cache miss and SPI bus is occupied
  → Need to wait for Flash read to complete
  → Interrupt response delay uncertain (possibly hundreds of µs)
  → Worst case: Cache miss + interrupt nesting → deadlock

IRAM_ATTR solution:
  CPU → IRAM (SRAM) → immediately get instruction
  → Zero-wait response (deterministic delay)

Core Principle: ISR functions (and all sub-functions they call) must be marked with IRAM_ATTR, or linked to IRAM segment at compile time. ISR-related APIs in driver/gpio.h already have IRAM placement built-in.

2.3 volatile — Compiler Optimization Barrier

xxxxxxxxxx
static volatile uint64_t intr_count = 0;   // Modified by ISR, read by main loop
static volatile uint32_t led_state  = 0;   // Modified by ISR, read by main loop
Without volatileWith volatile
Compiler caches intr_count in registerEvery access forces read from memory
while(last_count != intr_count) always reads old register value → may infinite loopEach read gets latest value in memory
ISR modifications "invisible" to main loopISR modifications take effect immediately

volatile does not guarantee atomicity. For 32-bit variables on 32-bit processors, reads are atomic (single instruction), but 64-bit variable reads require two instructions. In this experiment, ISR only writes, main loop only reads, and no strict timing consistency is required, so no data race occurs.

2.4 ISR Design Principles

xxxxxxxxxx
Things ISR can do:
  ✅ Increment counter
  ✅ Set flag
  ✅ Read/write volatile variables
  ✅ Call FreeRTOS API versions xxxFromISR()

Things ISR cannot do:
  ❌ printf() / ESP_LOGI() (blocking UART output)
  ❌ vTaskDelay() (ISR is not a task, cannot delay)
  ❌ Acquire mutex/semaphore (may deadlock)
  ❌ Heavy computation (extends interrupt response time)
  ❌ Operate Flash / NVS (SPI bus may be occupied)

3. Project Architecture and Flow

xxxxxxxxxx
app_main()
  ├─ led_init()         ← Configure GPIO44 output, initially off
  │
  ├─ exti_init()        ← Three-step interrupt configuration
  │   ├─ gpio_config(.intr_type=NEGEDGE)  GPIO0 input+pull-up+falling edge
  │   ├─ gpio_install_isr_service(0)      Install ISR unified dispatch service
  │   └─ gpio_isr_handler_add()           Bind exti_isr_cb to GPIO0
  │
  └─ while(1) Main Loop
      ├─ vTaskDelay(100ms)
      └─ last_count != intr_count?  →  New interrupt
           ├─ gpio_set_level()      →  Update LED
           ├─ led_state = !led_state
           └─ last_count = intr_count

Interrupt trigger chain:
  GPIO0 falling edge (button press)
    → GPIO hardware detection → generate interrupt signal
      → CPU save context → jump
        → gpio_isr_service() (IRAM)  Unified dispatch
          → exti_isr_cb() (IRAM)     User callback
            → intr_count++           Increment counter
          ← return to dispatcher
      ← CPU restore context
    ← continue executing interrupted code

4. Hardware Dependencies

Hardware Connection:

PinPeripheralConfigurationDescription
GPIO0BOOT ButtonInput + internal pull-up + falling edge interruptPressed=low, interrupt triggers
GPIO44LEDPush-pull outputInitially on, toggles after ISR triggers

Dependencies: freertos, driver/gpio, esp_log


5. Source Code Analysis

5.1 Macro Definitions and Global Variables

Use #define to give pins readable names, then define two volatile global variables for ISR and main loop to share data.

xxxxxxxxxx
#define TAG              "EXTI_IRQ"       // Log tag
#define EXTI_TRIGGER_PIN  GPIO_NUM_0      // Interrupt pin: GPIO0
#define LED_PIN           GPIO_NUM_44     // LED pin: GPIO44

static volatile uint64_t intr_count = 0;  // Interrupt trigger count (volatile: modified in ISR)
static volatile uint32_t led_state  = 0;  // LED state (volatile: modified in ISR)

5.2 exti_isr_cb() — Interrupt Callback (Core)

This is the most core interrupt service function in the entire program—only one thing: increment counter by 1, nothing else.

xxxxxxxxxx
static void IRAM_ATTR exti_isr_cb(void *arg)
{
    intr_count++;   // Only increment count
}

This is the innermost function of the entire interrupt system, doing only one thing: increment counter. Why so simple?

  1. ISR should be as short as possible — 1 increment instruction < 1µs

  2. All complex logic handed to main loop — Decouple interrupt response from business processing

  3. Usage of arg parameter — The 3rd parameter of gpio_isr_handler_add() can pass a context pointer (NULL in this experiment), used to distinguish trigger sources when one ISR corresponds to multiple GPIOs

5.3 led_init() — LED Initialization

Fill a GPIO configuration struct, set LED pin to push-pull output, pull-up/down disabled, interrupt disabled, and after completion default to low to keep it on.

xxxxxxxxxx
static void led_init(void)
{
    gpio_config_t led_conf = {
        .pin_bit_mask = (1ULL << LED_PIN),
        .mode         = GPIO_MODE_OUTPUT,
        .pull_up_en   = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type    = GPIO_INTR_DISABLE,
    };
    gpio_config(&led_conf);
        gpio_set_level(LED_PIN, 0);      // Initially on
    led_state = 0;
}

5.4 exti_init() — Interrupt Initialization (Three Steps)

Interrupt initialization in three steps: first configure GPIO0 as input+pull-up+falling edge trigger, then install a global ISR dispatcher, finally attach our callback function to GPIO0.

xxxxxxxxxx
static void exti_init(void)
{
    // === Step 1: Configure GPIO0 as interrupt input pin ===
    gpio_config_t exti_conf = {
        .pin_bit_mask = (1ULL << EXTI_TRIGGER_PIN),
        .mode         = GPIO_MODE_INPUT,
        // Internal pull-up, default HIGH
        .pull_up_en   = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        // Falling edge trigger
        .intr_type    = GPIO_INTR_NEGEDGE,
    };
    gpio_config(&exti_conf);

    // === Step 2: Install GPIO ISR unified dispatch service ===
    gpio_install_isr_service(0);               // flags=0: default priority

    // === Step 3: Bind our callback to GPIO0 ===
    gpio_isr_handler_add(EXTI_TRIGGER_PIN, exti_isr_cb, NULL);
}

gpio_install_isr_service(0) should only be called once. It creates a unified ISR dispatcher, all GPIO interrupts are dispatched through this dispatcher to their respective callbacks. Multiple calls return ESP_ERR_INVALID_STATE. This experiment's exti_init() is also called only once, executed during initialization phase from app_main().

Meaning of flags parameter: 0 = use default priority. Optional ESP_INTR_FLAG_IRAM (ISR itself in IRAM), ESP_INTR_FLAG_LEVEL1/2/3, etc., for higher priority interrupt requirements.

5.5 app_main() — Main Entry

The main function called automatically by FreeRTOS after boot, does three things: initialize LED, initialize external interrupt, then in an infinite loop check interrupt count every 100ms, toggle LED if changed.