4、UART实战:使用STM32或Arduino实现UART回环测试、与PC串口助手通信、中断与DMA方式收发数据
UART(Universal Asynchronous Receiver/Transmitter)作为最经典的串行通信接口,在嵌入式系统中承担着调试信息输出、传感器数据采集、模块间通信等关键角色。本章将围绕STM32与Arduino两大主流平台,从回环测试、PC串口助手通信、中断收发到DMA收发,层层递进地构建一套完整的UART实战能力。通过本章学习,你将掌握UART的硬件连接要点、寄存器配置逻辑、中断与DMA的工程化应用方法,并能够独立排查通信故障。
4.1 UART回环测试:验证硬件与驱动的基础手段
核心原理:回环测试(Loopback Test)是将UART的TX引脚直接短接到RX引脚,使发送的数据立即被自身接收。这种测试方式能够在不依赖外部设备的情况下,快速验证MCU的UART外设是否正常工作、引脚配置是否正确、波特率设置是否准确。回环测试分为硬件回环(物理跳线)和软件回环(内部寄存器配置)两种方式,工程中通常先做硬件回环以排除PCB布线问题。
操作流程:
- 将MCU的TX引脚与RX引脚用杜邦线或跳线帽短接;
- 初始化UART外设,设置波特率(如115200)、数据位8、停止位1、无校验;
- 编写发送函数,向串口发送一组已知数据(如0x55、0xAA、字符串"UART Loopback Test");
- 编写接收函数,读取接收到的数据并与发送数据比对;
- 若数据一致,则说明UART收发通路正常;若不一致,则需检查引脚映射、时钟使能或电平转换电路。
void setup() {
Serial.begin(115200); // 初始化串口,波特率115200
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (Serial.available() > 0) { // 检查接收缓冲区
char c = Serial.read(); // 读取一个字节
Serial.write(c); // 将收到的字节原样发送回去(回环)
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // 翻转LED
}
}
案例拆解:某工程师在调试STM32F103板卡时,发现通过串口助手发送数据无任何响应。首先进行硬件回环测试:将PA9(TX)与PA10(RX)短接,编写简单的回环程序。结果发现发送的数据能够正确接收,说明MCU的UART外设正常。随后检查外部电路,发现USB-TTL转换芯片的TXD/RXD与MCU的TX/RX交叉连接错误,修正后通信恢复正常。此案例说明回环测试是定位UART故障的第一道关卡,能够有效区分MCU内部问题与外部电路问题。
| 测试类型 | 连接方式 | 验证目标 | 适用场景 |
|---|---|---|---|
| 硬件回环 | TX短接RX | UART外设、引脚映射、波特率 | 新板卡上电调试 |
| 软件回环 | 内部寄存器配置 | 驱动代码逻辑 | 驱动开发阶段 |
| 外部回环 | 通过USB-TTL转接 | 电平转换电路、连接线缆 | 整机联调 |
4.2 与PC串口助手通信:建立调试通道
核心原理:PC串口助手通过USB-TTL转换芯片(如CH340、CP2102、FT232)与MCU的UART接口相连。通信链路为:PC端串口助手 → USB总线 → USB-TTL芯片 → TTL电平信号 → MCU的RX引脚。MCU发送的数据则沿相反方向传输。关键参数包括波特率、数据位、停止位、校验位,两端必须完全一致才能正确解析数据。
通信配置要点:
- 波特率:常用9600、115200、460800等,需考虑双方时钟误差(一般不超过±2%);
- 数据格式:8N1(8数据位、无校验、1停止位)是最常用配置;
- 电平匹配:MCU的UART引脚为TTL电平(0~3.3V或0~5V),PC串口为RS-232电平(±12V),必须经过转换芯片;
- 共地:MCU与USB-TTL转换器必须共地,否则通信不稳定。
UART_HandleTypeDef huart2;
void MX_USART2_UART_Init(void) {
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK) {
Error_Handler();
}
}
实战场景:在电机控制项目中,工程师需要通过串口助手实时监控电机转速、电流等参数。采用中断方式接收PC下发的指令(如"SPEED 1500\r\n"),解析后调整PWM占空比;同时以10Hz频率主动向上位机发送状态帧。通过串口助手的数据波形显示功能,可以直观观察参数变化趋势。此场景要求中断接收具备良好的实时性,且发送缓冲区管理要避免数据覆盖。
4.3 中断方式收发数据:提升实时响应能力
核心原理:中断方式收发数据利用UART外设的RXNE(接收数据寄存器非空)和TXE(发送数据寄存器空)中断标志。当接收到一个字节时,硬件自动触发RXNE中断,CPU在中断服务函数中读取数据;当发送寄存器空闲时,触发TXE中断,CPU写入下一个待发送字节。这种方式避免了轮询方式中CPU的忙等待,提高了系统实时性。
中断配置流程:
- 使能UART全局中断和RXNE中断;
- 配置NVIC中断优先级;
- 编写中断服务函数,在函数中读取DR寄存器获取数据;
- 对于发送,可采用"发送缓冲区+发送完成中断"的方式,避免阻塞。
uint8_t rx_buffer[64];
uint8_t rx_index = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
rx_buffer[rx_index++] = rx_data;
if (rx_index >= 64) rx_index = 0;
HAL_UART_Receive_IT(&huart2, &rx_data, 1); // 重新使能接收
}
}
// 主函数中启动首次接收
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
案例拆解:某智能家居网关项目需要同时处理UART、I2C、SPI等多个外设中断。工程师将UART接收中断优先级设置为中等(NVIC优先级分组2,抢占优先级2),确保不阻塞高优先级的定时器中断。在中断服务函数中仅将数据存入环形缓冲区,主循环中解析处理。实测在115200波特率下,连续接收1000字节无丢失,CPU占用率仅12%。若采用轮询方式,CPU占用率将超过60%,且无法响应其他外设事件。
| 对比维度 | 轮询方式 | 中断方式 | DMA方式 |
|---|---|---|---|
| CPU占用率 | 高(忙等待) | 中(每字节中断) | 低(批量传输) |
| 实时性 | 依赖轮询周期 | 即时响应 | 批量完成后响应 |
| 代码复杂度 | 简单 | 中等 | 较高 |
| 适用场景 | 低速、简单调试 | 中速、交互式通信 | 高速、大数据量 |
4.4 DMA方式收发数据:实现高效批量传输
核心原理:DMA(Direct Memory Access)允许外设与内存之间直接传输数据,无需CPU逐字节干预。UART的DMA传输分为两种模式:正常模式(传输指定长度后停止)和循环模式(缓冲区满后自动回绕)。DMA传输完成后触发传输完成中断,CPU只需在中断中处理整块数据。这种方式特别适合大数据量、高波特率的通信场景。
DMA配置关键步骤:
- 通道映射:STM32中USART1_TX对应DMA1通道4,USART1_RX对应DMA1通道5(不同型号有差异,需查参考手册);
- 方向配置:发送为内存到外设,接收为外设到内存;
- 缓冲区管理:定义接收缓冲区数组,设置DMA传输长度;
- 中断配置:使能DMA传输完成中断和UART空闲中断(用于不定长接收)。
#define RX_BUF_SIZE 256
uint8_t dma_rx_buf[RX_BUF_SIZE];
// 启动DMA接收
HAL_UART_Receive_DMA(&huart2, dma_rx_buf, RX_BUF_SIZE);
// 空闲中断回调(需在中断中判断)
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
// 计算实际接收长度
uint16_t len = RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
// 处理接收到的数据
process_data(dma_rx_buf, len);
// 重新启动DMA接收
HAL_UART_Receive_DMA(&huart2, dma_rx_buf, RX_BUF_SIZE);
}
}
实战场景:在无人机飞控系统中,需要以921600波特率从GPS模块接收NMEA协议数据,每帧约80字节,每秒输出10帧。采用DMA循环模式接收,缓冲区大小512字节,配合空闲中断判断帧边界。实测CPU占用率从中断方式的35%降至5%以下,且在高负载飞行控制算法运行时,GPS数据依然稳定接收无丢失。DMA方式还避免了中断频繁进出带来的抖动,提高了数据时间戳的准确性。
总结复盘:UART实战的核心在于理解三种收发方式的适用边界。回环测试是硬件调试的第一道工序,能够快速定位故障范围;中断方式适合中低速、交互式通信,兼顾实时性与代码复杂度;DMA方式适合高速、大数据量场景,能够显著降低CPU负载。在实际项目中,往往采用组合策略:中断接收命令帧、DMA传输批量数据、轮询发送调试信息。掌握这些实战技能,将为后续学习CAN、SPI等复杂通信协议打下坚实基础。