4. STM32硬件基础

好,咱们正式开始动手了。

这一章的目标很明确——用STM32CubeMX生成一个干净的基础工程。时钟、GPIO、串口,这三样配好了,后面移植FreeRTOS才有根。

我见过不少新手一上来就急着写代码,结果时钟没配对,串口打印乱码,查了半天才发现是HSE倍频算错了。嗯,咱们先把地基打牢。

4.1 为什么选择CubeMX?

说白了,CubeMX就是ST官方给的图形化配置工具。你点点鼠标,它帮你生成初始化代码。

我个人习惯是:时钟树手动配,外设按需开,中间件后面再加。这样生成的工程干净,没有冗余代码。

核心原则:CubeMX只负责生成初始化代码,业务逻辑你自己写。别指望它帮你搞定任务调度。

4.2 新建工程与芯片选择

打开CubeMX,点击“New Project”。

在Board Selector里,我一般直接搜芯片型号。咱们课程用的是STM32F103C8T6,也就是大家常说的“蓝 pill”核心板。

  • Series: STM32F1
  • Line: STM32F103
  • Package: LQFP48
  • MCU: STM32F103C8Tx

双击选中,工程就创建好了。

小技巧:如果你用的是开发板,可以直接在Board Selector里选板子型号,CubeMX会自动配好板载外设的引脚。但咱们为了学透,还是从芯片级别开始。

4.3 配置时钟树——核心中的核心

时钟是MCU的心脏。配错了,整个系统跑起来都是歪的。

我曾经在一个项目里,因为PLL分频系数设错,导致USB时钟不准,PC死活不识别设备。查了两天才找到原因……从那以后,我每次配时钟都会对着数据手册核对一遍。

进入Clock Configuration选项卡,咱们按以下步骤来:

  1. 选择时钟源: HSE(高速外部晶振)——咱们板子上一般焊的是8MHz晶振。
  2. 配置PLL: HSE 8MHz → PLLMUL x9 → SYSCLK 72MHz。
  3. APB1分频: /2 → 36MHz(注意,APB1最高只能到36MHz)。
  4. APB2分频: /1 → 72MHz。

注意:STM32F103的最高主频是72MHz。别贪心设到128MHz,芯片会直接死给你看。我见过有人把PLL倍频设到16,结果芯片发热严重,程序跑飞。

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

HSE (8MHz) → PLLSRC (HSE) → PLLMUL (x9) → SYSCLK (72MHz)
                                    → AHB Prescaler (/1) → HCLK (72MHz)
                                    → APB1 Prescaler (/2) → PCLK1 (36MHz)
                                    → APB2 Prescaler (/1) → PCLK2 (72MHz)

你可以在CubeMX里看到所有时钟频率都变成了蓝色——蓝色表示合法配置。

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

GPIO配置其实很简单,但有个坑我得提醒你:默认引脚状态。

我刚开始做项目时,配了一个推挽输出的引脚,结果上电瞬间它输出了一小段高电平,把后面的电路给烧了。后来我才知道,CubeMX生成的代码里,GPIO初始化是在系统时钟配置之后才执行的。也就是说,在初始化之前,引脚是默认浮空输入状态。

咱们这次配一个LED控制引脚:

  • 引脚: PC13(板载LED)
  • 输出模式: Push-Pull
  • 上拉/下拉: No pull-up, no pull-down
  • 初始电平: High(LED灭)
  • 速度: Low(控制LED不需要高速)

在Pinout & Configuration选项卡里,找到PC13,左键点击,选择GPIO_Output。然后在右侧的GPIO设置里,把Maximum output speed改成Low。

为什么选Low? 高速输出会增加功耗和EMI。控制LED这种低频信号,Low完全够用。省电又干净。

4.5 配置串口——调试的命脉

做嵌入式开发,串口就是你的眼睛。没有串口打印,你连程序跑到哪了都不知道。

咱们用USART1,挂在PA9(TX)和PA10(RX)上。

配置步骤:

  1. 在Pinout里找到USART1,模式选Asynchronous。
  2. CubeMX会自动把PA9和PA10配成USART1的TX和RX。
  3. 进入Parameter Settings:
    • Baud Rate: 115200
    • Word Length: 8 Bits
    • Parity: None
    • Stop Bits: 1

这里有个细节:NVIC选项卡里,记得使能USART1全局中断。虽然咱们这次用轮询方式打印,但后面移植FreeRTOS时,串口中断是少不了的。

波特率陷阱: 115200在8MHz HSE下是准确的。但如果你换了晶振频率,比如用12MHz,115200可能产生较大误差。我建议你配完后点一下CubeMX里的“Calculate”按钮,看看实际波特率误差是否在2%以内。超过2%,通信就可能丢数据。

4.6 生成工程代码

配置完了,该生成代码了。

点击Project → Generate Code,在弹窗里设置:

  • Project Name: stm32f103_freertos_base
  • Project Location: 自己选个目录
  • Toolchain / IDE: MDK-ARM V5
  • Firmware Package: 选你安装的版本,我用的1.8.5

然后勾选Generate peripheral initialization as a pair of .c/.h files per peripheral——这样每个外设单独一个文件,结构清晰。

点击GENERATE CODE,几秒钟后工程就生成了。

4.7 验证工程——写个简单的测试

打开生成的MDK工程,在main.c的main()函数里,咱们加几行测试代码:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  // 串口打印测试
  char msg[] = "STM32 FreeRTOS Base Project OK!\r\n";
  HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);

  while (1)
  {
    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    HAL_Delay(500);
  }
}

编译下载,打开串口助手(115200, 8N1),你应该能看到:

STM32 FreeRTOS Base Project OK!

同时板载LED以500ms间隔闪烁。

验证通过的标准: 串口打印不乱码,LED闪烁稳定。这两样对了,说明时钟、GPIO、串口都配好了。

4.8 本章知识结构图

下面这张图帮你理清整个基础工程的配置流程:

STM32基础工程创建流程 1. 新建工程 2. 配置时钟树 3. 配置GPIO 4. 配置串口 5. 生成工程代码 6. 验证:串口+LED 验证通过标准:串口打印正常 + LED闪烁稳定 → 基础工程创建完成

4.9 避坑总结

最后,我把这些年踩过的坑列出来,你配工程时对照着检查:

配置项 常见错误 正确做法
时钟源 选了HSI,精度不够 用HSE外部晶振
PLL倍频 倍频后超过72MHz 8MHz x9 = 72MHz
APB1分频 忘了分频,导致外设超频 APB1必须 ≤36MHz
GPIO速度 全设成High,功耗高 按需选择,LED用Low
串口波特率 晶振频率与波特率不匹配 用CubeMX计算实际误差
NVIC使能 忘了开中断 USART1中断必须使能