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

ESP32运行6个并行FreeRTOS任务触发Core0 LoadProhibited报错

ESP32 FreeRTOS任务启动后LoadProhibited崩溃修复

问题表现

在ESP32上尝试启动6个无限循环的FreeRTOS任务后,设备立即触发未处理异常自动重启,崩溃日志如下:

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400868b4  PS      : 0x00060033  A0      : 0x80085442  A1      : 0x3ffb0b50
0x400868b4: xTaskIncrementTick at C:/Users/preet/esp/esp-idf/components/freertos/tasks.c:3157

A2      : 0x00000001  A3      : 0x80059301  A4      : 0x00000000  A5      : 0x00000132  
A6      : 0x00000003  A7      : 0x00060023  A8      : 0x00000000  A9      : 0x3ffb0b30
A10     : 0x3ffb26b8  A11     : 0x00000003  A12     : 0x00060b20  A13     : 0x00060b23
A14     : 0x3ffb6300  A15     : 0x3ffb53fc  SAR     : 0x00000016  EXCCAUSE: 0x0000001c
EXCVADDR: 0x80059309  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  


Backtrace:0x400868b1:0x3ffb0b500x4008543f:0x3ffb0b70 0x40085199:0x3ffb0b90 0x400826e9:0x3ffb0ba0 0x400e4b6b:0x3ffb6210 0x400d18ef:0x3ffb6230 0x40086462:0x3ffb6250 0x40087991:0x3ffb6270
0x400868b1: xTaskIncrementTick at C:/Users/preet/esp/esp-idf/components/freertos/tasks.c:3156
0x4008543f: xPortSysTickHandler at C:/Users/preet/esp/esp-idf/components/freertos/port/port_systick.c:167
0x40085199: _frxt_timer_int at C:/Users/preet/esp/esp-idf/components/freertos/port/xtensa/portasm.S:329
0x400826e9: _xt_lowint1 at C:/Users/preet/esp/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1111
0x400e4b6b: cpu_ll_waiti at C:/Users/preet/esp/esp-idf/components/hal/esp32/include/hal/cpu_ll.h:183
 (inlined by) esp_pm_impl_waiti at C:/Users/preet/esp/esp-idf/components/esp_pm/pm_impl.c:837
0x400d18ef: esp_vApplicationIdleHook at C:/Users/preet/esp/esp-idf/components/esp_system/freertos_hooks.c:63

0x40086462: prvIdleTask at C:/Users/preet/esp/esp-idf/components/freertos/tasks.c:3973 (discriminator 1)
0x40087991: vPortTaskWrapper at C:/Users/preet/esp/esp-idf/components/freertos/port/xtensa/port.c:131
ELF file SHA256: 142fe637d0302132
Rebooting...

崩溃点落在FreeRTOS内核的tick中断处理函数xTaskIncrementTick,属于典型的内存被破坏后内核调度器访问非法地址触发的panic。

根因定位

逐行排查业务代码后,确认有6个直接/间接导致崩溃的问题:

  • 核心触发点:任务函数签名不符合FreeRTOS规范。MAGNETIC_END_SENSOR任务入口没有定义void *pvParameters形参,FreeRTOS启动任务时会按约定向任务栈压入传入的参数,函数形参不匹配会导致栈帧错位,任务执行时直接篡改栈上的返回地址、内核数据结构,最终触发内存访问错误。
  • 任务栈分配过小。电机控制类任务仅分配512字节栈,ESP32上调用ESP_LOG*打印日志、执行GPIO操作至少需要1024字节以上栈空间,栈溢出会直接踩踏相邻内存的任务控制块、信号量等内核对象,破坏任务链表结构,和崩溃点落在调度器tick处理函数的现象完全吻合。
  • 任务删除逻辑存在野指针风险。任务自删除时传入全局任务句柄而非NULL,如果任务启动后快速执行到删除逻辑,xTaskCreate还没来得及把任务句柄写入全局变量,就会向vTaskDelete传入野指针,直接破坏内核任务链表。
  • 重复初始化GPIO。多个任务中反复对同一个继电器、传感器引脚执行方向配置、引脚选择操作,并发执行时会篡改GPIO寄存器配置,同时带来不必要的内存访问风险。
  • 轮询任务无延时占满CPU。传感器、按键轮询的无限循环中没有加vTaskDelay让出CPU,高优先级任务会抢占全部时间片,导致IDLE任务无法执行,系统无法回收已删除任务的内存,最终耗尽堆内存触发内存错误。
  • 无任务存在性校验。按键触发任务创建时没有判断对应任务是否已经在运行,重复触发会重复创建任务,快速耗尽堆内存。

