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时钟、低功耗定时器 |
2.2 PLL倍频:把低频“翻”上去
PLL(锁相环)的作用,就是把一个较低的输入频率,通过倍频系数翻上去。比如8 MHz的HSE,经过PLL可以变成72 MHz、168 MHz甚至更高。
PLL的配置路径一般是:
- 选择PLL输入源:HSE或HSI,通常选HSE
- 配置分频系数(M):先把输入频率降下来,比如8 MHz ÷ 2 = 4 MHz
- 配置倍频系数(N):把低频翻上去,比如4 MHz × 36 = 144 MHz
- 配置输出分频(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 |
2.4 时钟树结构图
下面这张图,是我自己画的一个简化版时钟树。它把时钟源、PLL、总线分频的关系串起来了。
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的负载电容,波形立刻正常了。嗯,晶振的匹配电容真的不能随便抄。