2. 时钟树架构:时钟源、PLL倍频与总线配置

时钟,是单片机的“心跳”。没有它,CPU、外设、定时器全都停摆。我刚开始学单片机时,总觉得时钟配置就是随便选个频率跑起来就行。直到有一次,我做的数据采集系统在高速采样时频繁丢包,查了三天才发现是APB2总线时钟没配好,ADC采样时钟超限了。从那以后,我每次建项目第一件事就是先把时钟树理清楚。

说白了,时钟树就是一套“时钟分发网络”。你要知道源头在哪、怎么倍频、怎么分频、最终送到哪个模块。今天我们就把它彻底拆开。

2.1 四大时钟源:HSI / HSE / LSI / LSE

STM32这类主流MCU,内部通常有四个独立的时钟源。我习惯把它们分成两组:

时钟源 全称 典型频率 精度 主要用途
HSI 高速内部振荡器 8 MHz / 16 MHz ±1% ~ ±2% 系统启动默认时钟、PLL输入
HSE 高速外部晶振 4 ~ 26 MHz ±0.01% (晶振决定) 高精度系统时钟、PLL输入
LSI 低速内部振荡器 32 kHz / 40 kHz ±5% ~ ±10% 独立看门狗、RTC校准
LSE 低速外部晶振 32.768 kHz ±0.001% (晶振决定) RTC时钟、低功耗定时器
我的习惯:量产项目我几乎不用HSI做主时钟。温度一变,HSI频率会漂,UART波特率就跟着偏。HSE加PLL倍频,才是靠谱的选择。

2.2 PLL倍频:把低频“翻”上去

PLL(锁相环)的作用,就是把一个较低的输入频率,通过倍频系数翻上去。比如8 MHz的HSE,经过PLL可以变成72 MHz、168 MHz甚至更高。

PLL的配置路径一般是:

  1. 选择PLL输入源:HSE或HSI,通常选HSE
  2. 配置分频系数(M):先把输入频率降下来,比如8 MHz ÷ 2 = 4 MHz
  3. 配置倍频系数(N):把低频翻上去,比如4 MHz × 36 = 144 MHz
  4. 配置输出分频(P/Q/R):得到最终需要的频率

公式很简单:PLL输出 = (输入频率 / M) × N / P

避坑指南:我曾经在F103上把PLL倍频系数设到72,结果芯片直接不启动。后来查手册才发现,PLL输出频率不能超过72 MHz。每个型号的PLL范围都不一样,配之前一定看数据手册的“Electrical characteristics”章节。

2.3 AHB / APB1 / APB2 总线时钟配置

时钟源经过PLL倍频后,得到的是系统时钟(SYSCLK)。但CPU、内存、外设跑的时钟频率不一样,所以需要分频。

典型的时钟分发路径是这样的:

  • SYSCLK → 直接供给CPU核心
  • AHB总线:SYSCLK经过AHB预分频器,得到HCLK。CPU、内存、DMA都挂在这条总线上
  • APB1总线:HCLK经过APB1预分频器,得到PCLK1。低速外设(USART2/3、SPI2/3、I2C1/2)挂在这条总线上
  • APB2总线:HCLK经过APB2预分频器,得到PCLK2。高速外设(GPIO、USART1、SPI1、ADC)挂在这条总线上

你想想看,如果APB1的时钟配得太高,挂在上面的低速外设可能工作不稳定。反过来,APB2时钟太低,ADC采样率就上不去。

总线 最大频率(F103为例) 挂载的主要外设 分频系数选项
AHB 72 MHz CPU、Flash、DMA、SRAM 1, 2, 4, 8, 16, 64, 128, 256, 512
APB1 36 MHz USART2/3、SPI2/3、I2C1/2、TIM2~7 1, 2, 4, 8, 16
APB2 72 MHz GPIO、USART1、SPI1、ADC、TIM1/8 1, 2, 4, 8, 16
注意:APB1的最大频率通常是系统时钟的一半。如果你把SYSCLK设到72 MHz,APB1分频系数至少得是2,否则外设可能工作异常。我见过有人把APB1设成不分频,结果I2C通信一直出错。

2.4 时钟树结构图

下面这张图,是我自己画的一个简化版时钟树。它把时钟源、PLL、总线分频的关系串起来了。

STM32 时钟树架构(简化版) HSI (8 MHz) HSE (8 MHz) LSI (40 kHz) LSE (32.768 kHz) 时钟选择 PLL 倍频 SYSCLK AHB 分频 APB1 分频 APB2 分频 时钟源 倍频/分频 总线时钟

2.5 实战:配置一个72 MHz系统时钟

以STM32F103为例,我们配一个72 MHz的系统时钟。HSE用8 MHz晶振,PLL倍频到72 MHz,AHB不分频,APB1二分频,APB2不分频。

// 1. 开启HSE并等待稳定
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));

// 2. 配置Flash等待周期(72 MHz需要2个等待周期)
FLASH->ACR = FLASH_ACR_LATENCY_2;

// 3. 配置AHB、APB1、APB2分频
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;   // AHB不分频
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;  // APB1二分频 → 36 MHz
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;  // APB2不分频 → 72 MHz

// 4. 配置PLL:8 MHz / 1 * 9 = 72 MHz
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE;  // PLL源选HSE
RCC->CFGR |= RCC_CFGR_PLLMULL9;    // 倍频系数9

// 5. 开启PLL并等待稳定
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));

// 6. 切换系统时钟到PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

小技巧:配置完时钟后,我习惯用MCO引脚把系统时钟输出到示波器上确认一下。在F103上,把PA8配置为复用推挽输出,然后设置RCC->CFGR的MCO位为PLL/2,就能看到36 MHz的方波。一眼就能判断时钟有没有配对。

2.6 常见配置错误与排查

我总结了几种最常见的时钟配置翻车现场:

  • PLL输出超限:倍频系数设太大,芯片直接不启动。解决办法是用ST-Link的“Connect under reset”模式擦除芯片
  • APB1时钟超36 MHz:I2C、USART等外设工作异常。检查分频系数是否至少为2
  • Flash等待周期不够:程序跑飞或执行异常。72 MHz必须配2个等待周期
  • HSE起振失败:晶振负载电容不匹配或PCB走线太长。换小电容或改用HSI先让芯片跑起来

我曾经踩过的坑:有一次我换了PCB板厂的晶振型号,没改匹配电容,结果HSE死活不起振。系统一直卡在while(!HSERDY)里。后来用示波器量晶振引脚,发现振幅只有200 mV。换了15 pF的负载电容,波形立刻正常了。嗯,晶振的匹配电容真的不能随便抄。