3. STM32工程模板搭建:用CubeMX打好地基

说实话,很多初学者一上来就急着写代码、移植系统,结果往往卡在时钟配置不对、串口打印不出来这种基础问题上。我个人习惯是,先把工程模板搭得扎扎实实,后面移植FreeRTOS时才不会手忙脚乱。

这一章我们就用STM32CubeMX生成基础工程,把时钟、GPIO、串口这些"基础设施"配好。说白了,就是给FreeRTOS准备一个干净、稳定的"家"。

3.1 为什么非要用CubeMX?

你可能会问:我手写寄存器不行吗?当然可以。但我想说个真实经历——我曾经在一个项目里手动配置STM32F103的时钟树,结果因为PLL分频系数算错,芯片死活跑不到72MHz。折腾了两天才发现是配置问题。从那以后,但凡新项目起步,我都先用CubeMX生成框架。

CubeMX的好处很明显:

  • 图形化配置:时钟树、外设引脚一目了然
  • 自动生成初始化代码:避免手写遗漏
  • 中间件集成:FreeRTOS、FATFS等可以直接勾选
  • 引脚冲突检查:防止多个外设抢同一个引脚
我的建议:CubeMX生成的代码可以作为"只读"部分,不要手动修改。你自己的业务逻辑写在单独的用户代码区(就是那些 /* 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

具体操作步骤:

  1. 在HSE处选择Crystal/Ceramic Resonator
  2. 在PLL Source选择HSE
  3. 设置HCLK为72MHz,系统会自动计算PLL参数
  4. 确认APB1 Prescaler为/2,APB2为/1
注意:APB1最高只能到36MHz,如果配成72MHz,挂载在APB1上的外设(USART2/3、I2C、SPI2等)会工作异常。我曾经见过有人把APB1配到72MHz,结果I2C通信时好时坏,查了三天才发现是时钟过频。

配置完成后,时钟树应该长这样:

HSE(8MHz) → PLL(x9) → SYSCLK(72MHz) → HCLK(72MHz)
                                        → APB1(36MHz)
                                        → APB2(72MHz)

3.4 GPIO配置——点亮第一盏灯

GPIO配置相对简单,但有几个细节要注意。我们以PC13(板载LED)为例:

  1. 在Pinout & Configuration标签页,找到PC13
  2. 左键点击,选择GPIO_Output
  3. 在右侧GPIO Output Level选择High(默认熄灭)
  4. Maximum output speed选择Low(LED不需要高速)

这里有个小坑:很多STM32开发板的LED是低电平点亮的。也就是说,GPIO输出0时LED亮,输出1时LED灭。如果你发现灯不亮,先检查一下原理图。

经验之谈:调试阶段,我习惯把LED配置成推挽输出、初始状态为关闭。这样程序跑起来后,可以用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引脚和电平极性。如果串口没输出,检查波特率、引脚连接、以及时钟配置是否正确。

调试小技巧:如果串口输出乱码,90%是波特率不匹配。用示波器看TX引脚的波形,测量一个bit的宽度,1/bit_width就是实际波特率。我曾经用这个方法抓到过因为时钟配置错误导致波特率偏差30%的情况。

3.8 本章知识结构

下面这张图总结了本章的核心流程,从CubeMX新建工程到验证基础功能,每一步都是为FreeRTOS移植做准备:

STM32工程模板搭建流程 1. 新建工程 2. 时钟配置 (HSE→PLL→72MHz) 3. GPIO配置 (PC13 LED) 4. 串口配置 (USART1 115200) 5. 生成代码 6. 验证 (LED闪烁+串口打印) 关键检查点 • APB1 ≤ 36MHz • LED电平极性确认 • 串口波特率匹配

到这步,你的STM32工程模板就搭好了。时钟稳定、GPIO可控、串口能打印——这三个条件满足后,我们就可以放心地往这个工程里移植FreeRTOS了。