STM32时钟源分析
SYSCLK(系统时钟),SYSCLK 的来源是 “可配置” 的,不是固定的;你可以选择 PLL、HSE、HSI 等作为 SYSCLK,最终 SYSCLK 的大小由 “选定的时钟源” 和 “相关分频配置” 共同决定。
- HSI:内部高速振荡器(默认~16MHz,精度低、无需外部晶振);
- HSE:外部高速晶振(你代码中用的 8MHz,精度高);
- PLLCLK:PLL 锁相环的输出时钟(你代码中 168MHz)。
PLL 是什么?核心作用是什么?
PLL 全称 Phase-Locked Loop(锁相环),1,提升时钟频率;2,统一时钟源;
PLL 输出频率 = HSE 频率 × (PLLN / PLLM) / PLLP
高级定时器的时钟来源为 APB2 时钟(PCLK2):
- 代码中
APB2CLKDivider = RCC_HCLK_DIV2,而AHBCLKDivider = RCC_SYSCLK_DIV1,因此:HCLK = SYSCLK / 1 = 168MHzPCLK2 = HCLK / 2 = 84MHz - 高级定时器的时钟频率规则:若 APB2 预分频系数 ≠ 1,则 TIMxCLK = PCLK2 × 2;若 =1,则 TIMxCLK = PCLK2。此处 APB2 分频为 2(≠1),因此:TIMxCLK = 84MHz × 2 = 168MHz。
uint8_t sys_stm32_clock_init(uint32_t plln, uint32_t pllm, uint32_t pllp, uint32_t pllq)
{HAL_StatusTypeDef ret = HAL_OK;RCC_ClkInitTypeDef rcc_clk_init_handle;RCC_OscInitTypeDef rcc_osc_init_handle;__HAL_RCC_PWR_CLK_ENABLE(); /* 使能PWR时钟 *//* 下面这个设置用来设置调压器输出电压级别,以便在器件未以最大频率工作时使性能与功耗实现平衡 */__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /* VOS = 1, Scale1, 1.2V内核电压,FLASH访问可以得到最高性能 *//* 使能HSE,并选择HSE作为PLL时钟源,配置PLL1,开启USB时钟 */rcc_osc_init_handle.OscillatorType = RCC_OSCILLATORTYPE_HSE; /* 时钟源为HSE */rcc_osc_init_handle.HSEState = RCC_HSE_ON; /* 打开HSE */rcc_osc_init_handle.PLL.PLLState = RCC_PLL_ON; /* 打开PLL */rcc_osc_init_handle.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL时钟源选择HSE */rcc_osc_init_handle.PLL.PLLN = plln;rcc_osc_init_handle.PLL.PLLM = pllm;rcc_osc_init_handle.PLL.PLLP = pllp;rcc_osc_init_handle.PLL.PLLQ = pllq;ret=HAL_RCC_OscConfig(&rcc_osc_init_handle); /*初始化RCC*/if(ret != HAL_OK){return 1; /* 时钟初始化失败,可以在这里加入自己的处理 */}/* 选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2*/rcc_clk_init_handle.ClockType = (RCC_CLOCKTYPE_SYSCLK \| RCC_CLOCKTYPE_HCLK \| RCC_CLOCKTYPE_PCLK1 \| RCC_CLOCKTYPE_PCLK2);rcc_clk_init_handle.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /* 设置系统时钟时钟源为PLL */rcc_clk_init_handle.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB分频系数为1 */rcc_clk_init_handle.APB1CLKDivider = RCC_HCLK_DIV4; /* APB1分频系数为4 */rcc_clk_init_handle.APB2CLKDivider = RCC_HCLK_DIV2; /* APB2分频系数为2 */ret = HAL_RCC_ClockConfig(&rcc_clk_init_handle, FLASH_LATENCY_5); /* 同时设置FLASH延时周期为5WS,也就是6个CPU周期 */if(ret != HAL_OK){return 1; /* 时钟初始化失败 */}/* STM32F405x/407x/415x/417x Z版本的器件支持预取功能 */if (HAL_GetREVID() == 0x1001){__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); /* 使能flash预取 */}return 0;
}
这个代码给我解释一下一、函数整体框架
函数名:sys_stm32_clock_init参数:plln/pllm/pllp/pllq → PLL 锁相环的核心配置参数(之前算 168MHz 时用的是 336/8/2/7)返回值:uint8_t → 0 = 初始化成功,1 = 失败
核心逻辑:
- 使能电源管理时钟,配置内核电压(保证高频下稳定工作);
- 配置外部晶振(HSE)和 PLL,生成高频时钟;
- 选定 PLL 作为系统时钟源,配置总线分频(HCLK/PCLK1/PCLK2);
- 配置 Flash 访问延时(避免高频下取指令出错);
- 可选:使能 Flash 预取功能(提升指令执行速度)。
二、逐段代码解释
1. 变量定义与电源时钟使能
运行
HAL_StatusTypeDef ret = HAL_OK;
RCC_ClkInitTypeDef rcc_clk_init_handle; // 总线时钟配置结构体
RCC_OscInitTypeDef rcc_osc_init_handle; // 振荡器(时钟源)配置结构体__HAL_RCC_PWR_CLK_ENABLE(); /* 使能PWR时钟 */
- 两个结构体是 STM32 HAL 库提供的 “时钟配置模板”:
RCC_OscInitTypeDef管 “时钟源(如 HSE、PLL)”,RCC_ClkInitTypeDef管 “时钟分配(如系统时钟、总线时钟)”; __HAL_RCC_PWR_CLK_ENABLE():使能电源管理(PWR)外设的时钟。后续要配置内核电压,必须先开启这个时钟(类似 “用电器先通电才能设置参数”)。
2. 配置内核电压级别
运行
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* VOS = 1, Scale1, 1.2V内核电压,FLASH访问可以得到最高性能 */
- 作用:STM32 的内核电压(VOS)会影响最大工作频率和功耗:
Scale1:内核电压 1.2V → 支持最高频率(STM32F4 最大 168MHz),性能最强;Scale2:内核电压 1.0V → 最高频率 144MHz,功耗更低;Scale3:内核电压 0.8V → 最高频率 100MHz,功耗最低。
- 你代码中选
Scale1,是为了支撑 168MHz 高频,保证系统高性能。
3. 配置振荡器(HSE + PLL)
这是时钟初始化的核心,目的是 “让 PLL 生成目标高频时钟”:
运行
/* 使能HSE,并选择HSE作为PLL时钟源,配置PLL1,开启USB时钟 */
rcc_osc_init_handle.OscillatorType = RCC_OSCILLATORTYPE_HSE; /* 时钟源类型:HSE(外部晶振) */
rcc_osc_init_handle.HSEState = RCC_HSE_ON; /* 打开外部晶振(HSE) */
rcc_osc_init_handle.PLL.PLLState = RCC_PLL_ON; /* 打开PLL(锁相环) */
rcc_osc_init_handle.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL的输入时钟源:HSE(外部晶振) */
rcc_osc_init_handle.PLL.PLLN = plln; /* PLL倍频系数(如336) */
rcc_osc_init_handle.PLL.PLLM = pllm; /* PLL分频系数(如8) */
rcc_osc_init_handle.PLL.PLLP = pllp; /* PLL主输出分频系数(如2) */
rcc_osc_init_handle.PLL.PLLQ = pllq; /* PLL辅助输出分频系数(如7,给USB用) */ret=HAL_RCC_OscConfig(&rcc_osc_init_handle); /* 执行振荡器配置 */
if(ret != HAL_OK) return 1; /* 配置失败返回1 */
- 关键参数对应之前的 168MHz 计算:HSE(8MHz)→ PLLM(8 分频,8MHz/8=1MHz)→ PLLN(336 倍频,1MHz×336=336MHz)→ PLLP(2 分频,336MHz/2=168MHz)→ 最终 PLL 输出 168MHz;
PLLQ:专门给 USB、SDIO 等外设提供精准时钟(如 336MHz/7=48MHz,刚好满足 USB 要求);HAL_RCC_OscConfig:将上述配置写入 STM32 的 RCC 寄存器,让硬件生效。如果 HSE 没起振或 PLL 参数非法(如超出频率范围),会返回失败。
4. 配置系统时钟与总线分频
这一步是 “将 PLL 生成的高频时钟分配给系统和外设”:
运行
/* 选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2*/
rcc_clk_init_handle.ClockType = (RCC_CLOCKTYPE_SYSCLK \| RCC_CLOCKTYPE_HCLK \| RCC_CLOCKTYPE_PCLK1 \| RCC_CLOCKTYPE_PCLK2); /* 要配置的时钟类型:系统时钟+3条总线时钟 */rcc_clk_init_handle.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /* 系统时钟(SYSCLK)来源:PLL输出(168MHz) */
rcc_clk_init_handle.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB总线分频系数:1 → HCLK = SYSCLK/1 = 168MHz */
rcc_clk_init_handle.APB1CLKDivider = RCC_HCLK_DIV4; /* APB1总线分频系数:4 → PCLK1 = 168MHz/4 = 42MHz */
rcc_clk_init_handle.APB2CLKDivider = RCC_HCLK_DIV2; /* APB2总线分频系数:2 → PCLK2 = 168MHz/2 = 84MHz */ret = HAL_RCC_ClockConfig(&rcc_clk_init_handle, FLASH_LATENCY_5); /* 执行配置+设置Flash延时 */
if(ret != HAL_OK) return 1; /* 配置失败返回1 */
- 核心概念:STM32 的时钟树是 “分级分配” 的,各时钟对应不同用途:
时钟名称 频率(你的配置) 用途 SYSCLK 168MHz CPU 核心时钟(执行指令的速度) HCLK 168MHz AHB 总线时钟(给 DMA、GPIO 等外设) PCLK1 42MHz APB1 总线时钟(给低速外设:UART2、I2C、定时器 2-7 等,最大支持 42MHz) PCLK2 84MHz APB2 总线时钟(给高速外设:SPI1、定时器 1/8-11 等,最大支持 84MHz) FLASH_LATENCY_5:设置 Flash 访问延时(5WS,即 6 个 CPU 周期)。因为 CPU 跑 168MHz 高频时,从 Flash 读取指令需要时间,若延时不够,会出现 “指令还没读完就执行” 的错误,导致系统崩溃。频率越高,需要的 Flash 延时越大(168MHz 对应 5WS 是 STM32F4 的规范要求)。
5. 使能 Flash 预取功能(可选)
运行
/* STM32F405x/407x/415x/417x Z版本的器件支持预取功能 */
if (HAL_GetREVID() == 0x1001)
{__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); /* 使能flash预取 */
}
- 作用:Flash 预取功能是 “提前读取后续指令到缓存”,减少 CPU 等待 Flash 读取的时间,提升指令执行效率(类似看书时提前翻好下一页,不用等需要时再翻);
HAL_GetREVID() == 0x1001:判断芯片版本是否为 “Z 版本”(只有这类版本支持预取功能),避免给不支持的芯片配置导致错误。
三、函数调用示例(你之前的用法)
运行
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
- 代入参数后,最终生成的时钟:SYSCLK=168MHz(CPU 主频)、HCLK=168MHz、PCLK1=42MHz、PCLK2=84MHz、USB 时钟 = 48MHz;
- 这个配置是 STM32F4 的 “高性能标准配置”,既能发挥芯片最大性能,又能满足绝大多数外设(如你之前的步进电机定时器)的时钟需求。
四、关键注意事项
- 芯片兼容性:这段代码仅适用于 STM32F4 系列(如 F405、F407),其他系列(如 F1、F7、H7)的 PLL 参数范围、Flash 延时、电压级别配置不同,不能直接复用;
- PLL 参数限制:STM32F4 的 PLL 有严格参数范围(如 PLLN 43~432、PLLM 2~63、PLLP 2/4/6/8),超出范围会导致配置失败;
- 外部晶振要求:代码默认 HSE 为 8MHz,若你的外部晶振是其他频率(如 12MHz),需重新计算 PLL 参数(保证 PLL 输入 1~2MHz,输出不超过 168MHz)。
总结
这段代码的核心是 “用 HSE+PLL 打造高性能时钟系统”:
- 外部晶振(HSE)提供稳定的 “原始时钟”,PLL 负责 “倍频到高频”;
- 总线分频(AHB/APB1/APB2)兼顾 “高性能” 和 “外设兼容性”(如 APB1 限制 42MHz);
- Flash 延时和电压配置是 “高频工作的保障”,避免系统崩溃。
它是 STM32F4 开发中最常用的时钟初始化模板,后续所有外设(定时器、UART、SPI 等)的时钟都基于这个配置,因此这个函数的正确性直接决定整个系统的稳定性。
