RIOT STM32 时钟树配置详解:从 SYSCLK 源选择到 PLL、APB 与 MCO 参数的完整实践指南
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
本文以 RIOT 仓库中 STM32 CPU 模块的官方文档 cpu/stm32/doc.md 为主体,系统讲解 RIOT 在 STM32 系列上配置系统时钟的方法:如何选择 SYSCLK 时钟源(HSE/HSI/MSI/PLL)、如何按芯片家族设置 PLL 的 M/N/P/Q/R 或预分频/倍频参数、如何调整 APB1/APB2 分频,以及如何配置 MCO 时钟输出。读完本文后,你将能够对照芯片参考手册为任意受支持的 STM32 家族设定精确时钟,并理解这些配置常量在 RIOT 源码中的实际作用位置(stmclk_*初始化链路与 Kconfig 默认值)。
1. 适用范围:RIOT 支持的 STM32 家族
RIOT 的 STM32 CPU 模块包含 STM32 各芯片家族的代码与定义,文档明确列出支持的家族为:F0、F1、F2、F3、F4、F7、G0、G4、L0、L1、L4、L5 和 WB。所有下文描述的时钟常量都可以通过CFLAGS从命令行或应用 Makefile 中配置(见 cpu/stm32/doc.md)。
从源码结构看,仓库中的时钟实现覆盖面实际上更广:
- 时钟配置文件目录 cpu/stm32/kconfigs 下按家族划分了
c0、f0、f1、f2、f3、f4、f7、g0、g4、h7、l0、l1、l4、l5、mp1、u3、u5、wb、wl等子目录,其中还存在f0f1f3g0g4l0l1l4wbwl、f2f4f7mp1、g4l4l5、l4l5wx等跨家族共享目录; - 时钟实现代码目录 cpu/stm32/stmclk 提供了每个家族(组)的
stmclk_*.c源文件(见下文第 2.2 节)。
可以推断,这些c0、h7、mp1、u3、u5、wl家族是后续补充进时钟框架的,文档列出的 13 个家族是核心支持范围。
配置时钟前,文档给出的首要建议是:先阅读目标 CPU 的参考手册,了解其时钟树结构与可配置项,再对照下文的常量进行设置。
2. 时钟配置机制与初始化链路
2.1 统一入口:stmclk_init_sysclk()
对外接口定义在 cpu/stm32/include/stmclk.h,核心函数是stmclk_init_sysclk()。头文件注释描述了它的标准动作序列:
- 使能 HSI;
- 使用 HSI 作为系统时钟(确保进入一个确定的状态);
- 配置 Flash 等待状态(wait states)与 AHB/APB 总线分频;
- (可选)使能 HSE / MSI;
- 配置 PLL;
- 将系统时钟切换到 PLL;
- 若 HSI 未被使用则将其关闭。
调用时机可以从 cpu/stm32/cpu_init.c#L376 确认:CPU 初始化流程cpu_init()中直接调用stmclk_init_sysclk(),也就是说时钟树在系统启动早期即按配置常量建立完成。
同一头文件还定义了若干配套接口:
| 函数 | 作用 | 备注 |
|---|---|---|
stmclk_enable_hsi()/stmclk_disable_hsi() | 使能/禁用 HSI | 禁用前会检查 HSI 是否正在驱动 SYSCLK 或 PLL,避免误关 |
stmclk_enable_lfclk()/stmclk_disable_lfclk() | 配置并启用/禁用低速时钟域 | 低速时钟输入可为 LSE 或 LSI,在板级periph_conf.h中配置 |
stmclk_dbp_unlock()/stmclk_dbp_lock()/stmclk_dbp_is_locked() | 解锁/锁定/查询备份域写访问 | 操作 RTC、备份寄存器等备份域资源前需要解锁 |
这些常量"由板级periph_conf.h配置"的说法与头文件注释一致,而本文第 3~6 节重点介绍的是作用于时钟树本身的家族级常量。
2.2 按家族分治的时钟实现
cpu/stm32/stmclk 目录下的源文件按时钟架构差异分族实现:
- stmclk_f2f4f7.c:F2/F4/F7 家族(M/N/P/Q/R 型 PLL);
- stmclk_f0f1f3.c:F0/F1/F3 家族(预分频+倍频型 PLL);
- stmclk_l0l1.c:L0/L1 家族;
- stmclk_l4wx.c 与 stmclk_l5.c:L4、L5 家族;
- stmclk_gx.c:G0/G4 家族;
- 以及 stmclk_c0.c、stmclk_h7.c、stmclk_mp1.c、stmclk_u3.c、stmclk_u5.c、stmclk_common.c 等对应 C0、H7、MP1、U3、U5 家族与公共逻辑。
这种"一个时钟架构一个源文件"的组织方式意味着:不同家族的常量命名虽有差异(如 L0/L1 用CONFIG_CLOCK_PLL_DIV,F0/F1/F3 用CONFIG_CLOCK_PLL_PREDIV),但都汇入同一条stmclk_init_sysclk()初始化链路。
3. SYSCLK 系统时钟源选择
文档指出:所有 STM32 CPU 都带有时钟配置代码和宏。虽然 SYSCLK 可用的输入时钟源有多种,但同一时刻只能使用一种。RIOT 提供的四大时钟源如下:
- HSE(High Speed External,高速外部晶振)
- 是否存在取决于板级硬件,由板级常量
CONFIG_BOARD_HAS_HSE指明; - 外部晶振频率(Hz)由
CONFIG_CLOCK_HSE指定; - 要直接以 HSE 作为系统时钟,将
CONFIG_USE_CLOCK_HSE置 1。
- 是否存在取决于板级硬件,由板级常量
- HSI(High Speed Internal,高速内部振荡器)
- 频率按家族为 8MHz 或 16MHz;
- 要直接以 HSI 作为系统时钟,将
CONFIG_USE_CLOCK_HSI置 1。
- MSI(Multi-Speed Internal,多速内部振荡器)
- 仅 L0、L1、L4、L5、WB 家族可用;
- 要直接以 MSI 作为系统时钟,将
CONFIG_USE_CLOCK_MSI置 1; - MSI 频率(Hz)由
CONFIG_CLOCK_MSI指定。
- PLL(锁相环)
- 提供精细可配、可达到精确且高主频的时钟源;
- 在 RIOT 中,PLL 是 SYSCLK 的默认时钟源;
- 将
CONFIG_USE_CLOCK_PLL置 1 即选择 PLL(默认值)。
3.1 Kconfig 中的默认值与依赖
文档中所述常量在现仓库中由 Kconfig 承载,核心定义在 cpu/stm32/kconfigs/Kconfig.clk,可以看到:
- 时钟源选择是一个
choice块,default USE_CLOCK_PLL印证了"PLL 为默认 SYSCLK 源"; USE_CLOCK_HSE带有depends on BOARD_HAS_HSE——即没有外部晶振的板子根本无法选择 HSE,与文档中CONFIG_BOARD_HAS_HSE的板级约束完全对应;CLOCK_HSE的默认频率按家族区分:G0、G4、L0、L1、MP1 家族默认 24000000 Hz,其余默认 8000000 Hz。
3.2 PLL 输入源的选择规则
文档给出的 PLL 输入源规则是时钟配置中最容易踩坑的点:
- 通用规则:若板子提供 HSE,HSE 自动被选为 PLL 的输入源;否则选用 HSI。
- L4 与 WB 的特殊规则:PLL 输入源可以在 HSE、HSI、MSI 三者中显式选择(默认 MSI)。通过以下常量之一置 1 来配置:
CONFIG_CLOCK_PLL_SRC_HSICONFIG_CLOCK_PLL_SRC_HSECONFIG_CLOCK_PLL_SRC_MSI
4. PLL 参数配置:两种体系
文档将 STM32 的 PLL 划分为两种参数体系,这是理解各家族 Kconfig 文件差异的钥匙。
4.1 M/N/P/Q/R 参数体系(F2/F4/F7、G0/G4、L4/L5/WB)
对stm32f[2|4|7]、stm32g[0|4]、stm32l[4|5|wb]:
- PLL 可配置参数为M(输入分频器)、N(倍频器)、P/Q/R(输出分频器,R 可选);
- 在F2/F4/F7上,P 输出分频得到的时钟用作 SYSCLK 输入,Q 输出分频得到的时钟可用作 48MHz(如 USB/SDIO);
- 在G0/G4 与 L4/L5/WB上,R 输出分频得到的时钟用作 SYSCLK 输入;
- 对应配置常量为
CONFIG_CLOCK_PLL_M、CONFIG_CLOCK_PLL_N、CONFIG_CLOCK_PLL_P、CONFIG_CLOCK_PLL_Q、CONFIG_CLOCK_PLL_R。
这些常量如何落地到寄存器?查看 cpu/stm32/stmclk/stmclk_f2f4f7.c 可以看到,Kconfig 值被直接拼装进RCC_PLLCFGR的位段:
#define PLL_M (CONFIG_CLOCK_PLL_M << RCC_PLLCFGR_PLLM_Pos) #define PLL_N (CONFIG_CLOCK_PLL_N << RCC_PLLCFGR_PLLN_Pos) #define PLL_P (((CONFIG_CLOCK_PLL_P / 2) - 1) << RCC_PLLCFGR_PLLP_Pos) #define PLL_Q (CONFIG_CLOCK_PLL_Q << RCC_PLLCFGR_PLLQ_Pos) #define PLL_R (CONFIG_CLOCK_PLL_R << RCC_PLLCFGR_PLLR_Pos)注意PLL_P宏中的(P/2) - 1编码——寄存器值与逻辑分频比 2/4/6/8 的映射并非线性直接写入,修改CLOCK_PLL_P时只需按分频比填写,寄存器编码由stmclk层完成。
同文件还显示,F2/F4/F7 家族额外支持PLLI2S 与 PLLSAI两套独立 PLL 参数(CONFIG_CLOCK_PLLI2S_M/N/P/Q/R、CONFIG_CLOCK_PLLSAI_M/N/P/Q/R),在未显式配置时有默认值(例如 PLLI2S 的 P/Q/R 默认 8,注释标明其用途分别为 SPDIF-Rx 时钟 48MHz、USB/SDIO/SDMMC 备用 48MHz 时钟、I2S 时钟 48MHz)。如果你的应用需要精确 48MHz 的 USB/I2S/SAI 时钟,这一层也是可配置面。
4.2 预分频 + 倍频参数体系(F0/F1/F3、L0/L1)
对stm32f[0|1|3]与stm32l[0|1],PLL 只由一个预分频参数和一个倍频参数构成:
CONFIG_CLOCK_PLL_DIV(L0、L1)或CONFIG_CLOCK_PLL_PREDIV(F0、F1、F3)——输入预分频;CONFIG_CLOCK_PLL_MUL——倍频。
对应实现分别在 cpu/stm32/stmclk/stmclk_f0f1f3.c 与 cpu/stm32/stmclk/stmclk_l0l1.c。
4.3 以 F4 家族为例:查看 Kconfig 中的 PLL 默认参数
家族级 PLL 参数在 cpu/stm32/kconfigs/f4/Kconfig.clk 中定义(需打开CUSTOM_PLL_PARAMS选项"Configure PLL parameters"后才会暴露可配置界面)。以 F4 为例,仓库给出了大量按"HSE 频率 × 目标主频档"组合的默认值,可直接作为选型参考:
CLOCK_PLL_M:CLOCK_MAX_84MHZ或100MHZ档且 HSE=25MHz 时默认 25;CLOCK_MAX_180MHZ档且 HSE=16MHz 或 12MHz 时默认 12;兜底默认 4;CLOCK_PLL_N:合法范围range 50 432。默认值随 HSE 频率与主频档变化,例如 HSE=8MHz 且 180MHz 档默认 180(即 8MHz/8? 不,此处 M 由 Kconfig 另行决定);当启用periph_usbdev_clk且芯片无备用 48MHz 时钟源(!HAVE_CLOCK_ALT_48MHZ)时,默认值会自动收敛到能产生精确 48MHz 的组合(如 180MHz 档下 HSE=16MHz 取 336 而非 360)。
这说明 RIOT 的默认 PLL 参数并非拍脑袋给定,而是围绕"板级 HSE 实际频率 + 目标 SYSCLK + USB 48MHz 需求"三个约束推导的。自定义 M/N/P/Q/R 时应遵循同样的推理路径,并以芯片参考手册中的 PLL 输入/输出频率上下限为边界。
F2/F4/F7/MP1 共享的输出分频定义在 cpu/stm32/kconfigs/f2f4f7mp1/Kconfig.clk:
CLOCK_PLL_P:choice 提供 2/4/6/8 四档(MP1 固定 3),F4 84MHz 档默认 4,其余默认 2;CLOCK_PLL_Q:注释标明其用于"USB OTG FS 与 SDIO 时钟",合法范围range 2 15,默认值按家族与主频档取值(F2 默认 5、F4 100MHz 档默认 4、F7 默认 9 等)。
5. APB1/APB2 总线分频
文档说明:APB 分频器用于计算 PCLK1 与 PCLK2 的时钟值,配置方法:
CONFIG_CLOCK_APB1_DIV与CONFIG_CLOCK_APB2_DIV——直接填入频率分频系数即可;- F0 与 G0 家族没有 APB2,在这些家族上设置
CONFIG_CLOCK_APB2_DIV不产生任何效果。
Kconfig 层(cpu/stm32/kconfigs/Kconfig.clk)将这两个分频器组织为 1/2/4/8/16 五档的 choice 选项,并给出了按家族区分的工程默认值:
- APB1:F2、F4(180MHz 档)、F7、L4、L5、U5、WB、WL 默认
/4;F1、F3、F4(其余档位)、MP1 默认/2;其余家族默认/1; - APB2:
depends on !CPU_FAM_G0 && !CPU_FAM_F0从配置层面直接禁用了 F0/G0(与文档"APB2 不可用"的说明一致);F2、F4(180MHz 档)、F7、L4、L5、U5、WB、WL、MP1 默认/2,其余默认/1。
这些默认值体现的通用原则是:高频家族把 APB 域分得低一些以释放外设带宽与主频空间,低频/简单家族则保持 1:1。调整CLOCK_APB1_DIV/CLOCK_APB2_DIV前,请核对目标外设(定时器、USART、SPI 等)允许的最高 PCLK 频率。
6. MCO(Microcontroller Clock Output)配置
MCO 将芯片内部时钟引出到指定引脚,常用于用示波器/逻辑分析仪验证时钟配置结果。文档给出的配置方法如下:
MCO1(PA8)
- 置 1
CONFIG_CLOCK_ENABLE_MCO1使能 MCO1; CONFIG_CLOCK_MCO1_PRE指定 MCO1 预分频(默认 1);- 可选时钟源为 HSE、HSI、PLL(默认)。分别置 1 以下常量来选择源:
CONFIG_CLOCK_MCO1_USE_HSECONFIG_CLOCK_MCO1_USE_HSICONFIG_CLOCK_MCO1_USE_PLL
MCO2(PC9)
- 置 1
CONFIG_CLOCK_ENABLE_MCO2使能 MCO2; CONFIG_CLOCK_MCO2_PRE指定 MCO2 预分频(默认 1);- 可选时钟源为 HSE、SYSCLK、PLLI2S、PLL(默认),文档列出的源选择常量写作
CONFIG_CLOCK_MCO1_USE_HSE、CONFIG_CLOCK_MCO1_USE_SYSCLK、CONFIG_CLOCK_MCO1_USE_PLLI2S、CONFIG_CLOCK_MCO1_USE_PLL(此处沿用原文档的命名,实际配置时请以对应家族 Kconfig 中的实际符号为准)。
从源码结构看,MCO 的 Kconfig 定义位于 cpu/stm32/kconfigs/f0f1f3g0g4l0l1l4wbwl/Kconfig.clk,覆盖 F0/F1/F3/G0/G4/L0/L4/WB/WL 家族(PA8 引脚):
CLOCK_ENABLE_MCO打开后,源选择 choice 提供PLLCLK(默认)、HSE、HSI、SYSCLK,以及按家族条件启用的LSE(G0/G4/L0/L1/L4/WB/WL)、LSI(同上)、MSI(L0/L1/L4/WB/WL);- 预分频 choice 提供 1/2/4/8/16 五档,且 F0/F1/F3/G0 额外支持 32/64/128 三档(这些家族的
CLOCK_MCO_PRE可低至 128 分频)。
这与文档对 MCO1 的描述互为印证:低档位家族的 MCO 预分频范围明显更宽,方便把高速时钟降到示波器可测频段。
7. 实操路径小结
结合本文与仓库内容,为某块 STM32 板卡定制时钟的推荐步骤:
- 查阅芯片参考手册,确定目标 SYSCLK 频率与可用的 HSE 晶振频率;
- 确认板级
periph_conf.h与 Kconfig 中的BOARD_HAS_HSE、CLOCK_HSE(见 cpu/stm32/kconfigs/Kconfig.clk)与硬件一致; - 默认使用 PLL(
USE_CLOCK_PLL);若 L4/WB 需要从 HSI/HSE/MSI 中显式指定 PLL 输入源,则按文档第 3.2 节设置CONFIG_CLOCK_PLL_SRC_*; - 需要自定义 PLL 时打开
CUSTOM_PLL_PARAMS,在家族目录(如 cpu/stm32/kconfigs/f4/Kconfig.clk、cpu/stm32/kconfigs/f2f4f7mp1/Kconfig.clk)对应的 Kconfig 中按第 4 节的参数体系填写 M/N/P/Q/R 或 PREDIV/MUL,并注意 USB 48MHz(Q 分频或periph_usbdev_clk)约束; - 按目标外设带宽需求选择
CLOCK_APB1_DIV/CLOCK_APB2_DIV(F0/G0 无 APB2); - 若需验证实际输出频率,按第 6 节使能 MCO1/MCO2 并用外设测量。
所有常量最终都由 cpu/stm32/stmclk 中对应家族的stmclk源文件在cpu_init()→stmclk_init_sysclk()阶段写入 RCC 寄存器,任何配置偏离参考手册允许范围的行为都应在该层暴露,这也是排查"时钟配置不生效"问题时的第一现场。
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考