STM32 时钟配置全攻略——从结构体 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef入手

🌟 STM32 时钟配置全攻略——从结构体 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef入手

“给 STM32 配时钟就像调钢琴,调得好它就是贝多芬,调不好就是敲木鱼。” 🎼🔨

在 STM32 开发中,时钟配置至关重要,它影响 MCU 的运行速度和外设的工作效率。如果你的 STM32 代码运行缓慢,或者某些外设无法正常工作,那么十有八九是时钟出了问题!

今天,我们进行一场STM32 时钟系统的深度解析,不仅带你了解 RCC 配置的完整流程,还详细剖析 HAL_RCC_OscConfigHAL_RCC_ClockConfig 这两个关键函数的作用。

看完这篇文章,你一定能成为 STM32 时钟配置的高手! 🚀


📌 一、STM32 时钟系统概述

STM32 的时钟系统相当于一个庞大的交通网络,所有外设都需要从某个时钟源获取“动力”来运转。它主要包括:

🎯 1. 时钟源(Oscillator)

STM32 内部有多个时钟源,它们的特点如下:

⏱️ 时钟源 📌 作用 ⚡ 频率 📜 特点
HSE(外部高速晶振) 主时钟源 8MHz / 16MHz 精度高,需要外部晶振
HSI(内部高速振荡器) 备用时钟源 16MHz 自带校准,但精度一般
LSE(外部低速晶振) RTC(实时时钟) 32.768kHz 精度高,适合低功耗
LSI(内部低速振荡器) 看门狗、RTC备用 32kHz 低功耗,精度较差

👉 这些时钟源可以直接使用,也可以经过 PLL 进行放大! 🔄


🔄 2. PLL(相位锁环)

STM32 内置的 PLL(Phase Locked Loop) 允许对时钟进行倍频或分频,比如:

HSE = 8MHz,可以用 PLL 放大到 168MHz,作为 CPU 主频!
HSI = 16MHz,可以用 PLL 倍频到 48MHz,用于 USB 通信!

所以,高主频 STM32(72MHz、168MHz)基本都依赖 PLL! 🎯


🏗 3. 系统时钟(SYSCLK)与总线时钟

系统时钟(SYSCLK)决定了 MCU 的运行速度,而总线时钟决定了外设的速度。

SYSCLK → AHB (HCLK) → APB1 (PCLK1) → 低速外设 (UART2/3, I2C, SPI2/3)  
                 ↓  
                 APB2 (PCLK2) → 高速外设 (UART1, SPI1, ADC)
🏆 时钟 📌 作用 ⏳ 典型频率
SYSCLK 系统时钟 72MHz / 168MHz
HCLK AHB 总线时钟 SYSCLK
PCLK1 低速外设 HCLK / 2 或 HCLK / 4
PCLK2 高速外设 HCLK / 2

👉 掌握这些时钟后,我们就可以进行配置了! 🎯


🛠 二、STM32 时钟配置的完整流程

STM32 的时钟配置通常分为 两大步骤

配置时钟源(Oscillator)HAL_RCC_OscConfig(&RCC_OscInitStruct);
配置时钟分配(Clock)HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY);

这两个函数至关重要,我们逐个解析!


三、 结构体 RCC_OscInitTypeDefRCC_ClkInitTypeDef介绍

📌 RCC_OscInitTypeDef 结构体

该结构体用于配置 STM32 的时钟源(Oscillator),即 MCU 运行所需的基础时钟。
它主要包含 HSE(外部高速晶振)、HSI(内部高速振荡器)、LSE(外部低速晶振)、LSI(内部低速振荡器)以及 PLL(相位锁环)的相关参数。

typedef struct
{
  uint32_t OscillatorType;       // 选择要配置的振荡器(HSE、HSI、LSE、LSI)
  uint32_t HSEState;             // HSE(外部高速晶振)的状态(开启、关闭、旁路)
  uint32_t LSEState;             // LSE(外部低速晶振)的状态(开启、关闭、旁路)
  uint32_t HSIState;             // HSI(内部高速振荡器)的状态(开启、关闭)
  uint32_t HSICalibrationValue;  // HSI 校准值(用于调整 HSI 的精度)
  uint32_t LSIState;             // LSI(内部低速振荡器)的状态(开启、关闭)
  RCC_PLLInitTypeDef PLL;        // PLL(相位锁环)相关配置
} RCC_OscInitTypeDef;

🔹 结构体成员解析

