3. STM32CubeMX无线工程配置:时钟树配置、外设初始化(SPI/UART)、DMA配置、中断优先级设置
好,咱们正式开始动手配置工程。上一章我们把CubeMX环境搭好了,也选好了芯片。这一章,我们要把无线通信的“骨架”搭起来——时钟、外设、DMA、中断,一个都不能少。
我见过不少新手,上来就写代码,结果无线模块死活不工作。查了半天,发现是SPI时钟极性配反了,或者DMA通道没使能。说白了,硬件初始化没做好,后面全是白费功夫。
3.1 时钟树配置——无线模块的“心跳”
无线模块对时钟很敏感。比如常见的nRF24L01,SPI时钟最高支持10MHz,但你要是用内部RC振荡器,频率漂移了,通信就会丢包。我建议,无线工程一律用外部晶振。
在CubeMX的Clock Configuration页面,按以下步骤来:
- 选择HSE(高速外部晶振):在RCC设置里,把HSE选为Crystal/Ceramic Resonator。别选Bypass,那是给有源晶振用的。
- 配置PLL:一般STM32F1系列,我习惯把系统时钟配到72MHz。HSE通常是8MHz,那就设PLL倍频为9x(8*9=72)。
- APB1/APB2分频:APB1最高36MHz,APB2最高72MHz。无线模块如果挂在SPI1(在APB2上),那SPI时钟源就是72MHz。后面配SPI分频时,心里要有数。
- 确认外设时钟:在左侧Pinout & Configuration里,使能SPI和USART的时钟。CubeMX会自动帮你勾上,但最好检查一下。
关键参数速查表
| 时钟源 | 推荐值 | 说明 |
|---|---|---|
| HSE | 8MHz(外部晶振) | 精度高,无线通信首选 |
| SYSCLK | 72MHz | F1系列主流频率 |
| APB1 Timer | 72MHz | 注意APB1总线本身是36MHz,但定时器可以倍频 |
| APB2 | 72MHz | SPI1、USART1都在这个总线 |
我的习惯:配完时钟后,点一下“Apply”,然后看一眼右上角的“HCLK”数值。如果显示72MHz,说明PLL配置没问题。如果显示8MHz,那就是HSE没选对,或者PLL没使能。
3.2 外设初始化——SPI与UART
无线模块和MCU通信,最常用的就是SPI。调试时,UART用来打印日志。这两个外设,我建议在CubeMX里一次性配好,别等到写代码时再改。
3.2.1 SPI配置(以SPI1为例)
无线模块通常是SPI从机,MCU做主机。配置要点如下:
- Mode:选Full-Duplex Master。半双工也能用,但全双工更灵活。
- Hardware NSS Signal:我一般用Disable,然后用一个普通GPIO做片选。这样控制更灵活,不容易出问题。
- Data Size:8 Bits。无线模块的寄存器都是8位操作。
- Clock Polarity (CPOL) 和 Clock Phase (CPHA):这个要看无线模块的数据手册。nRF24L01是Mode 0(CPOL=Low, CPHA=1st Edge)。SX1278可能是Mode 0或Mode 1。我建议先设为Mode 0,如果通信失败,再试Mode 1。
- Prescaler:先设大一点,比如64分频(1.125MHz),确保通信稳定。等调通了,再逐步提高速度。
我曾经踩过的坑:有一次我把CPOL和CPHA配反了,无线模块能读到数据,但全是乱码。用逻辑分析仪一看,时钟和数据边沿对不上。后来老老实实对着数据手册改过来,一次通过。所以,SPI模式一定要和模块匹配,不能想当然。
3.2.2 UART配置(调试用)
调试信息输出,我推荐用USART1,因为它挂在APB2上,时钟高,不容易丢数据。
- Mode:Asynchronous(异步模式)。
- Baud Rate:115200。这个速度够快,而且大部分串口工具都支持。
- Word Length:8 Bits。
- Parity:None。
- Stop Bits:1。
配好后,记得在GPIO Settings里确认TX和RX的引脚没有冲突。USART1的TX通常是PA9,RX是PA10。
3.3 DMA配置——让数据飞起来
无线通信经常要收发大量数据。如果用CPU轮询,那CPU就被占死了,啥也干不了。DMA就是干这个的——它帮你搬数据,搬完了通知你一声。
我建议,SPI的收发都用DMA。UART的发送也可以用DMA,接收看情况。
3.3.1 SPI DMA配置步骤
- 在DMA Settings标签页里,点击Add。
- 选择SPI1_RX,然后SPI1_TX。注意顺序,先加RX,再加TX。
- Direction:RX选Peripheral To Memory,TX选Memory To Peripheral。
- Mode:选Normal。Circular模式适合连续数据流,无线通信一般用Normal,一次收发一包。
- Data Width:Byte。SPI数据是8位,DMA也配成8位。
DMA配置速查
| 通道 | 方向 | Mode | Data Width |
|---|---|---|---|
| SPI1_RX | Periph to Mem | Normal | Byte |
| SPI1_TX | Mem to Periph | Normal | Byte |
| USART1_TX | Mem to Periph | Normal | Byte |
小技巧:DMA的优先级,我一般保持默认(Low)。除非你有多个DMA同时工作,否则不用改。优先级设太高,反而可能影响其他中断。
3.4 中断优先级设置——别让“打架”
无线通信里,中断是家常便饭。SPI传输完成要中断,UART收到数据要中断,无线模块的IRQ引脚也要中断。如果优先级没配好,就会出现“中断嵌套打架”的情况——高优先级中断一直打断低优先级,导致低优先级任务饿死。
我建议,用NVIC(嵌套向量中断控制器)来统一管理。CubeMX里,在NVIC Settings标签页操作。
3.4.1 优先级分组
STM32支持4位优先级,可以分成抢占优先级和子优先级。我习惯用NVIC_PriorityGroup_2:2位抢占,2位子优先级。这样有4级抢占,每级有4个子优先级,够用了。
3.4.2 具体分配建议
- 最高抢占优先级(0):留给系统滴答定时器(SysTick)和PendSV。别动它们。
- 高抢占优先级(1):无线模块的IRQ引脚中断。这个要快,因为无线模块可能随时有数据来。
- 中抢占优先级(2):SPI和UART的DMA传输完成中断。DMA搬完数据,需要及时处理。
- 低抢占优先级(3):UART接收中断(如果不用DMA的话)。这个可以慢一点,因为数据在寄存器里不会丢。
我曾经犯过的错:有一次我把无线模块的IRQ中断优先级设成了最低,结果模块发来数据,MCU正在处理一个UART中断,等UART处理完,无线模块的数据已经超时了。后来我把IRQ中断提到最高,问题解决。所以,实时性要求高的中断,优先级一定要高。
3.5 知识体系总览
这一章的内容比较多,我画了一张图,帮你理清关系。
这张图把这一章的核心逻辑串起来了。你配置的时候,按这个顺序来,基本不会漏。
3.6 生成代码与验证
所有配置完成后,点击GENERATE CODE。CubeMX会生成一个完整的工程。我建议,生成后先编译一次,确保没有语法错误。
然后,打开main.c,找到MX_GPIO_Init()、MX_SPI1_Init()、MX_USART1_UART_Init()、MX_DMA_Init()这些函数。看一眼代码,确认配置和你在CubeMX里设的一致。
验证小方法:在main()函数里,加一句HAL_UART_Transmit(&huart1, (uint8_t*)"Hello\r\n", 7, 100);。下载到板子,用串口助手看能不能收到。如果能收到,说明UART和时钟都对了。这是最基础的“冒烟测试”。