1. STM32串口通信基础与USART模块解析
串口通信作为嵌入式系统中最基础也最常用的通信方式之一,在STM32开发中占据着重要地位。USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是STM32芯片内置的串行通信接口,支持全双工异步通信和半双工同步通信。与简单的UART不同,USART增加了同步时钟信号线,使其能够适应更多通信场景。
在实际项目中,USART通常用于:
- 单片机与上位机(如PC)的调试信息交互
- 与各类串口模块(GPS、蓝牙、WiFi等)的数据传输
- 多设备间的简单组网通信
- 固件升级(ISP编程)
STM32F1系列通常提供3-5个USART接口,以我常用的STM32F103C8T6为例,它拥有3个USART接口(USART1-USART3)。其中USART1挂载在APB2总线(最高72MHz),其余挂载在APB1总线(最高36MHz),这个差异会直接影响波特率计算的基准时钟。
注意:USART1与其他USART接口的时钟源不同,在配置时需要特别注意时钟使能位和波特率计算。
2. USART硬件连接与引脚配置
2.1 典型硬件连接方案
标准的USART通信需要至少两根信号线:
- TX:数据发送引脚(MCU端输出)
- RX:数据接收引脚(MCU端输入)
在STM32与PC通信的典型场景中,由于PC使用RS232电平(±12V)而STM32使用TTL电平(0-3.3V),必须使用电平转换芯片如MAX3232进行转换。实际连接示意图如下:
code复制STM32(TTL) <---> MAX3232 <---> DB9接口 <---> PC(RS232)
对于3.3V TTL设备间的直接通信(如STM32与蓝牙模块),则可以省略电平转换芯片,直接交叉连接TX-RX:
code复制STM32.TX ---> 设备.RX
STM32.RX ---> 设备.TX
STM32.GND ---> 设备.GND
2.2 STM32引脚复用配置
以USART1为例,在STM32F103中默认引脚为:
- PA9:USART1_TX
- PA10:USART1_RX
配置代码示例:
c复制// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置TX为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置RX为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
实操技巧:当引脚冲突时,STM32的USART接口通常有重映射选项。例如USART1可以重映射到PB6/PB7,需同时开启AFIO时钟和重映射功能。
3. USART寄存器配置详解
3.1 波特率计算与设置
波特率是串口通信的核心参数,表示每秒传输的符号数。STM32的波特率计算公式为:
code复制波特率 = fCK / (16 * USARTDIV)
其中fCK是USART模块的输入时钟频率(APB1或APB2总线时钟),USARTDIV是一个无符号定点数,存储在USART_BRR寄存器中。
例如配置115200波特率,APB2时钟72MHz:
code复制USARTDIV = 72000000 / (16 * 115200) = 39.0625
BRR寄存器值 = 整数部分<<4 | 小数部分
= 39<<4 | 0.0625*16
= 0x0273
代码实现:
c复制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_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
3.2 中断配置与DMA优化
对于高效的数据传输,通常需要启用接收中断或DMA:
c复制// 使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// NVIC配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// DMA配置示例
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
4. 数据收发实现与调试技巧
4.1 基础收发函数
阻塞式发送函数实现:
c复制void USART_SendByte(USART_TypeDef* USARTx, uint8_t ch)
{
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
USART_SendData(USARTx, ch);
}
void USART_SendString(USART_TypeDef* USARTx, char *str)
{
while(*str){
USART_SendByte(USARTx, *str++);
}
}
中断接收典型处理:
c复制void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
uint8_t ch = USART_ReceiveData(USART1);
// 处理接收数据
RingBuffer_Write(&rxBuffer, ch);
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
4.2 实用调试技巧
-
波特率校准:
- 使用示波器测量实际波特率
- 计算误差应小于2.5%(RS232标准要求)
- 调整BRR寄存器微调波特率
-
数据错乱排查:
- 检查地线连接是否可靠
- 确认双方波特率、数据位、停止位、校验位设置一致
- 长距离通信时考虑增加终端电阻
-
printf重定向:
c复制#include <stdio.h>
int fputc(int ch, FILE *f)
{
USART_SendByte(USART1, (uint8_t)ch);
return ch;
}
// 之后可直接使用printf输出调试信息
5. 常见问题与解决方案
5.1 通信异常排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无任何数据 | 线缆连接错误 | 检查TX/RX是否交叉连接 |
| 收到乱码 | 波特率不匹配 | 核对双方波特率设置 |
| 数据丢失 | 未接地线 | 连接双方GND |
| 只能单向通信 | 流控设置错误 | 禁用硬件流控 |
| 偶发错误 | 电磁干扰 | 使用屏蔽线,缩短距离 |
5.2 性能优化建议
-
环形缓冲区应用:
- 在中断服务程序中只做数据搬运
- 主循环中处理业务逻辑
- 避免在中断中进行复杂处理
-
DMA双缓冲技术:
- 配置两个接收缓冲区交替使用
- 当一个缓冲区满时自动切换,同时处理已满数据
- 极大提高大数据量接收效率
-
硬件流控使用:
- 在高速通信或不可控数据流场景
- 启用RTS/CTS流控信号
- 防止接收缓冲区溢出
在实际项目中,我发现USART通信的稳定性很大程度上取决于初始化的完整性和异常处理的健壮性。一个建议的做法是在初始化后发送一段已知数据(如"STM32 READY")进行自检,确认通信链路正常后再开始业务数据传输。