STM32F1串口通信详解:从配置到实战
一、串口通信基础
串口(UART)是一种异步全双工通信协议,广泛应用于嵌入式设备调试、传感器数据交互等场景。STM32F1系列微控制器内置多个USART外设(如USART1-USART3),支持以下关键特性:
- 波特率可调(1200bps~4.5Mbps)
- 可配置数据位(8/9位)、停止位(1/1.5/2位)
- 硬件流控制(CTS/RTS)
- 中断/DMA传输模式
二、硬件连接
以USART1为例(PA9-TX, PA10-RX):
- 使用USB转TTL模块(如CH340G)连接开发板
- 交叉连接TX-RX线(STM32_TX → TTL_RX,STM32_RX → TTL_TX)
- 共地连接(GND ↔ GND)
三、代码实现(详细注释版)
- 初始化配置函数解析
void USART1_Init(uint32_t baudrate) {
// 定义GPIO和USART初始化结构体
GPIO_InitTypeDef GPIO_InitStruct; // GPIO配置容器
USART_InitTypeDef USART_InitStruct; // 串口参数容器
/* 时钟使能(关键步骤!) */
// 启用USART1和GPIOA的时钟(APB2总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
/* TX引脚(PA9)配置 */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; // 选择引脚9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式
/* 为什么用推挽输出?
保证TX引脚能主动输出高/低电平,提供稳定的驱动能力 */
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 最大输出速率
GPIO_Init(GPIOA, &GPIO_InitStruct); // 应用配置到GPIOA
/* RX引脚(PA10)配置 */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式
/* 浮空输入特点:
不内部上拉/下拉,依赖外部信号电平,适合接收可变信号 */
GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART参数配置 */
USART_InitStruct.USART_BaudRate = baudrate; // 设置波特率(如9600)
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位停止位
USART_InitStruct.USART_Parity = USART_Parity_No; // 无校验位
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 启用收发
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_Init(USART1, &USART_InitStruct); // 应用配置到USART1
USART_Cmd(USART1, ENABLE); // 最后使能USART外设
}
2. 数据收发函数解析
// 阻塞式发送单字节函数
void USART1_SendByte(uint8_t data) {
USART_SendData(USART1, data); // 写入数据到发送寄存器
/* 等待发送完成:
TXE标志位=1表示数据已转移到移位寄存器(非完全发送完成)
实际使用时建议检查TC标志(传输完成标志)更可靠 */
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
// 中断接收初始化
void USART1_IT_Init(void) {
// 使能RXNE(接收寄存器非空)中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* 配置NVIC(嵌套向量中断控制器):
USART1_IRQn表示中断号,优先级使用默认值 */
NVIC_EnableIRQ(USART1_IRQn); // 使能USART1全局中断
}
// 中断服务函数(每次接收触发)
void USART1_IRQHandler(void) {
// 检查是否是RXNE中断触发
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 读取接收到的数据(自动清除RXNE标志)
uint8_t data = USART_ReceiveData(USART1);
/* 示例处理:回传数据
实际项目建议将数据存入环形缓冲区 */
USART1_SendByte(data);
// 必须手动清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
关键代码原理说明
1. 时钟使能
RCC_APB2PeriphClockCmd()
是开启外设的"电源开关",未使能时钟时配置无效
STM32F1的USART1挂载在APB2总线,其他USART在APB1
2. GPIO模式选择
TX引脚:复用推挽输出(GPIO_Mode_AF_PP)
复用功能由USART硬件控制输出时序
推挽结构确保强驱动能力
RX引脚:浮空输入(GPIO_Mode_IN_FLOATING)
高阻抗状态避免影响输入信号
3. 中断流程
接收数据时硬件自动置位RXNE标志
触发中断后跳转到
USART1_IRQHandler
USART_GetITStatus
检查特定中断源
读取数据寄存器会自动清除RXNE标志
必须调用
USART_ClearITPendingBit
清除中断挂起标志
4. 阻塞发送 vs 中断发送
当前示例使用简单阻塞发送,实际项目中:
高频率发送建议使用DMA或带缓冲区的中断发送
阻塞发送在低波特率时可能导致CPU资源浪费
常见疑问解答
Q1: 为什么使能时钟要放在最前面?
A1: STM32的外设配置必须在其时钟开启后进行,否则寄存器访问可能无效或导致硬件错误。
Q2: 如何修改为115200波特率?
A2: 调用
USART1_Init(115200)
即可,但需确保硬件支持(如晶振频率足够)
Q3: 中断函数里为什么要回传数据?
A3: 这是简单的回环测试示例,实际应用应根据协议处理数据,建议添加软件缓冲区。
四、调试技巧
常见问题排查
-
无数据收发
- 检查波特率是否匹配(建议从9600开始)
- 确认TX/RX线是否反接
- 测量TX引脚电压(正常应有3.3V高电平)
-
数据乱码
- 检查双方数据位/停止位配置
- 确认时钟源配置正确(HSI/PLL)
- 降低波特率测试信号质量
-
中断不触发
- 检查NVIC优先级配置
- 确认USART_ITConfig使能接收中断
五、实验结果
使用串口助手发送"Hello"时,设备返回:
发送: Hello
接收: Hello
(成功实现数据回环测试)
六、进阶应用
- DMA传输:减少CPU占用,适合高速通信
- Modbus协议:实现工业标准通信
- JSON数据解析:构建IoT应用
推荐工具:
- 串口调试助手:SSCOM/XCOM
- 协议分析:Wireshark+USB转接器
通过这篇博客,读者可以快速上手STM32F1的串口开发。实际应用中需根据具体需求调整硬件流控制、错误检测等参数。欢迎在评论区交流调试心得! 🚀