2. CubeMX初始化配置(上):新建工程、选择芯片、配置时钟树与调试接口

好,咱们正式开始动手。上一章我们把ADC多通道采样的整体框架聊清楚了,这一章就进入实操——打开CubeMX,把工程骨架搭起来。

我个人习惯,做任何STM32项目,第一步不是写代码,而是先把CubeMX的配置跑通。你想想看,时钟配错了,后面所有外设都是白搭。我曾经见过一个同事,花了两天调一个I2C传感器,最后发现是HSE的起振电容没配好,时钟根本没跑起来……嗯,这种坑咱们得绕过去。

2.1 新建工程与芯片选型

打开STM32CubeMX,界面很清爽。点击“New Project”,就会弹出芯片选型窗口。

这里有个小技巧:别直接搜型号全称,比如你要用STM32F103C8T6,输入“STM32F103C8”就够了,下拉列表会帮你补全。我一般还会勾上“Show only recommended parts for new design”,把那些已经停产或者不推荐的型号过滤掉。

选好芯片后,双击就能进入配置界面。咱们这门课,我以STM32F103C8T6为例,这是最经典的“蓝色药丸”板子,大家手头应该都有。如果你用的是其他型号,比如F4或者G0系列,操作逻辑完全一样,只是外设资源多少的区别。

选型建议: 做ADC多通道采样,建议选至少10个以上GPIO引脚的芯片,因为每个ADC通道都要占用一个引脚。F103C8T6有10个ADC通道,刚好够用。

2.2 配置调试接口(SWD)

进到配置界面后,第一件事——配调试接口。别急着点时钟树,先把“脚”站稳了。

在Pinout & Configuration标签页,找到SYS(System Core)分类,点开,选择Debug。下拉菜单里会有几个选项:

调试模式 占用引脚 适用场景
Serial Wire (SWD) PA13 (SWDIO), PA14 (SWCLK) 大多数项目,省引脚
JTAG (5-pin) PA13, PA14, PA15, PB3, PB4 需要JTAG调试时
None 不占用 量产固件,不调试

我强烈建议选Serial Wire。为什么?JTAG占5个引脚,对于F103这种小封装芯片来说太奢侈了。SWD只用两根线(SWDIO和SWCLK),而且速度足够快,配合ST-Link/V2,调试体验一点不差。

注意: 如果你选了“None”,下载完程序后,芯片的SWD引脚会被释放为普通GPIO。下次想再烧录?对不起,得先拉高BOOT0引脚进系统存储器模式,用串口ISP擦除芯片。我当年第一次做项目就踩过这个坑,程序跑飞了,死活连不上调试器,最后只能飞线拉BOOT0……那叫一个狼狈。

2.3 配置时钟树(HSE + PLL)

调试接口配好了,接下来是重头戏——时钟树。点顶部的Clock Configuration标签页,你会看到一张复杂的时钟树图。别慌,咱们一步步来。

2.3.1 选择时钟源

STM32F103有两个外部时钟源:

  • HSE(高速外部晶振):通常接8MHz晶振,精度高,适合做PLL的输入。
  • LSE(低速外部晶振):32.768kHz,给RTC用的,咱们暂时不管。

在时钟树图上,找到HSE的输入框,把值改成8(单位MHz)。然后,在HSE后面的多路选择器上,点一下,选择Crystal/Ceramic Resonator。这样CubeMX就知道你用的是外部晶振,而不是有源时钟。

关键点: 如果你用的是开发板,比如“蓝色药丸”,板载晶振就是8MHz。如果是自己画的板子,一定要确认晶振频率,别想当然。

2.3.2 配置PLL倍频

HSE的8MHz直接给系统时钟?太慢了。STM32F103最高可以跑到72MHz,所以我们需要用PLL(锁相环)来倍频。

在时钟树图上,找到PLL模块。它的输入源选择HSE(默认可能是HSI,记得改)。然后设置倍频系数:

  • PLL Mul (倍频系数):设为x9。8MHz × 9 = 72MHz。

为什么是x9?因为F103的PLL输出范围是16~72MHz,8×9=72刚好在范围内。如果你设成x10,那就是80MHz,超频了。芯片可能能跑,但稳定性没保证。我建议别超,除非你很清楚自己在做什么。

2.3.3 配置系统时钟与总线分频

PLL输出72MHz后,需要分配给各个总线:

  • SYSCLK(系统时钟):选择PLLCLK作为源。
  • AHB Prescaler:设为/1,这样HCLK = 72MHz。
  • APB1 Prescaler:设为/2,APB1时钟 = 36MHz。注意,APB1最高只能到36MHz,超过会出问题。
  • APB2 Prescaler:设为/1,APB2时钟 = 72MHz。ADC就是挂在APB2上的,所以ADC的时钟源是72MHz。

配完之后,时钟树图上应该所有参数都变成蓝色(表示在合法范围内)。如果某个参数变红,说明超限了,需要调整分频系数。

一个小细节: ADC的时钟不是直接等于APB2时钟。ADC内部还有一个分频器,可以再分频。这个我们后面配ADC参数时会细讲。现在先把系统时钟跑稳。

2.4 验证配置结果

配置完成后,点一下Project Manager标签页,设置工程名、保存路径、IDE类型(我选MDK-ARM V5)。然后点GENERATE CODE,CubeMX就会生成初始化代码。

生成完成后,打开工程,找到main.c里的SystemClock_Config()函数。你会看到类似这样的代码:

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // 配置HSE和PLL
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  // 配置系统时钟、AHB、APB1、APB2分频
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

看到FLASH_LATENCY_2了吗?当系统时钟超过48MHz时,Flash需要插入2个等待周期,否则CPU取指令会出错。CubeMX会自动算好这个值,不用我们操心。

2.5 本章知识结构图

下面这张图,把咱们这一章的配置流程串起来了。你可以把它当作一个检查清单,每次新建工程时对照着走一遍。

CubeMX初始化配置流程 CubeMX 初始化配置流程 新建工程 选择芯片 (F103C8T6) 配置调试接口 (SYS → Serial Wire) 配置时钟树 (HSE + PLL → 72MHz) 生成代码 & 验证 提示:每次新建工程都按这个流程走一遍,避免遗漏关键配置

2.6 避坑指南

最后,分享几个我实际项目中踩过的坑:

  • HSE起振失败:如果你发现程序卡在HAL_RCC_OscConfig()里出不来,八成是晶振没起振。检查晶振负载电容是否匹配,或者用示波器量一下OSC_IN引脚有没有波形。
  • PLL超频:F103的PLL输出不能超过72MHz。如果你设了x10倍频(80MHz),芯片可能能跑,但ADC采样值会飘,串口数据会乱码。别问我怎么知道的……
  • SWD引脚被占用:如果你在代码里把PA13或PA14配置成了普通GPIO,下次烧录就废了。解决办法:拉高BOOT0,上电复位,用串口ISP擦除。或者,在CubeMX里把SWD引脚的功能锁定,不让用户代码修改。