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
相关产品推荐
相关产品推荐

