STM32 HAL 函数大全 · 第 2 卷
UART(Universal Asynchronous Receiver/Transmitter)是嵌入式系统中最通用的串行通信接口。STM32 的 USART 外设功能非常强大,不仅支持标准的异步串口通信,还支持同步模式、单线半双工、LIN 总线、甚至 SmartCard 协议。
UART / USART 模块(通用同步/异步收发器)
版本:v1.0 | 适用系列:STM32F0 / F1 / F3 / F4 / F7 / L0 / L4 / H7
目录
1. UART 模块简介
UART(Universal Asynchronous Receiver/Transmitter)是嵌入式系统中最通用的串行通信接口。STM32 的 USART 外设功能非常强大,不仅支持标准的异步串口通信,还支持同步模式、单线半双工、LIN 总线、甚至 SmartCard 协议。
核心功能
-
全双工异步通信:RX 和 TX 独立工作,同时收发。
-
硬件流控 (RTS/CTS):防止高速传输时数据缓冲区溢出。
-
DMA 支持:实现零 CPU 占用的高速数据搬运。
-
多处理器通信:支持静默模式和地址唤醒。
-
同步同步模式 (USART)**:提供时钟输出 (SCLK),可驱动 SPI 从机设备。
2. 通信协议与参数配置
在 UART_InitTypeDef 结构体或 CubeMX 中,核心配置参数如下:
| 参数名称 | HAL 宏定义示例 | 说明 |
|---|---|---|
| 波特率 | BaudRate = 11520BaudRate = 115200` |
通信速度,双方必须严格一致。常用 9600, 115200。 |
| 数据位 | WordLength = UART_WORDLENGTH_8B |
数据帧长度,通常为 8 位。若开启奇偶校验,某些系列需设为 9 位。 |
| 停止位 | StopBits = UART_STOPBITS_1 |
帧结束标志,通常为 1 位。 |
| 校验位 | Parity = UART_PARITYParity = UART_PARITY_NONE` |
奇偶校验,通常无校验 (None)。 |
| 流控 | HwFlowCtl = UART_HWCONTROL_NONE |
硬件流控,高速传输时建议开启 RTS/CTS。 |
| 模式 | Mode = UART_MODE_TX_RX |
开启发送 (TX) 和接收 (RX)。 |
注意:通信双方(例如 STM32 与 电脑串口助手)的所有参数必须完全一致,否则会出现乱码。
3. 三大工作模式详解
STM32 UART 数据收发主要有三种方式,适用于不同场景:
3.1 轮询模式 (Blocking / Polling)
-
原理:CPU 在
while循环中不断查询状态寄存器,直到发送/接收完成。 -
优点:逻辑简单,无中断竞争。
-
缺点:严重阻塞 CPU,无法处理并发任务,容易丢数据。
-
场景:系统初始化打印、简单的调试信息输出。
3.2 中断模式 (Non-Blocking / IT)
-
原理:每接收或发送一个字节触发一次中断,CPU 暂停主程序去处理数据。
-
优点:CPU 不需要死等,实时性好。
-
缺点:大数据量时中断频繁,占用大量 CPU 资源(频繁压栈出栈)。
-
场景:低速、低频的命令交互(如 AT 指令)。
3.3 DMA 模式 (Direct Memory Access)
-
原理:DMA 控制器直接在内存(SRAM)和串口寄存器(DR)之间搬运数据,完成后才通知 CPU。
-
优点:CPU 零负载,适合高速、大数据流传输。
-
缺点:配置相对复杂,处理不定长数据需配合空闲中断 (Idle Line)。
-
场景:GPS 数据流、Modbus 协议、固件升级、高速传感器。
4. HAL UART API 函数全集
以下函数定义在 stm32xx_hal_uart.c 中:
4.1 初始化与控制
// 初始化 UART(底层调用 HAL_UART_MspInit 配置引脚和时钟) HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart); // 反初始化(释放 IO 和时钟) HAL_StatusTypeDef HAL_UART_DeInit(UART_HandleTypeDef *huart);
4.2 数据发送 (Transmit)
// 轮询发送 (阻塞,直到发送完 Size 字节或超时) HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 中断发送 (非阻塞) HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // DMA 发送 (非阻塞,推荐) HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
4.3 数据接收 (Receive)
// 轮询接收 (阻塞) HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 中断接收 (非阻塞) HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // DMA 接收 (非阻塞) HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // DMA 接收 + 空闲中断检测 (处理不定长数据神器,部分新版 HAL 库支持) HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
4.4 中断与状态
// 获取当前状态 HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart); // 获取错误码 (如 ORE 溢出错误) uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart);
5. 中断与 DMA 回调机制
HAL 库通过统一的中断处理函数分发事件,用户只需重写对应的 __weak 回调函数。
核心回调函数
| 回调函数名 | 触发时机 | 典型用途 |
|---|---|---|
HAL_UART_TxCpltCallback |
发送完成 (IT/DMA) | 切换 RS485 收发方向、启动下一次发送 |
HAL_UART_RxCpltCallback |
接收定长数据完成 | 处理数据包、解析命令 |
HAL_UARTEx_RxEventCallbackHAL_UARTEx_RxEventCallback` |
接收完成或空闲中断触发 | DMA 不定长接收的核心处理处 |
HAL_UART_ErrorCallback |
发生错误 (如溢出 ORE) | 清除错误标志、重启接收 |
6. printf 重定向实现
为了让 printf 输出到串口,需重写标准库底层输出函数。
6.1 Keil (MDK) 环境
在 main.c 或 usart.c 中添加(需勾选 "Use MicroLIB"):
#include <stdio.h>
/* 重写 fputc */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
6.2 GCC / STM32CubeIDE 环境
/* 重写 _write */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF);
return len;
}
7. 实战工程示例:DMA不定长接收
这是实际项目中处理 GPS、WIFI 模块数据最稳定、最高效的方案。
7.1 初始化 (main.c)
#define RX_BUF_SIZE 256
uint8_t RxBuf[RX_BUF_SIZE]; // 接收缓冲区
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USART1_UART_Init();
MX_DMA_Init(); // 必须初始化 DMA
// 启动 DMA 接收并开启空闲中断 (IDLE IE)
// 此时串口会将数据搬运到 RxBuf,直到缓冲区满或线路空闲
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuf, RX_BUF_SIZE);
while (1)
{
// 主循环处理业务
}
}
7.2 回调处理 (main.c)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
// Size 是实际接收到的字节数
// 1. 处理数据 (建议拷贝到其他 buffer 或置标志位,不要在此耗时)
Process_Data(RxBuf, Size);
// 2. 必须重新开启接收 (DMA Normal 模式下)
// 这里的 Size 参数是缓冲区最大长度
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuf, RX_BUF_SIZE);
}
}
8. 调试技巧与常见避坑
-
乱码问题:
-
检查外部晶振频率 (HSE) 设置是否与板子实际一致(如 8M vs 25M)。
-
检查波特率两端是否一致。
-
检查共地 (GND)
-
-
接收中断只进一次:
-
轮询/中断模式下,ORE (Overrun Error) 溢出错误会导致中断锁死。
-
解决:在
HAL_UART_ErrorCallback中清除错误标志,或在主循环检查并清除 ORE。 -
中断回调中忘记再次调用
HAL_UART_Receive_IT。
-
-
DMA 接收数据不更新**:
-
在 Cortex-M7 (F7/H7) 上,若开启了 D-Cache,DMA 更新了 SRAM 但 CPU 读的是 Cache 旧值。
-
解决:配置 MPU 将 DMA 缓冲区设为 Non-Cacheable,或调用
SCB_InvalidateDCache_by_Addr。
-
-
printf 卡死: *
-
未勾选 MicroLIB (Keil)。
-
硬件上未连接 TX 线,导致一直等待发送完成
-
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)