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
问题原因分析
- 任务栈空间不足:当前设置的
ECHO_TASK_STACK_SIZE为1024字节,远不足以支撑echo_task中的sprintf格式化、UART读写调用及FreeRTOS任务本身的栈开销。栈溢出会破坏FreeRTOS内部临界区的自旋锁状态,触发断言失败。 - UART0资源竞争(潜在风险):UART0默认用于ESP-IDF系统日志输出,用户任务直接操作UART0时,可能与系统日志输出产生资源竞争,加剧稳定性问题。
- 内存管理不规范:动态分配的
data和data1未在任务退出时释放,存在内存泄漏隐患。
修复方案
- 增大任务栈大小:将
ECHO_TASK_STACK_SIZE调整为4096字节,确保任务有足够栈空间处理字符串格式化和UART操作。 - 改用非系统日志UART端口:使用UART1或UART2替代UART0作为回显端口,避免与系统日志输出冲突。
- 优化内存分配:将动态缓冲区改为栈上固定数组,减少堆内存操作开销;同时用
snprintf替代sprintf,避免缓冲区溢出。 - 添加延时降低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
相关产品推荐
相关产品推荐

