第4章 CubeMX配置实战:时钟树、USART、DMA与中断优先级
好,咱们开始动手配置了。前面几章讲了蓝牙模块的选型、硬件连接,现在终于要打开CubeMX,把那些参数一项一项填进去。说实话,我刚开始用CubeMX的时候,看着时钟树那个界面也有点懵——那么多分频器、锁相环,到底该怎么设?别急,这一章我带你走一遍完整的配置流程。
本章核心目标:用CubeMX生成一个可直接用于蓝牙通信的基础工程。配置完时钟、USART、DMA和中断后,你就能在代码里直接调用HAL库的收发函数了。
4.1 时钟树配置:HSE与LSE的选择
时钟是MCU的心脏。STM32的时钟源有好几个:HSE(外部高速晶振)、HSI(内部高速RC振荡器)、LSE(外部低速晶振)、LSI(内部低速RC振荡器)。做蓝牙通信,我建议你优先用HSE。
为什么?因为蓝牙模块的波特率精度要求比较高。HSE通常用8MHz或16MHz的晶振,精度在±20ppm以内。而HSI内部振荡器虽然省了晶振成本,但精度只有±1%左右,温度一变化还会漂。我在一个户外项目里吃过这个亏——用HSI跑115200波特率,夏天高温时误码率直接飙升。
| 时钟源 | 典型频率 | 精度 | 推荐场景 |
|---|---|---|---|
| HSE | 8MHz / 16MHz | ±20ppm | 蓝牙通信、高波特率场景 |
| HSI | 16MHz | ±1% | 低功耗、非通信类应用 |
| LSE | 32.768kHz | ±10ppm | RTC、低功耗蓝牙唤醒 |
| LSI | 32kHz | ±5% | 独立看门狗、低精度RTC |
4.1.1 配置步骤
- 选择HSE:在CubeMX的
Pinout & Configuration标签页,找到RCC,将High Speed Clock (HSE)设为Crystal/Ceramic Resonator。 - 配置时钟树:切换到
Clock Configuration标签页。我习惯先把HSE设为8MHz,然后通过PLL倍频到72MHz(对于STM32F103系列)。 - 设置APB分频:USART通常挂在APB2(最高72MHz)或APB1(最高36MHz)上。为了得到标准波特率,我一般让APB2时钟跑72MHz,APB1跑36MHz。
- LSE可选:如果你的蓝牙模块支持低功耗模式,或者你需要RTC定时唤醒,那就把LSE也打开。同样在RCC里,把
Low Speed Clock (LSE)设为Crystal/Ceramic Resonator。
我的小技巧:配置完时钟树后,点一下Resolve Clock Issues按钮。CubeMX会自动检查有没有冲突。如果某个外设的时钟源不对,它会标红提示你。别硬着头皮往下配,先把红色消掉。
4.2 USART外设参数设置
USART是蓝牙通信的物理层。参数设错了,数据就收不到。我见过有人把停止位设成2位,结果蓝牙模块那边是1位,通信直接失败。所以,咱们得把每个参数都搞清楚。
4.2.1 基本参数
| 参数 | 常用值 | 说明 |
|---|---|---|
| 波特率 | 9600 / 115200 / 460800 | 与蓝牙模块保持一致。HC-05默认9600,HC-08默认115200 |
| 数据位 | 8 | 蓝牙模块几乎都是8位数据位 |
| 停止位 | 1 | 1位停止位最通用 |
| 校验位 | None | 蓝牙模块通常无校验。如果环境干扰大,可以选Even |
4.2.2 配置步骤
- 在
Pinout & Configuration里找到USART1(或你选的那个),模式选Asynchronous。 - 在
Configuration标签页里,Parameter Settings下,把波特率设为115200。数据位8,停止位1,校验None。 - 硬件流控:一般蓝牙模块不需要CTS/RTS,所以
Flow Control选None。除非你用的是带流控的模块(比如某些BLE模块),否则别开。
注意:波特率不是越高越好。115200在10米内没问题,但如果你的板子和蓝牙模块之间线比较长,或者环境干扰大,降到9600更稳。我曾经在一个电机驱动项目里,因为没做屏蔽,460800波特率下丢包率超过30%。
4.3 DMA配置(可选但推荐)
DMA,说白了就是让数据自己走,不占用CPU。做蓝牙通信时,如果你要连续接收大量数据(比如OTA升级、传感器数据流),DMA几乎是必须的。否则CPU一直在中断里搬数据,啥也干不了。
4.3.1 什么时候用DMA?
- 接收不定长数据:用DMA+空闲中断,可以自动接收一帧数据,CPU只在帧结束时处理。
- 发送大数据块:比如通过蓝牙发送固件包,DMA发送不阻塞CPU。
- 低功耗场景:DMA传输时CPU可以进入睡眠模式。
4.3.2 配置步骤
- 在USART1的
Configuration里,切换到DMA Settings标签页。 - 点击
Add,添加两个DMA通道:一个用于发送(USART1_TX),一个用于接收(USART1_RX)。 - 方向:TX选
Memory To Peripheral,RX选Peripheral To Memory。 - 模式:我习惯把TX设为
Normal,RX设为Circular。这样接收缓冲区可以循环存数据,不会溢出。 - 数据宽度:都选
Byte。USART是8位的,别选Half Word或Word。
避坑指南:DMA的优先级不要设得比中断高。我曾经把DMA优先级设成最高,结果中断响应被延迟,导致蓝牙模块的AT指令超时。DMA优先级设成Medium就够了。
4.4 中断优先级配置
中断优先级是个容易翻车的地方。STM32用NVIC管理中断,优先级分为抢占优先级和子优先级。抢占优先级高的可以打断低的,子优先级只在同抢占优先级时有效。
4.4.1 配置原则
- USART中断:建议设成
Preemption Priority = 1,Sub Priority = 0。因为蓝牙通信对实时性有要求,不能等太久。 - DMA中断:如果用了DMA传输完成中断,优先级设成
Preemption Priority = 2,比USART低一级。 - SysTick中断:保持默认的
Preemption Priority = 0,别动它。HAL库的延时函数依赖它。
4.4.2 配置步骤
- 在CubeMX的
NVIC Settings标签页里,找到USART1 global interrupt,勾选Enabled。 - 在
NVIC Priority Group里,选4 bits for pre-emption priority, 0 bits for subpriority。这样所有优先级都是抢占优先级,简单粗暴。 - 把USART1中断的抢占优先级设为1,DMA中断设为2。
重要:如果多个外设共用同一个中断优先级,可能会出现中断丢失。我建议你给每个外设分配不同的抢占优先级。尤其是USART和定时器,别让它们抢同一个优先级。
4.5 本章知识体系总览
下面这张图把本章的配置逻辑串起来了。从时钟源开始,到USART参数,再到DMA和中断,每一步都有依赖关系。
4.6 生成代码与验证
所有配置完成后,点Project → Generate Code。CubeMX会生成HAL库代码。我建议你打开main.c,检查一下这几项:
MX_USART1_UART_Init()里的波特率是不是115200。HAL_UART_Receive_DMA()有没有被调用(如果你配了DMA)。- NVIC的优先级分组是不是
NVIC_PRIORITYGROUP_4。
快速验证方法:在main.c里加一行HAL_UART_Transmit(&huart1, (uint8_t*)"AT\r\n", 4, 1000);,然后烧录。用USB转串口工具接上PA9(TX)和PA10(RX),打开串口助手。如果收到"OK",说明配置没问题。