STM32时钟树详解:配错一个全乱套

你有没有遇到过: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()发生了什么》

如果这篇文章对你有帮助,点赞 + 收藏 + 关注 ,这是我持续更新的动力!

有问题欢迎评论区交流,我会逐条回复。

作者:嵌入式阿蔡

相关推荐
晓蛋4 天前
c语言指的是什么意思
c语言·编译器·编程开发·集成开发环境·程序实例
不悔哥4 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
傲世仙尊4 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
XiHongShi20164 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
牵猫散步的鱼儿4 天前
重载、重写(覆盖)、重定义区别
c语言
phltxy4 天前
C 语言指针:从内存地址到灵活的数据访问
c语言
优信电子4 天前
AIT1001 非接触式红外测温模块 STM32 驱动开发实战:从引脚到代码全解析
stm32·嵌入式·温度传感器·红外测温·非接触式测温·ait1001
phltxy4 天前
C 语言中的数据存储:从类型到二进制位
c语言
深圳老胡4 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
wuyk5554 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网