STM32时钟配置全攻略——从小白到高手
STM32时钟配置全攻略——从小白到高手
STM32的时钟配置是开发过程中的关键环节,它直接影响到MCU的运行速度和外设的工作效率。本文将从时钟系统的概述开始,逐步深入讲解时钟源、PLL、系统时钟和总线时钟的概念,并详细解析RCC_OscConfig和RCC_ClockConfig两个关键函数的使用方法。通过本文的学习,你将能够掌握STM32时钟配置的核心要点,成为一名合格的嵌入式开发者。
STM32 时钟系统概述
STM32的时钟系统相当于一个庞大的交通网络,所有外设都需要从某个时钟源获取“动力”来运转。它主要包括:
时钟源(Oscillator)
STM32内部有多个时钟源,它们的特点如下:
时钟源 | 作用 | 频率 | 特点 |
---|---|---|---|
HSE(外部高速晶振) | 主时钟源 | 8MHz / 16MHz | 精度高,需要外部晶振 |
HSI(内部高速振荡器) | 备用时钟源 | 16MHz | 自带校准,但精度一般 |
LSE(外部低速晶振) | RTC(实时时钟) | 32.768kHz | 精度高,适合低功耗 |
LSI(内部低速振荡器) | 看门狗、RTC备用 | 32kHz | 低功耗,精度较差 |
这些时钟源可以直接使用,也可以经过PLL进行放大!
PLL(相位锁环)
STM32内置的PLL(Phase Locked Loop)允许对时钟进行倍频或分频,比如:
- HSE = 8MHz,可以用PLL放大到168MHz,作为CPU主频!
- HSI = 16MHz,可以用PLL倍频到48MHz,用于USB通信!
所以,高主频STM32(72MHz、168MHz)基本都依赖PLL!
系统时钟(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)
- 配置时钟分配(Clock)
这两个函数至关重要,我们逐个解析!
结构体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。
下列结构体配置中的参数是基于上述时钟树图,具体数字大家可以反复比对一下,细细理解其中每个参数的含义,加深对配置过程的理解。
结构体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 = 1MHz
- PLL.PLLN = 336 → 1MHz × 336 = 336MHz
- PLL.PLLP = 2 → 336MHz ÷ 2 = 168MHz
这样,我们就成功将系统主频调整到了168MHz!
HAL_RCC_ClockConfig() —— 分配时钟
HAL_RCC_ClockConfig()用于将主时钟分配给AHB、APB总线。
结构体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 = 168MHz
- APB1CLKDivider = RCC_HCLK_DIV4 → PCLK1 = 42MHz
- APB2CLKDivider = RCC_HCLK_DIV2 → PCLK2 = 84MHz
这样,我们的STM32就能跑得飞快!
总结
- HAL_RCC_OscConfig()选择HSE并开启PLL,设定168MHz时钟。
- HAL_RCC_ClockConfig()设定AHB = 168MHz,APB1 = 42MHz,APB2 = 84MHz。
- 所有外设正常运行,STM32高速运转!