05-系统时钟RCC详解.md 8.9 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(基础篇)V1.0.2 — 第5章:时钟系统" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

系统时钟RCC详解

用生活理解:时钟系统就像城市的电力系统——不同的设备需要不同的电压(频率)。CPU处理器要高速电(72MHz),APB1外设要中速电(36MHz),RTC实时时钟要独立不间断供电(32.768KHz)。RCC就是配电房,负责分配和管理所有的时钟。


为什么需要多个时钟源

STM32 设计多个时钟源的核心目的:节能

所有外设都跑 72MHz 会浪费大量功耗。不同外设对速度要求不同:

  • CPU 内核:需要最高速度(72MHz)
  • GPIO、USART1、SPI1 等(挂载在 APB2):需要较高速度
  • USART2/3、I2C、TIM 等(挂载在 APB1):需要适中速度
  • RTC、看门狗:需要低速独立时钟(32.768KHz、40KHz)

时钟树总览

STM32F103时钟树

参考:参考手册 §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 版配置 (HAL_RCC_ClockConfig)

HAL 库使用 RCC_OscInitTypeDefRCC_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

常见问题与避坑

  1. 修改 PLL 倍频后系统死机 → 检查 Flash 等待周期是否对应新频率
  2. 外设时钟不开启 → 所有外设使用前必须手动开启时钟(RCC->APBxENR |= ...
  3. ADC 采样不准 → ADC 需要独立的时钟配置(ADC 预分频器),不能直接用系统时钟
  4. HSE 起振失败(HSERDY 始终为 0) → 检查晶振电路、负载电容值、PCB 布局
  5. USB 不通 → USB 外设需要精确的 48MHz 时钟,USB 预分频器需要单独配置
  6. 时钟切换失败(SWS 不对应) → 新时钟源未就绪就切换,需等待就绪标志