1. STM32串口通信基础与固件库概述
在嵌入式系统开发中,串口通信是最基础也最常用的外设功能之一。STM32F103系列微控制器提供了多达5个USART/UART接口,支持同步/异步通信、单线半双工、多处理器通信等多种模式。使用标准外设库(SPL)进行开发时,所有串口相关功能都通过初始化结构体和配套库函数实现,这种抽象化的编程方式极大提高了开发效率。
实际工程中,串口常用于以下场景:
- 调试信息输出(替代昂贵的调试器)
- 与上位机通信(如Modbus协议)
- 连接无线模块(蓝牙、WiFi等)
- 工业现场设备间通信
与直接操作寄存器相比,固件库开发具有明显优势:
- 代码可读性强:通过结构体成员名称即可理解配置含义
- 可移植性好:更换同系列MCU时只需少量修改
- 开发效率高:库函数已处理好底层细节,开发者专注应用逻辑
提示:虽然HAL库已成为ST主推的开发方式,但在资源受限的F1系列中,标准外设库仍是最佳选择,因其占用资源少且执行效率高。
2. USART初始化结构体深度解析
2.1 USART_InitTypeDef结构体详解
这个结构体包含串口异步通信的所有关键参数配置,完整定义如下:
c复制typedef struct {
uint32_t USART_BaudRate; // 波特率
uint16_t USART_WordLength; // 字长
uint16_t USART_StopBits; // 停止位
uint16_t USART_Parity; // 校验位
uint16_t USART_Mode; // 工作模式
uint16_t USART_HardwareFlowControl; // 硬件流控
} USART_InitTypeDef;
2.1.1 波特率配置精要
波特率设置是串口通信中最关键的参数之一,直接影响通信质量和稳定性。STM32的波特率发生器采用分数计算方式,公式为:
code复制波特率 = fCK / (16 * USARTDIV)
其中fCK是外设时钟频率(PCLK1或PCLK2),USARTDIV是存放在USART_BRR寄存器中的数值。库函数内部会自动完成以下计算:
c复制// 波特率计算示例(PCLK2=72MHz,目标波特率115200)
float USARTDIV = 72000000.0 / (115200 * 16); // 约等于39.0625
uint16_t DIV_Mantissa = 39; // 整数部分
uint16_t DIV_Fraction = 0x04; // 小数部分0.0625*16=1 → 4位表示
USART_BRR = (DIV_Mantissa << 4) | DIV_Fraction;
常见波特率误差对比表:
| 目标波特率 | 实际波特率 | 误差率 | 适用场景 |
|---|---|---|---|
| 9600 | 9599.77 | 0.002% | 低速可靠通信 |
| 115200 | 115107.14 | 0.08% | 常用调试波特率 |
| 230400 | 229846.15 | 0.24% | 高速数据传输 |
| 921600 | 937500.00 | 1.7% | 仅建议短距离使用 |
经验:当误差超过2%时通信可能不稳定,应避免使用。长距离通信建议不超过115200bps。
2.1.2 数据帧格式配置
数据位、停止位和校验位的组合构成了串口的数据帧格式。最常见的8N1配置表示:
- 8位数据长度
- 无校验位(None)
- 1位停止位
实际选择时需要与通信对方严格匹配:
字长选择:
- 8位(USART_WordLength_8b):适用于ASCII字符和大多数二进制数据
- 9位(USART_WordLength_9b):用于特殊协议如LIN总线
停止位选择:
- 1位(USART_StopBits_1):标准配置,适用于大多数场景
- 2位(USART_StopBits_2):用于抗干扰能力较差的线路
校验位选择:
- 无校验(USART_Parity_No):默认选择,效率最高
- 奇校验(USART_Parity_Odd):确保数据中1的个数为奇数
- 偶校验(USART_Parity_Even):确保数据中1的个数为偶数
注意:启用校验位时,实际数据位会减少1位(如选择8位字长+校验,实际有效数据为7位)
2.2 工作模式与硬件流控
2.2.1 工作模式配置
USART_Mode成员控制串口的收发方向,常用组合:
c复制// 同时启用发送和接收
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
在只需要单向通信的场景(如仅作为调试输出),可以单独配置为发送或接收模式以节省功耗。
2.2.2 硬件流控实战解析
硬件流控通过RTS(Request To Send)和CTS(Clear To Send)信号线实现,能有效防止数据丢失。典型应用场景:
- 无线模块通信:当模块缓冲区满时通过CTS通知MCU暂停发送
- 高速通信:波特率≥115200时建议启用流控
- 长距离传输:线路延迟可能导致数据丢失
配置示例:
c复制USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_RTS_CTS;
硬件连接示意图:
code复制MCU 外设
TX -----> RX
RX <----- TX
RTS <---- CTS
CTS ----> RTS
调试技巧:初期可以不使用硬件流控,通过软件缓冲机制(如环形缓冲区)处理数据流控制。
3. 固件库函数应用详解
3.1 初始化流程完整示例
标准初始化流程包含四个关键步骤:
- 填充初始化结构体
- 调用USART_Init()
- 配置中断(如需)
- 使能串口
c复制void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置串口参数
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
// 4. 使能串口
USART_Cmd(USART1, ENABLE);
}
3.2 数据收发函数进阶技巧
3.2.1 发送数据优化
基础发送函数:
c复制void USART_SendByte(USART_TypeDef* USARTx, uint8_t data)
{
USART_SendData(USARTx, data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
高效发送方案(使用DMA):
c复制void USART1_DMA_Send(uint8_t *buf, uint16_t len)
{
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, len);
DMA1_Channel4->CMAR = (uint32_t)buf;
DMA_Cmd(DMA1_Channel4, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
}
3.2.2 接收数据处理
查询方式接收:
c复制uint8_t USART_ReceiveByte(USART_TypeDef* USARTx)
{
while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
return (uint8_t)USART_ReceiveData(USARTx);
}
中断接收最佳实践:
c复制#define RX_BUF_SIZE 256
uint8_t rx_buf[RX_BUF_SIZE];
uint16_t rx_index = 0;
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART1);
if(rx_index < RX_BUF_SIZE)
rx_buf[rx_index++] = data;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
3.3 状态检测与错误处理
关键状态标志位:
- USART_FLAG_TXE:发送数据寄存器空
- USART_FLAG_TC:发送完成
- USART_FLAG_RXNE:接收数据寄存器非空
- USART_FLAG_ORE:溢出错误
- USART_FLAG_FE:帧错误
错误处理示例:
c复制void USART_ErrorHandler(void)
{
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_ORE);
// 处理溢出错误
}
if(USART_GetFlagStatus(USART1, USART_FLAG_FE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_FE);
// 处理帧错误
}
}
4. 实战配置案例集锦
4.1 多串口协同工作配置
当系统中需要同时使用多个串口时(如USART1调试输出,USART2连接模块),需注意:
-
时钟配置差异:
- USART1挂载在APB2总线(最高72MHz)
- USART2/3挂载在APB1总线(最高36MHz)
-
中断优先级管理:
c复制NVIC_InitTypeDef NVIC_InitStructure;
// USART1中断配置(高优先级)
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
// USART2中断配置(低优先级)
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Init(&NVIC_InitStructure);
4.2 低功耗模式下的串口配置
在需要省电的应用中,可配置串口在空闲时进入低功耗模式:
c复制// 进入低功耗模式前
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, DISABLE);
// 唤醒后重新初始化
USART_Cmd(USART1, ENABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
4.3 自定义协议实现框架
基于串口实现简单通信协议:
c复制typedef struct {
uint8_t header[2]; // 0xAA 0x55
uint8_t cmd;
uint8_t len;
uint8_t data[32];
uint8_t checksum;
} ProtocolFrame;
void Protocol_Send(USART_TypeDef* USARTx, ProtocolFrame *frame)
{
frame->checksum = CalculateChecksum(frame);
uint8_t *p = (uint8_t*)frame;
for(int i=0; i<sizeof(ProtocolFrame); i++)
USART_SendByte(USARTx, p[i]);
}
5. 调试技巧与常见问题
5.1 硬件连接检查清单
- TX-RX交叉连接:MCU的TX应连接对方RX,RX连接对方TX
- 共地要求:确保通信双方有共同的地参考
- 上拉电阻:在长距离通信时,TX线建议加1kΩ上拉电阻
- 电平匹配:3.3V设备与5V设备连接时需要电平转换
5.2 典型故障排除指南
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 能发送不能接收 | RX引脚配置错误 | 检查GPIO模式应为GPIO_Mode_IN_FLOATING |
| 数据乱码 | 波特率不匹配 | 确认双方波特率一致,检查时钟配置 |
| 偶尔丢数据 | 无流控/缓冲区溢出 | 降低波特率或实现软件流控 |
| 发送最后一个字节丢失 | 未等待TC标志 | 发送完成后检查USART_FLAG_TC |
| 频繁进入中断 | 未清除中断标志 | 在中断服务函数中清除对应标志位 |
5.3 性能优化建议
- 使用DMA传输大数据块,减少CPU开销
- 实现双缓冲机制避免数据丢失
- 对时间敏感的操作使用TC标志而非TXE
- 合理设置中断优先级,避免通信被其他中断阻塞
- 在高速通信时关闭调试打印输出
通过深入理解USART初始化结构体和固件库函数,开发者可以构建稳定可靠的串口通信系统。实际应用中,建议结合具体需求选择合适的配置方案,并通过示波器或逻辑分析仪验证信号质量。
