3. STM32工程模板搭建:用CubeMX打好地基
说实话,很多初学者一上来就急着写代码、移植系统,结果往往卡在时钟配置不对、串口打印不出来这种基础问题上。我个人习惯是,先把工程模板搭得扎扎实实,后面移植FreeRTOS时才不会手忙脚乱。
这一章我们就用STM32CubeMX生成基础工程,把时钟、GPIO、串口这些"基础设施"配好。说白了,就是给FreeRTOS准备一个干净、稳定的"家"。
3.1 为什么非要用CubeMX?
你可能会问:我手写寄存器不行吗?当然可以。但我想说个真实经历——我曾经在一个项目里手动配置STM32F103的时钟树,结果因为PLL分频系数算错,芯片死活跑不到72MHz。折腾了两天才发现是配置问题。从那以后,但凡新项目起步,我都先用CubeMX生成框架。
CubeMX的好处很明显:
- 图形化配置:时钟树、外设引脚一目了然
- 自动生成初始化代码:避免手写遗漏
- 中间件集成:FreeRTOS、FATFS等可以直接勾选
- 引脚冲突检查:防止多个外设抢同一个引脚
/* USER CODE BEGIN */ 和 /* USER CODE END */ 之间的区域)。这样下次重新生成时,你的代码不会被覆盖。
3.2 新建工程与芯片选择
打开CubeMX,点击"New Project"。在芯片选择界面,我一般直接搜具体型号,比如我们课程用的STM32F103C8T6。
选芯片时注意几个点:
- 封装:C8T6是LQFP48封装,引脚数有限
- Flash大小:64KB,跑FreeRTOS+基础外设够用
- RAM大小:20KB,任务栈分配时要精打细算
双击选中后,进入配置界面。嗯,这里要提醒一下:如果你用的是国产兼容芯片(比如GD32、APM32),选型时最好选原厂对应型号,有些外设地址有差异。
3.3 配置时钟树——芯片的"心跳"
时钟配置是嵌入式开发里最容易翻车的地方之一。我记得有个同事,调试时串口数据全是乱码,查了半天发现是时钟配错了,波特率实际跑了115200的两倍。
进入Clock Configuration标签页,我们的目标是:
- HSE:外部8MHz晶振
- PLL倍频:9倍,得到72MHz
- APB1:36MHz(最大允许36MHz)
- APB2:72MHz
具体操作步骤:
- 在HSE处选择Crystal/Ceramic Resonator
- 在PLL Source选择HSE
- 设置HCLK为72MHz,系统会自动计算PLL参数
- 确认APB1 Prescaler为/2,APB2为/1
配置完成后,时钟树应该长这样:
HSE(8MHz) → PLL(x9) → SYSCLK(72MHz) → HCLK(72MHz)
→ APB1(36MHz)
→ APB2(72MHz)
3.4 GPIO配置——点亮第一盏灯
GPIO配置相对简单,但有几个细节要注意。我们以PC13(板载LED)为例:
- 在Pinout & Configuration标签页,找到PC13
- 左键点击,选择GPIO_Output
- 在右侧GPIO Output Level选择High(默认熄灭)
- Maximum output speed选择Low(LED不需要高速)
这里有个小坑:很多STM32开发板的LED是低电平点亮的。也就是说,GPIO输出0时LED亮,输出1时LED灭。如果你发现灯不亮,先检查一下原理图。
3.5 串口配置——调试的"眼睛"
串口是嵌入式开发最重要的调试手段。没有串口打印,你就像蒙着眼睛写代码。我们配置USART1:
- 引脚:PA9(TX)、PA10(RX)
- 模式:Asynchronous(异步)
- 波特率:115200
- 数据位:8
- 停止位:1
- 校验:None
在NVIC Settings标签页,记得勾选USART1 global interrupt。虽然我们暂时用轮询方式收发,但后面移植FreeRTOS后,串口中断是必不可少的。
配置完成后,生成代码。CubeMX会帮你生成MX_USART1_UART_Init()函数,以及引脚复用的GPIO初始化。
3.6 生成工程与代码结构
点击Project → Generate Code,选择MDK-ARM(如果你用Keil)或STM32CubeIDE。我个人推荐用STM32CubeIDE,因为它是免费的,而且和CubeMX无缝集成。
生成的工程结构大致如下:
| 文件夹/文件 | 说明 |
|---|---|
Core/Inc/ |
头文件,包括main.h、gpio.h、usart.h等 |
Core/Src/ |
源文件,包括main.c、gpio.c、usart.c等 |
Drivers/ |
HAL库和LL库的源码 |
.ioc文件 |
CubeMX的配置文件,双击可重新打开 |
在main.c中,你会看到HAL_Init()、SystemClock_Config()、MX_GPIO_Init()、MX_USART1_UART_Init()这些函数调用。这就是我们后面移植FreeRTOS的基础。
3.7 验证工程——跑个"Hello World"
工程生成后,先别急着移植系统。我习惯先验证一下基础功能是否正常。在main()函数的while(1)循环里加几行测试代码:
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);
char msg[] = "Hello from STM32!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
编译下载后,你应该能看到:
- 板载LED以1秒周期闪烁
- 串口助手每隔500ms收到"Hello from STM32!"
如果LED不闪,检查GPIO引脚和电平极性。如果串口没输出,检查波特率、引脚连接、以及时钟配置是否正确。
3.8 本章知识结构
下面这张图总结了本章的核心流程,从CubeMX新建工程到验证基础功能,每一步都是为FreeRTOS移植做准备:
到这步,你的STM32工程模板就搭好了。时钟稳定、GPIO可控、串口能打印——这三个条件满足后,我们就可以放心地往这个工程里移植FreeRTOS了。