参数 说明 可能的取值
OscillatorType 选择要配置的时钟源 RCC_OSCILLATORTYPE_HSERCC_OSCILLATORTYPE_HSIRCC_OSCILLATORTYPE_LSERCC_OSCILLATORTYPE_LSI
HSEState 控制 HSE(外部高速晶振)的状态 RCC_HSE_OFF(关闭)、RCC_HSE_ON(开启)、RCC_HSE_BYPASS(旁路)
LSEState 控制 LSE(外部低速晶振)的状态 RCC_LSE_OFFRCC_LSE_ONRCC_LSE_BYPASS
HSIState 控制 HSI(内部高速振荡器)的状态 RCC_HSI_OFFRCC_HSI_ON
HSICalibrationValue 设置 HSI 校准值(影响 HSI 的精度) 0~31(寄存器值)
LSIState 控制 LSI(内部低速振荡器)的状态 RCC_LSI_OFFRCC_LSI_ON
PLL 配置 PLL 倍频 RCC_PLLInitTypeDef 结构体

📌 RCC_ClkInitTypeDef 结构体

该结构体用于配置系统时钟(System Clock)及其在不同总线上的分配方式。
它决定了
主时钟来源
以及 AHB 和 APB 总线的时钟分频情况。

typedef struct
{
  uint32_t ClockType;      // 需要配置的时钟类型
  uint32_t SYSCLKSource;   // 选择系统时钟源
  uint32_t AHBCLKDivider;  // AHB 总线时钟分频
  uint32_t APB1CLKDivider; // APB1 总线时钟分频
  uint32_t APB2CLKDivider; // APB2 总线时钟分频
} RCC_ClkInitTypeDef;

🔹 结构体成员解析

参数 说明 可能的取值
ClockType 需要配置的时钟 RCC_CLOCKTYPE_SYSCLK(系统时钟)、RCC_CLOCKTYPE_HCLK(AHB 时钟)、RCC_CLOCKTYPE_PCLK1(APB1 时钟)、RCC_CLOCKTYPE_PCLK2(APB2 时钟)
SYSCLKSource 选择系统主时钟来源 RCC_SYSCLKSOURCE_HSI(HSI)、RCC_SYSCLKSOURCE_HSE(HSE)、RCC_SYSCLKSOURCE_PLLCLK(PLL)
AHBCLKDivider AHB 总线分频(影响 CPU 及高速外设) RCC_SYSCLK_DIV1(不分频)、RCC_SYSCLK_DIV2RCC_SYSCLK_DIV4RCC_SYSCLK_DIV8RCC_SYSCLK_DIV16
APB1CLKDivider APB1 总线分频(低速外设,如 UART2、I2C1) RCC_HCLK_DIV1RCC_HCLK_DIV2RCC_HCLK_DIV4RCC_HCLK_DIV8RCC_HCLK_DIV16
APB2CLKDivider APB2 总线分频(高速外设,如 UART1、SPI1) RCC_HCLK_DIV1RCC_HCLK_DIV2RCC_HCLK_DIV4RCC_HCLK_DIV8RCC_HCLK_DIV16

🔧 四、HAL_RCC_OscConfig() —— 选择时钟源

HAL_RCC_OscConfig() 用于选择 MCU 运行的时钟源,并配置 PLL。

下列结构体配置中的参数是基于上述时钟树图,具体数字大家可以反复比对一下,细细理解其中每个参数的含义,加深对配置过程的理解。

🔹 1. 结构体 RCC_OscInitTypeDef

RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 选择 HSE
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 开启 HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 让 PLL 使用 HSE
RCC_OscInitStruct.PLL.PLLM = 8; // 8 分频
RCC_OscInitStruct.PLL.PLLN = 336; // 336 倍频
RCC_OscInitStruct.PLL.PLLP = 2; // 2 分频,最终得到 168MHz
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler(); // 错误处理
}

🔹 理解关键参数

  • PLL.PLLM = 8HSE 8MHz ÷ 8 = 1MHz
  • PLL.PLLN = 3361MHz × 336 = 336MHz
  • PLL.PLLP = 2336MHz ÷ 2 = 168MHz
  • 这样,我们就成功将 系统主频调整到了 168MHz! 🚀


    五、HAL_RCC_ClockConfig() —— 分配时钟

    HAL_RCC_ClockConfig() 用于将主时钟分配给 AHB、APB 总线

    🔹 1. 结构体 RCC_ClkInitTypeDef

    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
                                  RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 选择 PLL
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1 4 分频
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2 2 分频
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
        Error_Handler();
    }
    

    🔹 理解关键参数

  • AHBCLKDivider = RCC_SYSCLK_DIV1HCLK = 168MHz
  • APB1CLKDivider = RCC_HCLK_DIV4PCLK1 = 42MHz
  • APB2CLKDivider = RCC_HCLK_DIV2PCLK2 = 84MHz
  • 这样,我们的 STM32 就能跑得飞快! 🚀


    🎯 六、总结

    HAL_RCC_OscConfig() 选择 HSE 并开启 PLL,设定 168MHz 时钟。
    HAL_RCC_ClockConfig() 设定 AHB = 168MHzAPB1 = 42MHzAPB2 = 84MHz
    所有外设正常运行,STM32 高速运转! 🎉

    作者:@陽光總在風雨後

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 时钟配置全攻略——从结构体 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef入手

    发表回复