tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(基础篇)V1.0.2 — 第5章:时钟系统" author: "尚硅谷研究院" date: 2026-07-15
用生活理解:时钟系统就像城市的电力系统——不同的设备需要不同的电压(频率)。CPU处理器要高速电(72MHz),APB1外设要中速电(36MHz),RTC实时时钟要独立不间断供电(32.768KHz)。RCC就是配电房,负责分配和管理所有的时钟。
STM32 设计多个时钟源的核心目的:节能。
所有外设都跑 72MHz 会浪费大量功耗。不同外设对速度要求不同:
参考:参考手册 §7(RCC 寄存器)中的时钟树框图 参考:开发板原理图晶振电路部分(HSE 8MHz + LSE 32.768KHz)
| 时钟源 | 类型 | 频率 | 精度 | 用途 |
|---|---|---|---|---|
| HSI | 内部RC | 8MHz | 较差(~1%) | 低成本方案、HSE故障后备 |
| HSE | 外部晶振 | 4~16MHz(典型8MHz) | 高(~50ppm) | 系统时钟首选来源 |
| PLL | 锁相环倍频 | 最高72MHz | — | 产生CPU最高频率 |
| LSI | 内部RC | ~40KHz | 差(~10%) | IWDG、AWU自动唤醒 |
| LSE | 外部晶振 | 32.768KHz | 高 | RTC实时时钟 |
注意:HSE 精度高的原因是外部晶振的品质远好于芯片内部的 RC 振荡器。HSI 精度差的根源是 RC 振荡器受温度和电压影响大。
HSE(8MHz) ──────┐
├──→ PLLSRC ─→ PLLx9 ─→ SYSCLK(72MHz) ─→ AHB Prescaler(/1~512)
│ │
HSI(8MHz) ───────┘ ┌────────────────────┘
│
AHB Bus(72MHz)
│
┌──────────────┼──────────────┐
↓ ↓ ↓
APB1 Prescaler APB2 Prescaler 其它
(/1~16) (/1~16)
↓ ↓
APB1 Bus(36MHz) APB2 Bus(72MHz)
↓ ↓
TIMx 倍频器 TIMx 倍频器
(×2) (×2)
↓ ↓
TIM 时钟(72MHz) TIM 时钟(72MHz)
关键路径:
8MHz(HSE) → PLLSRC → PLLMUL(x9) → SYSCLK(72MHz) → AHB(72MHz)
→ APB1(36MHz, max)
→ APB2(72MHz, max)
注意:APB1 最大 36MHz(因为 APB1 预分频器 /2),APB2 最大 72MHz(/1)。
| 总线 | 最高频率 | 挂载的外设 |
|---|---|---|
| AHB | 72MHz | Flash、SRAM、DMA、SDIO |
| APB1 | 36MHz | USART2/3、I2C1/2、SPI2/3、TIM2~7、RTC(后备域)、PWR、BKP |
| APB2 | 72MHz | GPIO(A~G)、USART1、SPI1、TIM1、ADC1/2/3、AFIO |
结论:高带宽外设(USART1、SPI1)挂在 APB2 才能跑到较高波特率。低功耗外设(I2C、USART2)挂在 APB1 降低功耗。
STM32 上电后默认使用 HSI(8MHz)作为系统时钟,直到 SystemInit() 完成 PLL 配置切换。
启动流程:
复位 → startup_hd.s → SystemInit() → main()
SystemInit() 伪代码(简化):
void SystemInit(void)
{
/* 1. 复位RCC配置 */
RCC->CR |= 0x00000001; // HSI 开启
RCC->CFGR = 0x00000000; // 复位CFGR(SYSCLK=HSI)
RCC->CR &= 0xFEF6FFFF; // HSE 关闭、PLL 关闭
/* 2. 配置 Flash 等待周期(72MHz 需要 2 个等待周期) */
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2;
/* 3. 配置 HSE+PLL → 72MHz */
RCC->CR |= RCC_CR_HSEON; // 开启 HSE
while (!(RCC->CR & RCC_CR_HSERDY)); // 等待 HSE 就绪
RCC->CFGR |= RCC_CFGR_PLLSRC; // PLL 时钟源 = HSE
RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL 9 倍频
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB 分频 = /1
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 分频 = /2
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2 分频 = /1
RCC->CR |= RCC_CR_PLLON; // 启动 PLL
while (!(RCC->CR & RCC_CR_PLLRDY)); // 等待 PLL 就绪
RCC->CFGR |= RCC_CFGR_SW_PLL; // 切换系统时钟为 PLL
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
}
参考:
system_stm32f1xx.c中的 SystemInit() 完整源码
| 标志位 | 寄存器 | 说明 |
|---|---|---|
| HSIRDY | CR[1] | HSI 就绪 |
| HSERDY | CR[17] | HSE 就绪 |
| PLLRDY | CR[25] | PLL 就绪 |
| LSIRDY | CSR[1] | LSI 就绪 |
| LSERDY | BDCR[1] | LSE 就绪 |
HAL 库使用 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef 结构体完成同样的配置:
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 配置 HSE + PLL */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 选择HSE
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 开启HSE
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; // HSE不分频
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL源=HSE
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 9倍频
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* 配置总线分频 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // SYSCLK源自PLL
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB不分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1/2
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2/1
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); // Flash 2等待
}
如果需要修改系统时钟(比如降低频率省电):
1. 关闭 PLL
2. 修改 PLL 倍频系数
3. 修改 Flash 等待周期(与频率匹配)
4. 重新开启 PLL 并等待就绪
5. 切换系统时钟源为 PLL
Flash 等待周期与频率的关系:
| 系统时钟 | Flash 等待周期 |
|---|---|
| 0~24MHz | 0 (零等待) |
| 24~48MHz | 1 |
| 48~72MHz | 2 |
| 配置 | 寄存器方式 | HAL库方式 |
|---|---|---|
| 开启 HSE | `RCC->CR | = RCC_CR_HSEON` |
| 开启 PLL | `RCC->CR | = RCC_CR_PLLON` |
| PLL源=HSE | `RCC->CFGR | = RCC_CFGR_PLLSRC` |
| PLL 9倍频 | `RCC->CFGR | = RCC_CFGR_PLLMULL9` |
| AHB/1 | `RCC->CFGR | = RCC_CFGR_HPRE_DIV1` |
| APB1/2 | `RCC->CFGR | = RCC_CFGR_PPRE1_DIV2` |
| APB2/1 | `RCC->CFGR | = RCC_CFGR_PPRE2_DIV1` |
| SYSCLK=PLL | `RCC->CFGR | = RCC_CFGR_SW_PLL` |
| Flash 2等待 | FLASH->ACR = 0x05 |
FLASH_LATENCY_2 |
RCC->APBxENR |= ...)