## 一、波特率你真的算对了吗? USART 的波特率决定数据传输速率。STM32 通常使用分数波特率发生器,外设时钟 `fck` 经过分频后产生位时钟。标准公式为: ```c BRR = fck / (USARTDIV * 16) ``` 其中 `USARTDIV` 写在 `BRR` 寄存器中,可含小数。例如 `APB2 = 72MHz`,要得 `115200` 波特率: ```c USARTDIV = 72e6 / (115200 * 16) = 39.0625 BRR = (39 << 4) | 0x0001 // 小数部分乘16取整 ``` 实际波形频率偏差应小于 2%~3%。CubeMX 会自动计算,但批量生产时需验证晶振精度,使用外部晶振时 `HSE_VALUE` 必须与实际一致。 ## 二、中断收发:标志位是灵魂 USART 中断收发依赖两个核心标志: - **RXNE**:接收数据寄存器非空,读取 `DR` 后自动清除 - **TXE**:发送数据寄存器空,写入 `DR` 后自动清除 HAL 库对此做了封装,但使用 `HAL_UART_Receive_IT(&huart, buffer, size, callback)` 时,**每接收一个字节都会进入中断**,直到收满 size 个字节回调 `HAL_UART_RxCpltCallback`。发送同理,`HAL_UART_Transmit_IT` 发送完 buffer 后回调。 注意:HAL 库的中断方式默认只触发一次(单次模式),若需连续接收,必须在回调中再次调用接收函数,否则串口将“停摆”。此外必须处理 `ORE`(过载错误),通常在中断中检查 `__HAL_UART_GET_FLAG(&huart, UART_FLAG_ORE)` 并调用 `__HAL_UART_CLEAR_OREFLAG(&huart)` 清除,否则可能死循环。 ## 三、DMA:让 CPU 去干正事 DMA 收发无需 CPU 逐字节干预,适合高速或大块数据。关键配置点: - **方向**:外设到内存(接收)或内存到外设(发送) - **模式**:普通模式传完自动停止;循环模式配合环形缓冲可无缝接收流式数据 - **数据宽度**:通常字节对字节 - **DMA 中断**:传输完成、半传输等,用于包处理 HAL 库用法: ```c HAL_UART_Receive_DMA(&huart, rx_buf, RX_LEN); // 开始 DMA 接收 HAL_UART_Transmit_DMA(&huart, tx_buf, len); // DMA 发送 ``` 完成回调:`HAL_UART_RxCpltCallback`、`HAL_UART_TxCpltCallback`。若开启循环模式,则配合 `HAL_UARTEx_RxEventCallback` 才能处理半满中断。 ## 四、完整代码示例(HAL 库 + 中断接收 + DMA 发送) ```c /* 配置信号量/标志 */ volatile uint8_t rx_byte = 0; volatile uint8_t rx_flag = 0; void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart1); } /* 启动中断接收 */ HAL_UART_Receive_IT(&huart1, &rx_byte, 1); /* 回调:收到一个字节后触发 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 将收到的字节在 DMA 模式下回显 HAL_UART_Transmit_DMA(&huart1, &rx_byte, 1); // 重新启用中断接收(单次模式必须重新调用) HAL_UART_Receive_IT(&huart1, &rx_byte, 1); } } /* DMA 直接发送字符串示例 */ HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"Hello STM32\r\n", 15); ``` 注意:DMA 发送时若 CPU 修改 buffer 可能导致数据错乱,应用层需等 `HAL_UART_TxCpltCallback` 后再修改;接收中断里操作 DMA 发送是安全的,但高频收发时考虑“忙碌”状态。 ## 五、常见坑与注意事项 - **DMA 时钟**:`__HAL_RCC_DMA_CLK_ENABLE()` 必须开启,否则硬件不工作。 - **缓存一致性问题**:若使用多缓冲,请避免 CPU 与 DMA 同时访问同一段内存,必要时使用 `__HAL_DMA_DISABLE` 后再操作。 - **波特率误差累积**:多字节组帧时,误差会累积,建议接收端每帧重新同步。 - **空闲中断(IDLE)**:处理不定长数据时,可开启 `HAL_UARTEx_ReceiveToIdle_DMA` 实现“收完一帧再处理”,避免逐字节回调开销。 掌握波特率原理、中断标志和 DMA 模式,能让你在资源紧张的系统中游刃有余。建议先理解寄存器行为,再借助 HAL 快速开发——知其然,更知其所以然。