08-时钟系统(RCC).md 24 KB

STM32 时钟系统:RCC

基于铁头山羊(38, 39)、HAL库教程(16, 17)、江科大(13-2)三教程综合整理


1. 时钟系统的作用

时钟(Clock)是数字电路的心跳。芯片内部的每个寄存器、计数器、通信接口等所有数字逻辑都在时钟沿的驱动下工作。RCC(Reset and Clock Control)模块负责:

  • 管理 STM32 的所有时钟源
  • 分配时钟到各个外设总线
  • 配置系统主频(SYSCLK)
  • 控制外设时钟的使能与关闭(省电)

STM32 系统架构图 上图:STM32 系统架构(包含总线矩阵与各外设挂载关系)(来源:STM32入门教程 PPT 第9页)

STM32 时钟树结构 上图:STM32F10x 时钟树结构示意图(来源:STM32入门教程 PPT 第62页)

2. 时钟源

STM32F10x 共有 5 个时钟源

时钟源 名称 频率 精度 用途
HSI 内部高速振荡器 8 MHz 较低(出厂校准) 上电默认时钟源,可作为 PLL 输入
HSE 外部高速振荡器 4~16 MHz(常用 8 MHz) 高(晶振决定) 可作为 PLL 输入,系统时钟
PLL 锁相环 最高 72 MHz 取决于输入源 倍频输出作为 SYSCLK
LSI 内部低速振荡器 40 kHz 较低 独立看门狗(IWDG)、RTC
LSE 外部低速振荡器 32.768 kHz 高(专门音叉晶振) RTC 时钟

2.1 PLL 时钟来源

PLL 的输入可以选择:

HSI(8 MHz)
   ↓ 二分频 → 4 MHz → PLL ×2~×16 → 最高 72 MHz
   
HSE(8 MHz 外部晶振)
   ↓ 直通或二分频 → 8 MHz 或 4 MHz → PLL ×2~×16 → 最高 72 MHz

默认配置: HSE = 8 MHz → 不分频 → PLL ×9 = 72 MHz。

2.2 系统时钟树

                      ┌──────────────┐
                      │   HSI 8 MHz  │
                      └──────┬───────┘
                             │
                      ┌──────▼───────┐
                      │   HSE 8 MHz  │
                      └──────┬───────┘
                             │
                      ┌──────▼───────┐
                      │  PLL ×9      │  → 72 MHz
                      └──────┬───────┘
                             │
                   ┌─────────▼──────────┐
                   │  SYSCLK 选择器      │  ← 可选择 HSI / HSE / PLL
                   └─────────┬──────────┘
                             │
                      ┌──────▼───────┐
                      │  AHB 分频器   │  → HCLK(内核、存储器、DMA)
                      └──────┬───────┘
                             │
              ┌──────────────┼────────────────┐
              │              │                │
       ┌──────▼──────┐ ┌────▼─────┐   ┌──────▼──────┐
       │ APB1 分频器  │ │ APB2 分频器│   │ 其他外设    │
       │ ≤36 MHz     │ │ ≤72 MHz   │   │             │
       └──────┬──────┘ └────┬─────┘   └─────────────┘
              │             │
        ┌─────▼─────┐  ┌───▼────┐
        │ TIM 等     │  │GPIO、ADC│
        │ APB1 外设  │  │USART1等 │
        └───────────┘  └────────┘

2.3 各总线最高频率

总线 最高频率 挂载的典型外设
AHB 72 MHz 内核、存储器、DMA
APB1 36 MHz TIM2~TIM7、USART2~5、SPI2/3、I2C1/2
APB2 72 MHz GPIO、USART1、SPI1、ADC1/2、TIM1/TIM8

注意:APB1 上的定时器(TIM2~TIM7)时钟源实际等于 APB1 ×2(当 APB1 分频系数 ≠1 时),所以在 APB1=36MHz 时 TIM 仍可获得 72MHz 的计数时钟。


3. SystemInit 与默认时钟配置

3.1 上电启动流程

启动文件(startup_stm32f10x_xx.s)在跳转到 main 之前会调用 SystemInit()

SystemInit() 的功能:

  1. 复位 RCC 寄存器
  2. 配置外部存储器接口(FSMC 等)
  3. 调用 SetSysClock() 设置系统时钟
  4. 更新 SystemCoreClock 全局变量(记录当前 SYSCLK 频率)

3.2 SetSysClock 配置流程

SetSysClock()system_stm32f10x.c 中实现,根据宏定义选择目标频率:

// 在 system_stm32f10x.c 中可选择以下宏之一
// #define SYSCLK_FREQ_HSE    HSE_VALUE    // 直接使用 HSE
// #define SYSCLK_FREQ_24MHz  24000000
// #define SYSCLK_FREQ_36MHz  36000000
// #define SYSCLK_FREQ_48MHz  48000000
// #define SYSCLK_FREQ_56MHz  56000000
#define SYSCLK_FREQ_72MHz  72000000         // 默认启用
// #define SYSCLK_FREQ_24MHz  24000000

