You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 GPIO中断执行NVS擦除触发lock_acquire_generic错误求助

问题描述

我尝试在GPIO中断服务程序(ISR)中执行NVS闪存擦除操作,为避免DI引脚高电平频繁触发中断,在中断处理里添加了3秒消抖逻辑,代码如下:

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t current_time = xTaskGetTickCountFromISR();
    // Debounce: Only process if enough time has passed since the last interrupt
    if ((current_time - last_interrupt_time) > (3000 / portTICK_PERIOD_MS))
    {
        // button_state = true;
        esp_err_t err = nvs_flash_erase();
    
        if (err == ESP_OK) {
            ESP_LOGI("NVS", "NVS flash erased successfully.");
        } else {
            ESP_LOGE("NVS", "Failed to erase NVS flash: %s", esp_err_to_name(err));
        }
        last_interrupt_time = current_time;
    }
}

运行后出现以下错误,已确认无堆损坏,求解决办法:

abort() was called at PC 0x40082edf on core 0
--- 0x40082edf: lock_acquire_generic at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/newlib/locks.c:133

Backtrace: 0x40081e0a:0x3ffb1b20 0x4008aa05:0x3ffb1b40 0x40092f55:0x3ffb1b60 0x40082edf:0x3ffb1bd0 0x4008301d:0x3ffb1c00 0x400830aa:0x3ffb1c20 0x4017e74e:0x3ffb1c50 0x40171c09:0x3ffb1f70 0x4018e435:0x3ffb1fa0 0x400918b9:0x3ffb1fd0 0x400831a9:0x3ffb2020 0x40083799:0x3ffb2040 0x4008386b:0x3ffb2070 0x40083545:0x3ffb20a0 0x40089723:0x3ffbbe70 0x400d47c3:0x3ffbbe90 0x4008c5d5:0x3ffbbeb0 0x4008b511:0x3ffbbed0
--- 0x40081e0a: panic_abort at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_system/panic.c:463
0x4008aa05: esp_system_abort at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_system/port/esp_system_chip.c:92
0x40092f55: abort at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/newlib/abort.c:38
0x40082edf: lock_acquire_generic at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/newlib/locks.c:133
0x4008301d: _lock_acquire_recursive at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/newlib/locks.c:162
0x400830aa: __retarget_lock_acquire_recursive at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/newlib/locks.c:321
0x4017e74e: _vfprintf_r at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp-elf/src/newlib/newlib/libc/stdio/vfprintf.c:846 (discriminator 2)
0x40171c09: vprintf at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp-elf/src/newlib/newlib/libc/stdio/vprintf.c:34
0x4018e435: esp_log_writev at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/log/log.c:210
0x400918b9: esp_log_write at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/log/log.c:220
0x400831a9: gpio_isr_handler at C:/Users/Admin/Desktop/gateway/main/wifi.c:168 (discriminator 1)
0x40083799: gpio_isr_loop at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_driver_gpio/src/gpio.c:466
0x4008386b: gpio_intr_service at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_driver_gpio/src/gpio.c:496
0x40083545: _xt_lowint1 at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/xtensa/xtensa_vectors.S:1240
0x40089723: xt_utils_wait_for_intr at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/xtensa/include/xt_utils.h:82
 (inlined by) esp_cpu_wait_for_intr at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_hw_support/cpu.c:55
0x400d47c3: esp_vApplicationIdleHook at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/esp_system/freertos_hooks.c:58
0x4008c5d5: prvIdleTask at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/freertos/FreeRTOS-Kernel/tasks.c:4344 (discriminator 1)
0x4008b511: vPortTaskWrapper at C:/Users/Admin/.espressif/frameworks/esp-idf-v5.3/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:134

ELF file SHA256: 1a4a2a846

Rebooting...
I (13) boot: ESP-IDF v5.3-dirty 2nd stage bootloader
I (13) boot: compile time Dec  5 2024 15:23:00
I (13) boot: Multicore bootloader
问题原因及解决办法

原因分析

从回溯信息可看出,崩溃发生在lock_acquire_generic,根源是在ISR中调用了非中断安全的函数:

  1. nvs_flash_erase():NVS操作涉及闪存访问、锁操作,这类操作无法在ISR上下文执行
  2. ESP_LOGI()/ESP_LOGE():标准日志函数会调用libc的vfprintf,内部用到递归锁,ISR中无法安全获取这类锁,直接触发abort

ISR的执行上下文有严格限制:只能调用带ISR后缀的FreeRTOS API,或明确标注为中断安全的ESP-IDF函数,且执行时间必须极短。

解决步骤

正确做法是用任务通知把NVS擦除操作从ISR转移到普通任务中执行:

1. 定义全局任务句柄和消抖变量

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static TaskHandle_t nvs_erase_task_handle = NULL;
static volatile uint32_t last_interrupt_time = 0;

2. 创建处理NVS擦除的后台任务

void nvs_erase_task(void *arg)
{
    while(1) {
        // 等待ISR发送的任务通知
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        // 执行NVS擦除操作
        esp_err_t err = nvs_flash_erase();
        if (err == ESP_OK) {
            ESP_LOGI("NVS", "NVS flash erased successfully.");
        } else {
            ESP_LOGE("NVS", "Failed to erase NVS flash: %s", esp_err_to_name(err));
        }
    }
}

// 在app_main中初始化任务
void app_main()
{
    // ... 其他初始化代码(GPIO、NVS等)
    
    xTaskCreate(nvs_erase_task, "nvs_erase_task", 4096, NULL, 5, &nvs_erase_task_handle);
}

3. 修改ISR,仅处理消抖并发送任务通知

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t current_time = xTaskGetTickCountFromISR();
    // 消抖判断
    if ((current_time - last_interrupt_time) > (3000 / portTICK_PERIOD_MS))
    {
        // 非阻塞方式发送任务通知给后台处理任务
        vTaskNotifyGiveFromISR(nvs_erase_task_handle, NULL);
        last_interrupt_time = current_time;
    }
}

额外注意事项

  • last_interrupt_time必须声明为volatile类型,防止编译器优化导致数值错误
  • ISR中只能调用xTaskGetTickCountFromISR()、vTaskNotifyGiveFromISR()这类明确支持ISR的API
  • NVS擦除操作耗时较长,放在后台任务执行不会阻塞中断响应,符合实时系统要求

内容的提问来源于stack exchange,提问作者mauli02k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:12:34