目标:理解时钟源结构,配置 SYSCLK = 72MHz(最大频率)
STM32 内部包含三大核心部件:CPU 核心(Cortex-M3)、存储器(Flash + SRAM)、外设总线(AHB + APB1 + APB2)。时钟树的任务就是把外部/内部时钟源通过 PLL 倍频、分频器分频后,分配到每个部件。
STM32 有 4 个独立的时钟源:
| 时钟源 | 符号 | 典型频率 | 精度 | 用途 |
|---|---|---|---|---|
| 高速内部振荡器 | HSI | 8MHz | 较差 (±1%) | 上电默认时钟,可作 PLL 输入 |
| 高速外部晶振 | HSE | 4~16MHz | 精确 (±0.01%) | 常用 8MHz 作为 PLL 输入源 |
| 低速内部振荡器 | LSI | 40kHz | 较差 | 独立看门狗 (IWDG)、RTC 备用 |
| 低速外部晶振 | LSE | 32.768kHz | 精确 | RTC 专用时钟 |
PLL(Phase-Locked Loop,锁相环)通过反馈控制回路,将输入时钟倍频到目标频率:
┌──────┐ ┌──────────────┐ ┌──────┐
f_osc ─→│ /M │────→│ 相位检测器 │────→│ /P │──→ f_PLL_out
└──────┘ │ ┌─ VCO ──┐ │ └──────┘
│ │ f_VCO │ │
│ └────────┘ │
│ ┌── 反馈分频 ─┐ │
│ │ /N │ │
│ └────────────┘ │
└────────────────┘
核心公式:
f_VCO = f_osc × N / M
f_PLL_out = f_VCO / P
在 STM32F1 系列中,PLL 的配置参数由 RCC_CFGR 寄存器直接控制:
PLLXTPRE 位控制,对 HSE 做 /1 或 /2PLLMUL[3:0] 位编码,实际乘法系数 = (PLLMUL + 2),范围 ×4 ~ ×16(STM32F103 常用 ×9:8MHz × 9 = 72MHz)典型配置路径:HSE(8MHz) → PLLMUL(×9) → SYSCLK(72MHz)
实际 VCO 频率计算公式(以 STM32F103 为例):
f_VCO = f_HSE × (PLLMUL) (其中 PLLMUL 为实际倍率值)
72MHz = 8MHz × 9
寄存器编码公式:
f_VCO = f_HSE × (PLLMUL_编码值 + 2) / (PLLXTPRE + 1)
其中 PLLMUL_编码值 = 2~15(对应 ×4 ~ ×16)
PLLXTPRE = 0~1(对应 /1 ~ /2)
⚠️ VCO 频率有范围限制:STM32F103 的 VCO 要求工作于 48~72MHz 之间。
| 时钟源 | 启动时间 | 特点 |
|---|---|---|
| HSI (8MHz RC) | ≈ 8µs | 上电即就绪,无需等待晶体起振 |
| HSE (晶振) | ≈ 2ms | 需要晶体起振稳定时间,取决于晶振负载电容和质量 |
HSI 在上电后几乎瞬间可用,因此被选为系统默认时钟源。但 HSI 精度较差(±1%),在需要精确通信(如 USART、USB)时必须切换到 HSE 或 PLL。
CSS 是 STM32 的一个硬件安全机制,监控 HSE 是否正常振荡:
正常状态: HSE 稳定振荡 → CSS 不动作
故障状态: HSE 停振 → CSS 自动切换系统时钟到 HSI
→ 产生 NMI (不可屏蔽中断)
→ 用户可在 NMI_Handler 中做紧急处理
CSS 使能后,如果 HSE 因晶振损坏、接触不良或外部干扰停振,硬件自动完成以下操作:
在 NMI 中断服务函数中,用户可以:
⚠️ CSS 只能在 HSE 正常工作时使能,如果 HSE 本身未就绪就使能 CSS,会立即触发 NMI。
STM32 内部采用 AHB (高速总线) 与 APB (低速外设总线) 两级总线结构,两者之间通过 AHB/APB 桥 连接:
CPU ──→ AHB ──→ AHB/APB1 桥 ──→ APB1 (≤36MHz)
AHB/APB2 桥 ──→ APB2 (≤72MHz)
APB 桥内部包含 FIFO(先入先出缓冲),用于跨时钟域的数据同步:
APB 预分频器隐藏规则(定时器 2× 时钟):
APB 预分频器 = 1 → TIM 时钟 = PCLK(不分频,无加倍)
APB 预分频器 ≠ 1 → TIM 时钟 = 2 × PCLK(硬件自动加倍)
例:
APB1 = /2 → PCLK1 = 36MHz → TIM2/3/4/5 时钟 = 2 × 36 = 72MHz
APB2 = /1 → PCLK2 = 72MHz → TIM1/TIM8 时钟 = 72MHz(无加倍)
时钟树的本质是多路选择器 (MUX) + 锁相环 (PLL) + 分频器 (Divider) 组成的时钟分配网络:
HSI (8MHz) ──┐
├──→ MUX ──→ SYSCLK ──→ AHB Div ──→ HCLK (CPU, Flash, SRAM)
HSE (8MHz) ──┘ │
↑ ├──→ APB1 Div ──→ PCLK1 (≤36MHz)
PLL x9 │ 外设: TIM2~4, I2C1~2, USART2, etc.
(8×9=72MHz) └──→ APB2 Div ──→ PCLK2 (≤72MHz)
外设: GPIO, USART1, SPI1, ADC1, TIM1
APB 预分频器有一个隐藏行为:
APB1 预分频 = 1 → TIM 时钟 = PCLK1(36MHz → 定时器 36MHz) ❌ 本配置
APB1 预分频 ≠ 1 → TIM 时钟 = 2 × PCLK1(36MHz → 定时器 72MHz) ✅ 配 /2 时
当 APB1 分频为 /2 时(72MHz 配置),虽然 PCLK1 = 36MHz,但挂载在 APB1 上的定时器(TIM2/3/4/5/6/7)实际时钟为 2 × 36 = 72MHz。配置定时器时分频要按 72MHz 计算。
同理 APB2 分频 ≠ 1 时(如/2),TIM1/TIM8 也得到 2× 时钟。72MHz 配置下 APB2=/1,所以 TIM1/TIM8 时钟就是 72MHz。
HCLK 频率与 Flash 等待周期的关系:
| HCLK | 等待周期 |
|---|---|
| 0~24MHz | 0 wait (WS=0) |
| 24~48MHz | 1 wait (WS=1) |
| 48~72MHz | 2 wait (WS=2) |
CubeMX 会自动计算并配置
FLASH_ACR寄存器,不需要手动设置。
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
for (volatile uint32_t i = 0; i < 1000000; i++); // 软件延时
}
对比默认 8MHz 和配置后 72MHz:
CubeMX 修改时钟后,Keil 需同步:
HAL_Delay 仍正常工作