STM32F103 串口的实践使用说明
在嵌入式开发的入门路径里,STM32F103的串口是几乎所有开发者都会第一个深入掌握的外设。它就像芯片对外沟通的“有线电话”,既可以把芯片内部的运行状态、传感器数据实时打印到电脑上辅助调试,也能和蓝牙、GPS、语音模块等各类外接设备完成数据交互,是打通芯片和外部世界的第一道桥梁。很多新手刚接触STM32时,往往会被复杂的寄存器配置劝退,但只要理清串口的底层逻辑和分步操作流程,就能快速上手,把这个最常用的外设用得稳定可靠。
先理清串口通信的核心基础
在动手写代码之前,先把几个核心概念理解清楚,后续遇到问题时才能快速定位根因,而不是盲目照搬网上的示例代码。
STM32F103全系列最多支持5个串口,其中前三个是通用同步异步收发器,不仅能做我们最常用的异步通信,还支持同步时钟模式,适合对时序精度要求极高的场景;后两个是通用异步收发器,仅支持最常用的异步通信模式,完全能满足绝大多数普通项目的需求。不同串口挂载的时钟总线也有区别:USART1挂载在高速的APB2总线上,最高主频可以达到72MHz,能支持更高的波特率;剩下的USART2、USART3、UART4、UART5都挂载在APB1总线上,最高主频为36MHz,日常使用中9600、115200这类常用波特率完全不会有任何问题。
异步串口通信的核心优势是不需要额外的时钟线,只需要TX发送引脚、RX接收引脚和一根共地线就能完成两台设备之间的数据传输,硬件连接极其简单。通信双方必须提前约定好统一的通信规则,才能保证数据收发不出错:首先是波特率,也就是每秒传输的码元个数,最常用的9600和115200是嵌入式项目里的通用选择,两端必须完全一致;其次是数据包格式,每一个传输的字节都以一个低电平的起始位作为开头,后面跟着8位有效数据,最后以高电平的停止位作为结束,还可以根据需求选择是否增加校验位来检测传输过程中可能出现的数据错误。日常开发中,几乎所有外接模块的默认配置都是“8位数据、1位停止位、无校验位”,新手优先使用这个标准配置,能避免90%以上的基础通信问题。
从零开始的分步配置流程
STM32F103串口的配置步骤逻辑非常清晰,新手按照固定顺序一步步操作,就能快速完成可用的串口初始化,不容易出现遗漏。
第一步是开启对应外设的时钟。很多新手第一次写串口代码没有效果,第一个踩坑点就是忘记开时钟。STM32的所有外设默认时钟都是关闭的,必须手动使能才能正常工作,而且不同串口的时钟所属总线不一样:USART1和它对应的GPIOA端口时钟都在APB2总线上,而USART2的时钟在APB1总线,对应的GPIO端口时钟依然在APB2总线,配置时一定要区分清楚所属总线,不能统一调用同一个时钟使能函数,否则外设完全不会工作。
第二步是完成GPIO引脚的模式配置。串口的TX发送引脚是由串口外设直接控制输出的,不能配置成普通的推挽输出,必须设置为复用推挽输出模式,这样引脚的输出控制权才会交给串口外设,而不是普通的GPIO寄存器。RX接收引脚负责读取外部设备发来的电平信号,配置为浮空输入或者上拉输入模式都可以,推荐新手使用上拉输入模式,空闲状态下引脚电平保持高电平,能减少外部干扰导致的误触发乱码。配置完成后,TX和RX引脚的电平控制权就完全交给了串口外设,普通的GPIO读写函数将不再对这两个引脚生效。
第三步是串口核心参数的初始化配置。这里不需要手动操作底层寄存器,直接调用标准库的初始化结构体就能完成所有参数设置:首先设置好约定好的波特率,然后关闭硬件流控制——普通异步通信几乎用不到流控制,开启后反而会导致通信异常;接着配置串口模式为收发双全工,同时开启发送和接收功能;再设置为无校验位、1位停止位、8位有效数据,和之前约定的标准通信规则保持一致。所有参数配置完成后,调用串口使能函数,整个串口外设就正式启动开始工作了。
如果需要实现数据的主动接收,而不是一直轮询查询标志位,就必须额外配置串口中断。先在嵌套向量中断控制器里开启对应串口的中断通道,设置好抢占优先级和子优先级,保证串口中断能被正常响应,再单独开启串口的接收中断使能位。这样每当串口收到一个新的字节,就会自动触发中断,通知CPU来读取数据,不需要CPU一直循环等待,大幅提升程序的运行效率。
收发功能的代码实现与实用技巧
完成初始化之后,就可以基于串口的状态标志位,封装出实用的收发函数,逐步实现从单字节发送到格式化打印的全功能。
串口发送的核心逻辑非常简单,每发送一个字节,硬件就会自动置位“发送数据寄存器为空”的标志位,我们可以基于这个特性封装单字节发送函数:把待发送的字节写入串口数据寄存器,然后循环等待空标志位置位,确认当前字节已经被硬件送入发送移位寄存器之后,函数再退出,保证数据不会丢失。在单字节发送函数的基础上,很容易扩展出字符串发送、数组发送等功能:遍历字符串的每一个字符,依次调用单字节发送函数,直到遇到字符串结束符为止,就能把一整段文本完整发送出去。
很多新手都希望能像在电脑上一样,直接用printf函数把格式化的数字、字符串打印到串口助手上,这个功能只需要重定向C标准库的底层输出函数就能实现。把标准库的字符输出函数替换成我们自己写的串口单字节发送函数,重新编译工程之后,所有的printf调用都会自动把内容通过串口发送出去,不需要自己手动拼接格式化字符串,调试代码的效率会大幅提升。使用这个功能的时候要注意,部分开发环境默认开启了半主机模式,需要在工程配置里关闭,否则程序运行会出错。
串口接收的实现分为两种模式,新手入门阶段可以先使用轮询模式,在主循环里不断查询接收数据寄存器非空的标志位,如果标志位置位,就读取接收到的字节,这种方式逻辑简单,不容易出错,适合调试阶段快速验证通信链路是否正常。进阶之后推荐使用中断接收模式,在串口中断服务函数里把每一个收到的字节存入自定义的接收缓冲区,再通过标记位通知主循环“一整包数据已经接收完成”,主程序只需要在合适的时机检查标记位,处理完整的数据包即可,不会阻塞主循环的其他任务运行。
实战避坑与调试优化技巧
实际项目开发中,串口通信出现问题是非常常见的情况,掌握几个实用的调试技巧,就能快速定位99%的常见问题。
硬件层面的问题是新手最容易踩的坑:首先要确认接线没有接反,本端的TX必须接对端的RX,本端的RX接对端的TX,很多新手第一次接线直接TX接TX、RX接RX,导致完全收不到数据;其次要保证两台设备共地,STM32的GND和USB转串口模块的GND必须用杜邦线连接在一起,否则两边的电平基准不一样,就算接线正确也会出现乱码;最后要确认硬件电平匹配,STM32的串口是3.3V TTL电平,不能直接和RS232电平的设备直接连接,否则不仅通信失败,还可能烧坏芯片的串口引脚。
软件层面的调试可以借助几个小技巧快速定位问题:如果串口完全没有任何数据输出,可以在初始化完成之后,用普通GPIO函数直接翻转TX引脚的电平,用示波器或者万用表查看引脚电平是否正常,先确认硬件没有故障,再回头检查时钟和引脚模式配置;如果能收到数据但全是乱码,优先检查波特率配置是否正确,其次确认系统时钟配置正常,很多新手把外部晶振选错,导致系统实际主频和预期不一致,最终串口波特率出现巨大偏差,就会出现全乱码的现象。
在复杂项目中使用串口时,还要做好性能优化:给接收缓冲区设置合理的大小,避免短时间内收到大量数据导致缓冲区溢出;在中断服务函数里只做最基础的字节存储操作,所有复杂的数据解析逻辑都放到主循环里执行,避免中断停留时间过长影响其他外设的实时响应;如果项目里用到多个串口,给不同的串口封装独立的初始化函数和收发缓冲区,避免不同串口的逻辑互相干扰,代码的可维护性会大幅提升。
从最基础的点亮LED,到后续连接各类传感器、外接通信模块,串口几乎贯穿了STM32开发的整个学习过程。把串口的原理和使用方法吃透,不仅能快速解决调试过程中的各种问题,还能为后续学习I2C、SPI等其他串行通信外设打下坚实的基础,是嵌入式开发者必须掌握的核心基本功。





