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

ESP32修改UART回显代码后频繁重启,请求故障排查

ESP32 UART回显添加前缀计数器后spinlock断言失败问题排查

问题描述

ESP32新手修改官方UART回显示例,添加自定义前缀与计数器功能后,设备持续重启,报错为spinlock_acquire断言失败,需排查问题原因并实现稳定的UART回显功能。

修改后的代码

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"

#define ECHO_UART_PORT_NUM      UART_NUM_0
#define ECHO_TEST_TXD 1
#define ECHO_TEST_RXD 3

#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)

#define ECHO_UART_BAUD_RATE     115200
#define ECHO_TASK_STACK_SIZE    1024

static const char *TAG = "UART TEST";

#define BUF_SIZE (1024)

static void echo_task(void *arg)
{
    int ctr=0;

    uart_config_t uart_config = {
        .baud_rate = ECHO_UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_REF_TICK,
    };
    int intr_alloc_flags = 0;

#if CONFIG_UART_ISR_IN_IRAM 
    intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif

    ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
    ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));

    char *data = (char *) malloc(BUF_SIZE);
    char *data1 = (char *)malloc(2048);
    int len ;
    uart_flush_input(ECHO_UART_PORT_NUM);
    
    while (1) {
        memset(data,'\0',BUF_SIZE);
        len=0;
        len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        
        if (len >= 1){
            memset(data1,'\0',2048);
            sprintf(data1," input from ESP1  %d %s",ctr++, data);
            uart_write_bytes(ECHO_UART_PORT_NUM, (const char *)data1, strlen(data1));
        }
    }
}

void app_main(void)
{
    xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}

错误栈信息(翻译后)

断言失败: spinlock_acquire spinlock.h:135 (result == core_id || result == SPINLOCK_FREE)
0x40081aea: panic_abort at /home/balaji/esp/esp-idf/components/esp_system/panic.c:409
0x40085ec1: esp_system_abort at /home/balaji/esp/esp-idf/components/esp_system/esp_system.c:135
0x4008af01: __assert_func at /home/balaji/esp/esp-idf/components/newlib/assert.c:85
0x40088a8f: spinlock_acquire at /home/balaji/esp/esp-idf/components/esp_hw_support/include/spinlock.h:135
0x40086c09: vPortEnterCritical at /home/balaji/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:600
0x400d6d04: uart_tx_all at /home/balaji/esp/esp-idf/components/driver/uart.c:1185
0x400d759d: uart_write_bytes at /home/balaji/esp/esp-idf/components/driver/uart.c:1221 (判别器2)
0x400d5b5a: echo_task at /home/balaji/esp/workspace/UART_PROJECT/ESP-1/main/ESP-1.c:86
0x400888cd: vPortTaskWrapper at /home/balaji/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:141
禁用RNG早期熵源...
0x400810e8: call_start_cpu1 at /home/balaji/esp/esp-idf/components/esp_system/port/cpu_start.c:152

问题原因分析

  1. 任务栈空间不足:当前设置的ECHO_TASK_STACK_SIZE为1024字节,远不足以支撑echo_task中的sprintf格式化、UART读写调用及FreeRTOS任务本身的栈开销。栈溢出会破坏FreeRTOS内部临界区的自旋锁状态,触发断言失败。
  2. UART0资源竞争(潜在风险):UART0默认用于ESP-IDF系统日志输出,用户任务直接操作UART0时,可能与系统日志输出产生资源竞争,加剧稳定性问题。
  3. 内存管理不规范:动态分配的data和data1未在任务退出时释放,存在内存泄漏隐患。

修复方案

  1. 增大任务栈大小:将ECHO_TASK_STACK_SIZE调整为4096字节,确保任务有足够栈空间处理字符串格式化和UART操作。
  2. 改用非系统日志UART端口:使用UART1或UART2替代UART0作为回显端口,避免与系统日志输出冲突。
  3. 优化内存分配:将动态缓冲区改为栈上固定数组,减少堆内存操作开销;同时用snprintf替代sprintf,避免缓冲区溢出。
  4. 添加延时降低CPU占用:在循环中加入短暂延时,减少任务对CPU的持续占用。

验证后的修复代码

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"

#define ECHO_UART_PORT_NUM      UART_NUM_1  // 改用UART1避免系统日志冲突
#define ECHO_TEST_TXD 17
#define ECHO_TEST_RXD 16

#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)

#define ECHO_UART_BAUD_RATE     115200
#define ECHO_TASK_STACK_SIZE    4096  // 增大栈空间

static const char *TAG = "UART TEST";

#define BUF_SIZE (1024)
#define OUTPUT_BUF_SIZE (2048)

static void echo_task(void *arg)
{
    int ctr=0;

    uart_config_t uart_config = {
        .baud_rate = ECHO_UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_REF_TICK,
    };
    int intr_alloc_flags = 0;

#if CONFIG_UART_ISR_IN_IRAM 
    intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif

    ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
    ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));

    // 使用栈上数组替代动态malloc
    char data[BUF_SIZE] = {0};
    char data1[OUTPUT_BUF_SIZE] = {0};
    int len ;
    uart_flush_input(ECHO_UART_PORT_NUM);
    
    while (1) {
        memset(data,'\0',BUF_SIZE);
        len=0;
        // 用pdMS_TO_TICKS统一超时时间格式
        len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), pdMS_TO_TICKS(20));
        
        if (len >= 1){
            memset(data1,'\0',OUTPUT_BUF_SIZE);
            // 使用snprintf避免缓冲区溢出
            snprintf(data1, OUTPUT_BUF_SIZE,"input from ESP1  %d %s",ctr++, data);
            uart_write_bytes(ECHO_UART_PORT_NUM, (const char *)data1, strlen(data1));
        }
        // 短暂延时降低CPU占用
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

void app_main(void)
{
    xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 02:05:20