UART 串口通信详解

发布时间:2026/7/2 11:28:04
UART 串口通信详解 引言如果你做过嵌入式开发一定对串口不陌生——它既是调试时的救命稻草也是与传感器、GPS模块、蓝牙模块等外设通信最常用的接口。可以说掌握了UART就掌握了嵌入式设备最基本的语言。本章将深入讲解 ESP32-S3 的 UART 控制器原理、驱动配置方法并通过 printf 重定向和串口协议解析两个实战案例让你彻底吃透串口通信。适用读者已完成第3章学习对 ESP-IDF 基础编程有一定了解的开发者。一、UART 串口通信基础1.1 什么是 UARTUARTUniversal Asynchronous Receiver/Transmitter通用异步收发器是一种异步串行通信协议。它只需要两条数据线就能实现全双工通信TX发送发送数据RX接收接收数据通信双方通过事先约定的波特率Baud Rate来同步数据采样时机不需要额外的时钟线。1.2 数据帧格式UART 传输的基本单位是一帧数据通常包含起始位(1) 数据位(5~8) 校验位(可选) 停止位(1~2)以最常用的格式8N18数据位、无校验、1停止位为例传输一个字节0x55即二进制01010101的波形如下空闲(高) ────┐ │ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ └──┘ └─┘ └─┘ └─┘ └─────┘ └── 起始 0 1 0 1 0 1 0 1 停止注意UART 从低位开始发送数据因此0x550101_0101在线上先发最低位。参数常用值说明波特率9600 / 115200 / 921600每秒传输的比特数数据位8大多数场景使用 8 位校验位None / Even / Odd奇偶校验用于简单检错停止位1 / 2帧结束标志1.3 电平标准UART 通常指TTL 电平0V 为低电平3.3V 或 5V 为高电平。但电脑串口DB9使用的是RS-232 电平±12V两者不能直连——这就是为什么我们需要USB 转 TTL 模块如 CH340、CP2102。⚠️ 重要ESP32-S3 的 UART 引脚输出为3.3V TTL 电平连接 5V 设备时需要确认电平兼容性必要时加电平转换芯片。二、ESP32-S3 的 UART 控制器2.1 硬件资源ESP32-S3 内置了3 个独立的 UART 控制器UART0 ~ UART2主要特性特性参数控制器数量3 个最高波特率5 MbpsFIFO 深度128 字节收发各 128 字节硬件流控RTS / CTS 支持奇偶校验支持中断触发可配置 FIFO 阈值、超时等2.2 默认引脚分配控制器TXRX默认用途UART0GPIO43GPIO44默认控制台烧录/调试UART1——可自由映射到任意 GPIOUART2——可自由映射到任意 GPIOESP32-S3 的 UART0 默认连接到 USB 串口用于程序烧录和printf输出。UART1 和 UART2 没有默认引脚需要用户在代码中指定。三、UART 编程实战3.1 基本配置流程使用 ESP-IDF 配置 UART 的完整流程如下#includedriver/uart.h#includedriver/gpio.h#defineUART_PORTUART_NUM_1// 使用 UART1#defineUART_TX_GPIOGPIO_NUM_17// TX 引脚#defineUART_RX_GPIOGPIO_NUM_18// RX 引脚#defineUART_BAUD_RATE115200// 波特率#defineBUF_SIZE1024// 缓冲区大小voiduart_init(void){// 1. 配置 UART 参数uart_config_tuart_config{.baud_rateUART_BAUD_RATE,.data_bitsUART_DATA_8_BITS,// 8 位数据.parityUART_PARITY_DISABLE,// 无校验.stop_bitsUART_STOP_BITS_1,// 1 位停止位.flow_ctrlUART_HW_FLOWCTRL_DISABLE,// 无硬件流控.source_clkUART_SCLK_DEFAULT,// 默认时钟源};uart_param_config(UART_PORT,uart_config);// 2. 设置引脚ESP32-S3 支持任意引脚映射uart_set_pin(UART_PORT,UART_TX_GPIO,UART_RX_GPIO,UART_PIN_NO_CHANGE,UART_PIN_NO_CHANGE);// 3. 安装驱动分配 FIFO 缓冲区uart_driver_install(UART_PORT,BUF_SIZE,BUF_SIZE,0,NULL,0);}代码解析uart_param_config配置波特率、数据格式等协议参数uart_set_pin将 UART 控制器映射到具体的 GPIO 引脚uart_driver_install安装驱动并分配收发缓冲区3.2 收发数据安装驱动后可以通过标准 API 进行读写// 发送数据constchar*msgHello ESP32-S3!\r\n;uart_write_bytes(UART_PORT,msg,strlen(msg));// 接收数据阻塞式带超时uint8_trx_buf[256];intlenuart_read_bytes(UART_PORT,rx_buf,sizeof(rx_buf),pdMS_TO_TICKS(100));// 100ms 超时if(len0){printf(Received %d bytes: %.*s\n,len,len,rx_buf);}uart_read_bytes是阻塞调用建议在 FreeRTOS 任务中使用配合适当的超时时间以避免任务挂死。3.3 串口回环实验最简单的测试——将 TX 和 RX 短接或用串口助手发送验证 UART 通信#includestring.h#includefreertos/FreeRTOS.h#includefreertos/task.h#includedriver/uart.h#includedriver/gpio.h#defineECHO_UART_PORTUART_NUM_1#defineECHO_TX_GPIOGPIO_NUM_17#defineECHO_RX_GPIOGPIO_NUM_18#defineBUF_SIZE1024voidecho_task(void*arg){uint8_tdata[BUF_SIZE];while(1){// 等待接收数据intlenuart_read_bytes(ECHO_UART_PORT,data,BUF_SIZE,pdMS_TO_TICKS(100));if(len0){// 原样回发uart_write_bytes(ECHO_UART_PORT,(char*)data,len);}}}voidapp_main(void){// 配置 UART 参数uart_config_tuart_config{.baud_rate115200,.data_bitsUART_DATA_8_BITS,.parityUART_PARITY_DISABLE,.stop_bitsUART_STOP_BITS_1,.flow_ctrlUART_HW_FLOWCTRL_DISABLE,.source_clkUART_SCLK_DEFAULT,};uart_param_config(ECHO_UART_PORT,uart_config);uart_set_pin(ECHO_UART_PORT,ECHO_TX_GPIO,ECHO_RX_GPIO,UART_PIN_NO_CHANGE,UART_PIN_NO_CHANGE);uart_driver_install(ECHO_UART_PORT,BUF_SIZE,BUF_SIZE,0,NULL,0);// 创建回环任务xTaskCreate(echo_task,uart_echo,4096,NULL,10,NULL);}用 USB 转 TTL 模块连接 PC 和 ESP32-S3打开串口助手发送任意字符——ESP32-S3 会原样返回这就是串口通信最基础的验证方式。四、printf 重定向到 UART4.1 为什么要重定向 printfprintf是 C 语言最常用的格式化输出函数。在 ESP-IDF 中printf默认输出到 UART0通过esp_rom_printf但当我们使用其他 UART 接口时需要将其重定向到指定串口。4.2 实现方法ESP-IDF 提供了esp_timer和console组件但最直接的方式是自定义_write函数Newlib 底层接口#includedriver/uart.h// 指定 printf 输出的 UART 端口#definePRINTF_UARTUART_NUM_1// 重写 _write 函数int_write(intfile,char*data,intlen){uart_write_bytes(PRINTF_UART,data,len);returnlen;}只需添加上述函数所有printf调用就会自动路由到 UART1。这对于串口屏、蓝牙模块等需要输出格式化命令的场景非常方便。// 重定向后printf 直接输出到 UART1voidapp_main(void){// 先初始化 UART1// ...printf(System initialized!\n);printf(Free heap: %d bytes\n,esp_get_free_heap_size());printf(Chip model: %s\n,CONFIG_IDF_TARGET);}4.3 注意事项仅需重写一次_write是 Newlib 库的弱符号用户定义后自动覆盖默认实现线程安全uart_write_bytes内部已加锁可在多任务环境中安全使用缓冲区大小UART 发送是异步的大量数据时注意发送缓冲区是否溢出五、实战温湿度传感器数据采集接下来我们做一个综合实战——通过 UART 连接一个SHT30 温湿度传感器模块该模块通过 UART 接口输出数据学习如何解析串口协议。5.1 协议分析SHT30 的 UART 协议如下命令发送数据返回数据读取温湿度0x22 0x000xA5 0x5A 6 字节数据 校验和返回数据格式6 字节字节0: 温度高位 | 字节1: 温度低位 | 字节2: 温度校验 字节3: 湿度高位 | 字节4: 湿度低位 | 字节5: 湿度校验5.2 代码实现#includestdio.h#includestring.h#includefreertos/FreeRTOS.h#includefreertos/task.h#includedriver/uart.h#includedriver/gpio.h#defineSENSOR_UARTUART_NUM_2#defineSENSOR_TXGPIO_NUM_10// 接传感器的 TX#defineSENSOR_RXGPIO_NUM_9// 接传感器的 RX#defineBUF_SIZE256// CRC 校验staticuint8_tcrc8(constuint8_t*data,intlen){uint8_tcrc0xFF;for(inti0;ilen;i){crc^data[i];for(intj0;j8;j){if(crc0x80)crc(crc1)^0x31;elsecrc1;}}returncrc;}voidapp_main(void){// 初始化 UARTuart_config_tuart_config{.baud_rate9600,.data_bitsUART_DATA_8_BITS,.parityUART_PARITY_DISABLE,.stop_bitsUART_STOP_BITS_1,.flow_ctrlUART_HW_FLOWCTRL_DISABLE,.source_clkUART_SCLK_DEFAULT,};uart_param_config(SENSOR_UART,uart_config);uart_set_pin(SENSOR_UART,SENSOR_TX,SENSOR_RX,UART_PIN_NO_CHANGE,UART_PIN_NO_CHANGE);uart_driver_install(SENSOR_UART,BUF_SIZE,0,0,NULL,0);uint8_tcmd[]{0x22,0x00};// 读取命令uint8_trx_buf[32];while(1){// 发送读取命令uart_write_bytes(SENSOR_UART,(char*)cmd,sizeof(cmd));vTaskDelay(pdMS_TO_TICKS(50));// 等待传感器响应// 读取响应intlenuart_read_bytes(SENSOR_UART,rx_buf,8,pdMS_TO_TICKS(100));if(len6rx_buf[0]0xA5rx_buf[1]0x5A){// 校验温度if(crc8(rx_buf[2],2)rx_buf[4]){uint16_ttemp_raw(rx_buf[2]8)|rx_buf[3];floattemp-45.0f175.0f*temp_raw/65535.0f;printf(Temperature: %.2f°C\n,temp);}// 校验湿度if(crc8(rx_buf[5],2)rx_buf[7]){uint16_thum_raw(rx_buf[5]8)|rx_buf[6];floathum100.0f*hum_raw/65535.0f;printf(Humidity: %.2f%%\n,hum);}}else{printf(Sensor read failed (len%d)\n,len);}vTaskDelay(pdMS_TO_TICKS(2000));// 每 2 秒读取一次}}这个案例展示了串口协议解析的完整流程发送指令 → 读取响应 → 校验数据 → 解析值。这是与几乎所有串口外设通信的标准模板。六、常见问题与调试技巧6.1 乱码问题串口收到乱码的最常见原因问题解决方法波特率不匹配确认两端波特率完全一致不要大概相等电平不匹配3.3V 设备接 5V 信号可能烧坏反之可能无法识别接地不良TX/RX 和 GND 必须共地TX/RX 接反交叉连接A的TX → B的RXA的RX ← B的TX6.2 调试工具推荐逻辑分析仪最推荐的调试方式直接看波形波特率、格式一目了然推荐 Saleae Logic 或国产 24MHz 8通道版串口助手sscom、Putty、Arduino IDE 串口监视器ESP-IDF Monitoridf.py monitor自带色彩高亮和时间戳6.3 波特率上限ESP32-S3 的 UART 最高支持 5 Mbps但实际可用波特率受以下因素限制USB 转 TTL 模块普通 CH340 最高约 2 MbpsCP2102 约 1 MbpsFT232 可达 3 Mbps线长和干扰高速串口建议线长不超过 20cm使用屏蔽线双方时钟精度高波特率下时钟偏差容忍度更低总结本章我们从理论到实践系统学习了 ESP32-S3 的 UART 串口通信UART 协议基础异步串行通信的原理与数据帧格式ESP32-S3 UART 控制器3 个独立控制器、灵活的引脚映射驱动配置与收发标准 API 的使用方法和回环测试printf 重定向一行代码将格式化输出路由到任意串口实战SHT30 传感器完整的串口协议解析流程调试技巧乱码排查、工具推荐、波特率限制串口是嵌入式开发的万能接口无论是调试输出、日志记录还是与各类外设通信它都是你最值得熟练掌握的通信方式。下篇预告第5章I2C 通信与传感器驱动—— 相比 UARTI2C 用更少的引脚连接更多的设备。我们将学习 ESP32-S3 的 I2C 控制器并驱动 OLED 显示屏和多种传感器。本文基于 ESP-IDF v5.x 编写GPIO 引脚号请根据实际硬件连接调整。SHT30 实验中请确认传感器模块的工作电压为 3.3V。