你有没有遇到过:UART波特率9600发出来却是乱码?定时器中断频率不对?
十有八九是时钟配置错了。这篇把STM32时钟树从HSE讲到APB,帮你彻底搞懂。
一、为什么时钟这么重要?
STM32的每个外设都依赖时钟工作:
- UART波特率 = APB时钟 / 分频
- I2C/SPI速率 = APB时钟 / 分频
- 定时器中断频率 = APB时钟 / (PSC+1)(ARR+1)
- ADC转换时间 = ADC时钟相关
时钟配错了,所有外设的时序全错。
二、四个时钟源
STM32有4个时钟源:
| 时钟源 | 简称 | 频率 | 精度 | 用途 |
|---|---|---|---|---|
| 内部高速RC | HSI | 8MHz/16MHz | ±1% | 上电默认,精度差 |
| 外部高速晶振 | HSE | 4-26MHz | ±20ppm | 主力时钟,精度高 |
| 内部低速RC | LSI | 32kHz-40kHz | ±10% | 看门狗、RTC |
| 外部低速晶振 | LSE | 32.768kHz | ±20ppm | RTC精准计时 |
主力用HSE(外部晶振)。HSI精度差(±1%),高波特率通信可能出错。
三、时钟树(以STM32F103为例)
HSE 8MHz ──→ PLL倍频 ──→ SYSCLK(72MHz) ──→ AHB分频 ──→ HCLK(72MHz)
> **PLL是什么?** PLL(Phase-Locked Loop,锁相环)是STM32内部的一个硬件模块,你可以理解为一个"频率乘法器"------输入8MHz,设置倍频系数×9,输出就是8×9=72MHz。STM32内部没法直接产生72MHz的高频时钟,所以用PLL把外部晶振的8MHz"放大"到72MHz。
│
┌─────────────────────────┤
↓ ↓
APB1分频 APB2分频
(/2 = 36MHz) (/1 = 72MHz)
│ │
↓ ↓
PCLK1(36MHz) PCLK2(72MHz)
(UART2/3, I2C, (UART1, SPI1,
TIM2-7, ADC) TIM1, GPIO)
│
↓
ADC时钟(≤14MHz)
3.1 SYSCLK
系统时钟,MCU的主频。STM32F103最高72MHz,STM32F407最高168MHz,STM32H7最高480MHz。
3.2 HCLK
AHB总线时钟,CPU和DMA跑在这个频率上。通常等于SYSCLK。
3.3 PCLK1(APB1)
低速外设时钟,STM32F103最大36MHz。挂在这里的:USART2/3、I2C、SPI2、TIM2-7、ADC。
3.4 PCLK2(APB2)
高速外设时钟,STM32F103最大72MHz。挂在这里的:USART1、SPI1、TIM1、GPIO、ADC。
3.5 定时器倍频
重要 :当APB1分频≠1时(通常÷2=36MHz),挂在上面的定时器会自动×2倍频=72MHz。
APB1 = 36MHz → 定时器时钟 = 36MHz × 2 = 72MHz
APB2 = 72MHz → 定时器时钟 = 72MHz(不倍频,因为APB2分频=1)
坑点:很多人以为TIM2-7的时钟是36MHz,实际是72MHz。算定时器频率时别搞错。
四、典型配置(F103 @ 72MHz)
4.1 CubeMX配置
HSE: 8MHz (外部晶振)
PLL Source: HSE
PLL Multiplier: ×9 → 8MHz × 9 = 72MHz
SYSCLK: PLL = 72MHz
AHB Prescaler: /1 → HCLK = 72MHz
APB1 Prescaler: /2 → PCLK1 = 36MHz
APB2 Prescaler: /1 → PCLK2 = 72MHz
4.2 代码(寄存器版)
c
void SystemClock_Config(void)
{
/* 1. 开HSE */
RCC->CR |= (1 << 16); /* HSEON */
while (!(RCC->CR & (1 << 17))); /* 等HSERDY */
/* 2. 配置Flash等待周期(72MHz需要2个等待) */
/* Flash是物理存储,读取速度有限。CPU跑72MHz时,Flash来不及在一个时钟周期内交出数据,
* 需要等2个周期。不配等待周期会导致CPU读到错误指令,程序跑飞。
* prefetch(预取)是让Flash提前取下一条指令,弥补等待周期的性能损失 */
FLASH->ACR = 0x00000012; /* 2 wait states + prefetch */
/* 3. 配置PLL:HSE × 9 = 72MHz */
RCC->CFGR |= (1 << 16); /* PLLSRC = HSE */
RCC->CFGR |= (7 << 18); /* PLLMUL = ×9 */
/* 4. 配置分频 */
RCC->CFGR |= (4 << 8); /* APB1 = HCLK/2 = 36MHz */
/* APB2 = HCLK/1 = 72MHz,默认不用配 */
/* 5. 开PLL */
RCC->CR |= (1 << 24); /* PLLON */
while (!(RCC->CR & (1 << 25))); /* 等PLLRDY */
/* 6. 切换SYSCLK到PLL */
RCC->CFGR |= (2 << 0); /* SW = PLL */
while (((RCC->CFGR >> 2) & 3) != 2); /* 等SWS = PLL */
}
五、时钟与外设的关系
5.1 UART波特率
c
/* UART1挂在APB2(72MHz) */
波特率 = 72MHz / (16 × BRR值)
BRR = 72MHz / (16 × 115200) = 39.0625 ≈ 39
/* UART2挂在APB1(36MHz) */
波特率 = 36MHz / (16 × BRR值)
BRR = 36MHz / (16 × 115200) = 19.53 ≈ 20
注意:UART1和UART2时钟不同,BRR值也不同。配错时钟,波特率必错。
5.2 定时器频率
c
/* TIM2挂在APB1(36MHz),定时器倍频×2 = 72MHz */
中断频率 = 72MHz / [(PSC+1) × (ARR+1)]
/* 要1kHz中断 */
/* 72MHz / [(71+1) × (999+1)] = 1kHz ✓ */
5.3 ADC时钟
STM32F103的ADC最大14MHz,从APB2(72MHz)分频:
ADCCLK = PCLK2 / 分频系数
72MHz / 6 = 12MHz ✓(≤14MHz)
72MHz / 4 = 18MHz ✗(超过14MHz,可能出错)
六、常见踩坑
坑1:UART乱码------时钟没配
现象:代码写115200,串口助手收到的全是乱码。
原因:系统时钟默认8MHz(HSI),但波特率按72MHz算的。
解决 :确保SystemClock_Config()被调用,且配置正确。
坑2:定时器频率差9倍
原因:没配PLL,SYSCLK=8MHz而非72MHz。定时器按72MHz算,实际8MHz,差9倍。
坑3:ADC时钟超限
ADC时钟超过14MHz(F103限制),采样数据不稳定甚至损坏。检查分频系数。
坑4:Flash等待周期不够
CPU频率>24MHz(F103)时,Flash读取需要等待周期。72MHz需要2个等待周期,不配会HardFault。
七、总结
| 要点 | 内容 |
|---|---|
| 四个时钟源 | HSE(主力)/HSI(备用)/LSE(RTC)/LSI(看门狗) |
| SYSCLK | 系统主频,F103最高72MHz |
| APB1 | 低速,F103最大36MHz,定时器×2倍频=72MHz |
| APB2 | 高速,F103最大72MHz |
| 时钟影响 | UART波特率、定时器频率、ADC速率全取决于时钟 |
一句话总结 :时钟树的核心是**"HSE→PLL倍频→SYSCLK→AHB/APB分频→外设"**,配错任何一级,下游全错。
下一篇预告:《启动流程详解:从上电到main()发生了什么》
如果这篇文章对你有帮助,点赞 + 收藏 + 关注 ,这是我持续更新的动力!
有问题欢迎评论区交流,我会逐条回复。
作者:嵌入式阿蔡