SYSCLK = 72 MHz 的配置步骤(典型):

  1. 等待 HSI 就绪(上电默认 HSI)
  2. 使能 HSE,等待 HSE 就绪
  3. 配置 Flash 等待周期(72MHz 需要 2 个等待周期)
  4. 配置 AHB 分频 = 1(AHB = SYSCLK = 72 MHz)
  5. 配置 APB1 分频 = 2(APB1 = 36 MHz)
  6. 配置 APB2 分频 = 1(APB2 = 72 MHz)
  7. 配置 PLL:HSE 不分频 → PLL ×9 → 72 MHz
  8. 使能 PLL,等待 PLL 就绪
  9. 切换 SYSCLK 到 PLL 输出
  10. 等待 SYSCLK 切换完成

3.3 SetSysClockTo72 完整代码(标准库)

以下代码来自 system_stm32f10x.c,当定义 SYSCLK_FREQ_72MHz 宏时被 SetSysClock() 调用:

static void SetSysClockTo72(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    /* 使能 HSE */
    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    /* 等待 HSE 就绪,超时则退出 */
    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        /* 使能预取缓冲 */
        FLASH->ACR |= FLASH_ACR_PRFTBE;

        /* Flash 2 个等待周期(72MHz 需要) */
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;

        /* HCLK = SYSCLK(AHB 不分频) */
        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

        /* PCLK2 = HCLK(APB2 不分频) */
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

        /* PCLK1 = HCLK / 2(APB1 二分频) */
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

        /* PLL 配置:PLLCLK = HSE × 9 = 72 MHz */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC |
                                            RCC_CFGR_PLLXTPRE |
                                            RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

        /* 使能 PLL */
        RCC->CR |= RCC_CR_PLLON;
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) { }

        /* 选择 PLL 作为系统时钟 */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;

        /* 等待 PLL 切换完成 */
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
    }
    else
    {
        /* HSE 起振失败 — 可在此添加错误处理代码 */
    }
}

3.4 SetSysClockTo72 HAL库等价代码

static void SetSysClockTo72(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /* HSE 振荡器配置:使能 HSE,作为 PLL 输入 */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL9;       /* HSE × 9 = 72 MHz */
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { }

    /* 时钟树配置:SYSCLK = PLL72MHz,总线分频 */
    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_HCLK  |
                                       RCC_CLOCKTYPE_SYSCLK |
                                       RCC_CLOCKTYPE_PCLK1  |
                                       RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;    /* HCLK = 72 MHz */
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;      /* PCLK2 = 72 MHz */
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;      /* PCLK1 = 36 MHz */

    /* Flash 等待周期:72MHz 需要 2 个等待周期 */
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { }
}

3.6 SetSysClockToHSE 完整代码(标准库 — HSE 直通)

static void SetSysClockToHSE(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        FLASH->ACR |= FLASH_ACR_PRFTBE;
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE;

        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) { }
    }
}

/* --- HAL库等价 --- */
static void SetSysClockToHSE(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_NONE;
    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_HSE;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}

3.7 SetSysClockTo24 完整代码(标准库 — 24 MHz)

static void SetSysClockTo24(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        FLASH->ACR |= FLASH_ACR_PRFTBE;
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6);

        RCC->CR |= RCC_CR_PLLON;
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) { }

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
    }
}

/* --- HAL库等价 --- */
static void SetSysClockTo24(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL6;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_ALL;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}

3.8 SetSysClockTo36 完整代码(标准库 — 36 MHz)

static void SetSysClockTo36(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        FLASH->ACR |= FLASH_ACR_PRFTBE;
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9);

        RCC->CR |= RCC_CR_PLLON;
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) { }

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
    }
}

/* HAL库等价版 */
static void SetSysClockTo36(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL9;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_ALL;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
}

3.9 SetSysClockTo48 完整代码(标准库 — 48 MHz)

static void SetSysClockTo48(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        FLASH->ACR |= FLASH_ACR_PRFTBE;
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6);

        RCC->CR |= RCC_CR_PLLON;
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) { }

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
    }
}

/* HAL库等价版 */
static void SetSysClockTo48(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL6;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_ALL;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
}

3.10 SetSysClockTo56 完整代码(标准库 — 56 MHz)

static void SetSysClockTo56(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    do
    {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)
        HSEStatus = (uint32_t)0x01;
    else
        HSEStatus = (uint32_t)0x00;

    if (HSEStatus == (uint32_t)0x01)
    {
        FLASH->ACR |= FLASH_ACR_PRFTBE;
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7);

        RCC->CR |= RCC_CR_PLLON;
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) { }

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
    }
}

