STM32 时钟配置全攻略——从结构体 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef入手
🌟 STM32 时钟配置全攻略——从结构体 RCC_OscInitTypeDef 和 RCC_ClkInitTypeDef入手
“给 STM32 配时钟就像调钢琴,调得好它就是贝多芬,调不好就是敲木鱼。” 🎼🔨
在 STM32 开发中,时钟配置至关重要,它影响 MCU 的运行速度和外设的工作效率。如果你的 STM32 代码运行缓慢,或者某些外设无法正常工作,那么十有八九是时钟出了问题!
今天,我们进行一场STM32 时钟系统的深度解析,不仅带你了解 RCC 配置的完整流程,还详细剖析 HAL_RCC_OscConfig
和 HAL_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_OscInitTypeDef 和 RCC_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_HSE 、RCC_OSCILLATORTYPE_HSI 、RCC_OSCILLATORTYPE_LSE 、RCC_OSCILLATORTYPE_LSI |
HSEState |
控制 HSE(外部高速晶振)的状态 | RCC_HSE_OFF (关闭)、RCC_HSE_ON (开启)、RCC_HSE_BYPASS (旁路) |
LSEState |
控制 LSE(外部低速晶振)的状态 | RCC_LSE_OFF 、RCC_LSE_ON 、RCC_LSE_BYPASS |
HSIState |
控制 HSI(内部高速振荡器)的状态 | RCC_HSI_OFF 、RCC_HSI_ON |
HSICalibrationValue |
设置 HSI 校准值(影响 HSI 的精度) | 0~31(寄存器值) |
LSIState |
控制 LSI(内部低速振荡器)的状态 | RCC_LSI_OFF 、RCC_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_DIV2 、RCC_SYSCLK_DIV4 、RCC_SYSCLK_DIV8 、RCC_SYSCLK_DIV16 |
APB1CLKDivider |
APB1 总线分频(低速外设,如 UART2、I2C1) | RCC_HCLK_DIV1 、RCC_HCLK_DIV2 、RCC_HCLK_DIV4 、RCC_HCLK_DIV8 、RCC_HCLK_DIV16 |
APB2CLKDivider |
APB2 总线分频(高速外设,如 UART1、SPI1) | RCC_HCLK_DIV1 、RCC_HCLK_DIV2 、RCC_HCLK_DIV4 、RCC_HCLK_DIV8 、RCC_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 = 8
→ HSE 8MHz ÷ 8 = 1MHzPLL.PLLN = 336
→ 1MHz × 336 = 336MHzPLL.PLLP = 2
→ 336MHz ÷ 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_DIV1
→ HCLK = 168MHzAPB1CLKDivider = RCC_HCLK_DIV4
→ PCLK1 = 42MHzAPB2CLKDivider = RCC_HCLK_DIV2
→ PCLK2 = 84MHz✨ 这样,我们的 STM32 就能跑得飞快! 🚀
🎯 六、总结
✅ HAL_RCC_OscConfig()
选择 HSE 并开启 PLL,设定 168MHz 时钟。
✅ HAL_RCC_ClockConfig()
设定 AHB = 168MHz,APB1 = 42MHz,APB2 = 84MHz。
✅ 所有外设正常运行,STM32 高速运转! 🎉
作者:@陽光總在風雨後