--- 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时钟树](assets/rcc_时钟树.png) > **参考**:参考手册 §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() 伪代码(简化)**: ```c 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_OscInitTypeDef` 和 `RCC_ClkInitTypeDef` 结构体完成同样的配置: ```c 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` | `HSEState = RCC_HSE_ON` | | 开启 PLL | `RCC->CR |= RCC_CR_PLLON` | `PLLState = RCC_PLL_ON` | | PLL源=HSE | `RCC->CFGR |= RCC_CFGR_PLLSRC` | `PLLSource = RCC_PLLSOURCE_HSE` | | PLL 9倍频 | `RCC->CFGR |= RCC_CFGR_PLLMULL9` | `PLLMUL = RCC_PLL_MUL9` | | AHB/1 | `RCC->CFGR |= RCC_CFGR_HPRE_DIV1` | `AHBCLKDivider = RCC_SYSCLK_DIV1` | | APB1/2 | `RCC->CFGR |= RCC_CFGR_PPRE1_DIV2` | `APB1CLKDivider = RCC_HCLK_DIV2` | | APB2/1 | `RCC->CFGR |= RCC_CFGR_PPRE2_DIV1` | `APB2CLKDivider = RCC_HCLK_DIV1` | | SYSCLK=PLL | `RCC->CFGR |= RCC_CFGR_SW_PLL` | `SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK` | | 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 不对应)** → 新时钟源未就绪就切换,需等待就绪标志