修复方案

按以下步骤修改即可解决崩溃问题:

  1. 统一修正所有任务的函数签名,所有FreeRTOS任务入口必须遵循void 任务函数名(void *pvParameters)格式,即使不使用传入参数也必须保留该形参。
  2. 统一在app_main中完成所有GPIO的初始化配置,仅执行一次,禁止在各个业务任务中重复初始化引脚。
  3. 调整任务栈大小:所有涉及日志打印、外设操作的任务栈最小分配2048字节,禁止分配小于1024字节的任务栈。
  4. 修正任务删除逻辑:任务删除自身时直接传NULL给vTaskDelete;跨任务删除其他任务前必须校验句柄非空,删除完成后立即将全局句柄置为NULL,避免野指针。
  5. 所有轮询类无限循环任务必须增加至少10ms的vTaskDelay,主动让出CPU时间片,避免饿死IDLE任务。
  6. 创建任务前增加句柄校验,对应任务已经在运行(句柄非空)时禁止重复创建,避免堆内存泄漏。
  7. 信号量创建后增加有效性校验,确保信号量创建成功再启动业务逻辑。

关键修正代码示例

修正后的app_main初始化逻辑:

void app_main(void)
{
    // 一次性初始化所有GPIO
    gpio_pad_select_gpio(PUSH_BUTTON);
    gpio_pad_select_gpio(FORCE_STOP_BUTTON);
    gpio_pad_select_gpio(IR_PIN);
    gpio_pad_select_gpio(RELAY_1_PIN);
    gpio_pad_select_gpio(RELAY_2_PIN);
    gpio_pad_select_gpio(MAGNET_1_PIN);
    gpio_pad_select_gpio(MAGNET_2_PIN);

    gpio_set_direction(PUSH_BUTTON, GPIO_MODE_INPUT);
    gpio_set_direction(FORCE_STOP_BUTTON, GPIO_MODE_INPUT);
    gpio_set_direction(IR_PIN, GPIO_MODE_INPUT);
    gpio_set_direction(RELAY_1_PIN, GPIO_MODE_OUTPUT);
    gpio_set_direction(RELAY_2_PIN, GPIO_MODE_OUTPUT);
    gpio_set_direction(MAGNET_1_PIN, GPIO_MODE_INPUT);
    gpio_set_direction(MAGNET_2_PIN, GPIO_MODE_INPUT);

    // 创建信号量并校验
    I_F_F_O_SIGNAL = xSemaphoreCreateBinary();
    M_F_F_O_SIGNAL = xSemaphoreCreateBinary();
    M_F_R_O_SIGNAL = xSemaphoreCreateBinary();
    configASSERT(I_F_F_O_SIGNAL && M_F_F_O_SIGNAL && M_F_R_O_SIGNAL);

    // 初始化所有全局任务句柄
    FFO_xHandle = FRO_xHandle = MS_xHandle = NULL;
    MFS_xHandle = MRS_xHandle = IR_xHandle = MAGNET_xHandle = NULL;
    MOTOR_STATE = 0;

    // 启动按键检测任务
    xTaskCreate(&BUTTON_OPERATION, "BUTTON_OPERATION", 2048, NULL, tskIDLE_PRIORITY, NULL);
}

修正后的磁传感器任务示例:

// 补全符合规范的任务形参
void MAGNETIC_END_SENSOR(void * pvParameters)
{
    while (1)
    {
        if (gpio_get_level(MAGNET_1_PIN) == 1 || gpio_get_level(MAGNET_2_PIN) == 1){
            if (MOTOR_STATE == 0){
                xSemaphoreGive(M_F_F_O_SIGNAL);
                ESP_LOGW(TAG, "F Signal sent.");
            }
            else if (MOTOR_STATE == 1){
                xSemaphoreGive(M_F_R_O_SIGNAL);
                ESP_LOGW(TAG, "R Signal sent.");
            }
            ESP_LOGW(TAG, "Gate reached to the destination, Stopping the motor.");
            // 校验句柄避免重复创建任务
            if(MS_xHandle == NULL) {
                xTaskCreate(&MOTOR_STOP, "MOTOR_STOP", 2048, NULL, 1, &MS_xHandle);
            }
            MOTOR_STATE = !MOTOR_STATE;
            ESP_LOGW(TAG, "Motor stopped and Motor State changed.");
            MAGNET_xHandle = NULL;
            vTaskDelete(NULL); // 自删除传NULL,避免野指针
        }
        vTaskDelay(10 / portTICK_PERIOD_MS); // 加延时让出CPU
    }
}

其余任务按照相同规则修正签名、栈大小、删除逻辑、轮询延时即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:27:25