/* HAL库等价版 */
static void SetSysClockTo56(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL7;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_ALL;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

3.11 MCO 时钟输出配置

MCO(Microcontroller Clock Output)可将内部时钟从 PA8 引脚输出,供外部测量或作为其它芯片的时钟源。

/* 标准库版:PA8 输出 PLL/2 = 36 MHz */
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

RCC_MCOConfig(RCC_MCO_PLLCLK_DIV2);   /* MCO = PLL / 2 */

/* 其他可选时钟源 */
// RCC_MCOConfig(RCC_MCO_HSI);        /* MCO = HSI 8 MHz       */
// RCC_MCOConfig(RCC_MCO_HSE);        /* MCO = HSE 8 MHz       */
// RCC_MCOConfig(RCC_MCO_SYSCLK);     /* MCO = SYSCLK           */
// RCC_MCOConfig(RCC_MCO_PLLCLK_DIV2);/* MCO = PLLCLK / 2      */
/* HAL库等价:PA8 输出 PLL/2 = 36 MHz */
GPIO_InitTypeDef GPIO_InitStruct = {0};

__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin       = GPIO_PIN_8;
GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

HAL_RCC_MCOConfig(RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_2);

3.12 SystemCoreClock 变量

// system_stm32f10x.c 中定义
uint32_t SystemCoreClock = 72000000;

// 用户代码可直接读取
OLED_ShowNum(1, 8, SystemCoreClock, 8);  // 显示当前系统主频

如果程序运行中修改了时钟配置(比如降低主频进入低功耗),需要调用 SystemCoreClockUpdate() 更新此变量。


4. 外设时钟使能

RCC 通过三个寄存器组控制外设时钟的开关。使用时只需调用以下标准库函数:

总线 标准库函数 HAL 宏
APB2 RCC_APB2PeriphClockCmd() __HAL_RCC_GPIOA_CLK_ENABLE()
APB1 RCC_APB1PeriphClockCmd() __HAL_RCC_TIM2_CLK_ENABLE()
AHB RCC_AHBPeriphClockCmd() __HAL_RCC_DMA1_CLK_ENABLE()

标准库示例:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

HAL库示例:

__HAL_RCC_GPIOB_CLK_ENABLE();     /* APB2 — GPIOB 时钟使能 */
__HAL_RCC_TIM2_CLK_ENABLE();      /* APB1 — TIM2 时钟使能 */
__HAL_RCC_DMA1_CLK_ENABLE();      /* AHB  — DMA1 时钟使能 */

__HAL_RCC_USART1_CLK_ENABLE();    /* APB2 — USART1 时钟使能 */
__HAL_RCC_ADC1_CLK_ENABLE();      /* APB2 — ADC1 时钟使能 */
__HAL_RCC_SPI1_CLK_ENABLE();      /* APB2 — SPI1 时钟使能 */

不能在初始化函数中关闭时钟后再开启,否则外设寄存器会丢失配置。

常见外设的时钟总线归属

APB1(36 MHz 最高) APB2(72 MHz 最高)
TIM2, TIM3, TIM4, TIM5, TIM6, TIM7 TIM1, TIM8
USART2, USART3, USART4, USART5 USART1
SPI2, SPI3 SPI1
I2C1, I2C2 GPIOA~GPIOC(部分)
PWR, BKP AFIO, EXTI

5. 修改主频实验

5.1 实验思路

通过修改 system_stm32f10x.c 中的宏定义来改变 SYSCLK 频率,观察主循环运行快慢和 SystemCoreClock 的值。

5.2 代码

int main(void)
{
    OLED_Init();
    
    OLED_ShowString(1, 1, "SYSCLK:");
    OLED_ShowNum(1, 8, SystemCoreClock, 8);
    
    while (1)
    {
        OLED_ShowString(2, 1, "Running");
        Delay_ms(500);
        OLED_ShowString(2, 1, "       ");
        Delay_ms(500);
    }
}

OLED 上会显示当前的 SYSCLK 值(如 72000000)。注意修改主频后:

  • Flash 等待周期需要同步调整(system_stm32f10x.c 中的 SetSysClock() 会自动处理)
  • APB1 外设不能超过 36 MHz
  • 使用 HSI 作为 PLL 源时,最大只能到 64 MHz(HSI/2 × 16 = 64 MHz)

6. RCC 相关常见问题

6.1 外设不工作

症状:某个外设(GPIO、定时器、USART 等)寄存器写不进去,功能异常。

排查:检查是否调用了对应的 RCC_xxxPeriphClockCmd() 开启了时钟。

原因:STM32 的外设默认时钟是关闭的(省电设计),开启前无法访问其寄存器。

6.2 主频与预期不符

运行 SystemCoreClock 不正确可能的原因:

  • HSE 晶振故障(起振失败),自动回退到 HSI 8 MHz
  • system_stm32f10x.c 中的宏没有正确配置
  • PLL 倍频系数超出芯片支持的频率范围

6.3 修改 APB 分频对外设的影响

APB1 分频 APB1 时钟 TIM 时钟源
1(不分频) 72 MHz 72 MHz
2(分频) 36 MHz 72 MHz(×2 自动补偿)
4 18 MHz 36 MHz(×2)
8 9 MHz 18 MHz(×2)

APB1 定时器(TIM2~TIM7)时钟 = APB1 ×2 仅当 APB1 分频系数 ≠ 1 时成立。调试定时器频率时要注意这一点。