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

ESP32 UART仅在分步调试时接收数据问题求助

问题描述

ESP32的两个UART分别外接RS485收发器,硬件连接正常(含数据使能引脚),两个UART均能独立发送RS485数据。但UART1向UART2发送字符时,UART2无法正常接收:

  • 正常运行时控制台始终显示接收0字节,偶尔会一次性收到大量累积的历史数据
  • 仅在使用调试器分步执行uart_get_buffered_data_len函数时,才能正确获取缓冲区字符数并通过uart_read_bytes读取数据

UART配置与任务逻辑

两个UART各对应一个优先级相同的FreeRTOS任务,配置细节如下:

UART1配置

const int uart_num = UART_NUM_1;
uart_config_t uart_config = {
    .baud_rate = 19200,
    .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_DEFAULT,
};
    
// Set UART log level
esp_log_level_set(TAG, ESP_LOG_INFO);

ESP_LOGI(TAG, "Start RS485 application test and configure UART.");

// Install UART driver (we don't need an event queue here)
// In this example we don't even use a buffer for sending data.
ESP_ERROR_CHECK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0));

// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));

ESP_LOGI(TAG, "UART set pins, mode and install driver.");

// Set UART pins as per KConfig settings
ESP_ERROR_CHECK(uart_set_pin(uart_num, 17, 18, 8, UART_PIN_NO_CHANGE));

// Set RS485 half duplex mode
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));

// Set read timeout of UART TOUT feature
ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT));

UART2配置

与UART1完全一致,仅修改:

  • UART编号为UART_NUM_2
  • 引脚改为43(TX)、44(RX)、21(DE/RE)

收发逻辑

  • UART1任务:每秒发送一次数据
printf("SEND!\r\n");
uart_write_bytes(uart_num, "CIAO\r\n", 6);
  • UART2任务:每秒检查并读取缓冲区数据
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
printf("RECEIVED %d bytes\r\n", length);
if (length > 0)
{
    int len = uart_read_bytes(uart_num, Uart2RxBuf, length, (100 / portTICK_PERIOD_MS));
    if (len > 0)
    {
        uint16_t i = 0;
        while (len) {
            putchar(Uart2RxBuf[i]);
            len--;
            i++;
        }
    }
}

硬件已确认UART2的RX引脚TTL信号正常。


问题排查与解决方案

1. 调整RX超时参数ECHO_READ_TOUT

uart_set_rx_timeout设置的是UART硬件触发接收完成的超时阈值(单位:波特率周期)。如果ECHO_READ_TOUT过小,UART还未将完整数据帧移入缓冲区,任务就去检查缓冲区长度,导致读到0。

  • 计算合理值:19200波特率下,1个字节(8数据+1停止位)约占9个波特周期,建议设置超时为至少3个字节的周期,即ECHO_READ_TOUT = 27,或直接设为100(对应约5ms超时)。

2. 优化UART2接收逻辑,改用阻塞读取

当前轮询方式依赖固定1秒的检查间隔,容易错过刚写入缓冲区的数据。改为让任务阻塞等待数据,由UART驱动自动处理缓冲区:

uint8_t Uart2RxBuf[64];
// 阻塞等待1秒,有数据立即读取
int len = uart_read_bytes(UART_NUM_2, Uart2RxBuf, sizeof(Uart2RxBuf), pdMS_TO_TICKS(1000));
if (len > 0)
{
    printf("RECEIVED %d bytes\r\n", len);
    for(int i=0; i<len; i++){
        putchar(Uart2RxBuf[i]);
    }
    putchar('\n');
}

此方式无需主动调用uart_get_buffered_data_len,可靠性更高。

3. 确保RS485发送完成后切换至接收状态

虽然UART_MODE_RS485_HALF_DUPLEX会自动控制DE/RE引脚,但手动添加发送完成等待,避免发送时序影响接收:
在UART1发送代码后添加:

uart_wait_tx_done(uart_num, pdMS_TO_TICKS(10)); // 等待发送完成,超时10ms

4. 修正任务调度与缓存一致性问题

  • 两个同优先级任务的轮询间隔可能导致数据读取时机不对,改用阻塞读取后可解决此问题
  • 若坚持轮询方式,可在读取缓冲区长度前添加内存屏障,避免缓存未同步:
__asm__ __volatile__ ("" : : : "memory");
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:43:13