1 STM32时钟系统
STM32有多种时钟源,主要是以下这几种。
| 时钟源 | 全称 | 特点 | 典型频率 |
|---|---|---|---|
| HSE | 高速外部时钟 | 精度高,接外部石英晶体。最常用。 | 4MHz - 26MHz |
| HSI | 高速内部时钟 | 成本低、启动快,但精度受温度影响。 | 16MHz (RC振荡器) |
| PLL | 锁相环 | 核心角色,负责把低频倍增为高频。 | 168MHz+ |
| LSE | 低速外部时钟 | 专门给实时时钟(RTC)用。 | 32.768kHz |
| LSI | 低速内部时钟 | 给独立看门狗(IWDG)用,低功耗。 | 32KHz到40kHz |
其中HSE,LSE是外部的。如果没有,就会自动用内部的HSI和LSI。
HSI和HSE对比:
| 特性 | HSI (High-Speed Internal) | HSE (High-Speed External) |
|---|---|---|
| 来源 | 芯片内置的 RC 振荡器 | 外部石英晶体或有源晶振 |
| 精度 | 较低(误差约 ±1% 至 ±5%) | 极高(通常以 ppm 百万分比计) |
| 稳定性 | 差(受温度、电压影响极大) | 非常稳定 |
| 成本 | 零成本(无需外围电路) | 需要购买晶振、电容并占用引脚 |
| 启动时间 | 极快(微秒级,瞬间唤醒) | 较慢(毫秒级,需等待起振稳定) |
| 功耗 | 相对略低 | 略高 |
也就是说一般是实验用HSI还是可以,但是如果要用到高精度的时钟,比如USB,CAN,高波特率UART,ETH等,就必须用HSE才行。实际上一个晶振也不贵,小几块钱,一般的板子都会配。
对于STM32,时钟的基本流程就是:时钟源 -> 锁相环(PLL)倍频 -> 系统时钟(SYSCLK) -> 总线分频 -> 外设。
以F103为例看看具体的时钟路径:
初始阶段:启动外部HSE 8MHz晶振。
加压阶段: 外部 8MHz 的 HSE 信号进入 PLL。通过倍频(如 9 倍),产生72MHz的超高频信号。
核心枢纽: 这个 72MHz 信号被选作 SYSCLK(系统时钟)。它是单片机的心脏跳动频率。
分流阶段: SYSCLK 经过 AHB 预分频器分发给不同的总线:
HCLK: 供给 CPU 核心、内存、DMA,通常也是 72MHz。
PCLK1 (APB1): 低速总线,最高 36MHz。连接串口 2-5、定时器等。
PCLK2 (APB2): 高速总线,最高 72MHz。连接 GPIO、串口 1、ADC 等。
这个是之前我发过的配置的图:

这里不能选HSE,应就是没开。下面会写一下怎么开。
2 HSI/HSE
HSI是内部晶振,HSE是外部晶振。
开启 HSE 硬件接口:
1 切换到 Pinout & Configuration(引脚和配置)选项卡。
2 在左侧列表中找到 System Core -> RCC。
3 在右侧的 Mode 面板中,找到 High Speed Clock (HSE)。
4 根据你的硬件连接选择对应模式:
Crystal/Ceramic Resonator(无源晶振): 如果你的电路板上焊接的是普通的金属壳两脚或四脚晶振。
BYPASS Clock Source(有源晶振): 如果你使用的是外部提供的有源时钟信号(例如从 ST-Link 提供的 MCO 时钟)。
之后就可以看到完整的时钟配置,可以选择HSE了。

在HCLK直接填入希望的频率168,其它的会帮你自动算好。
LSE/LSI目前没用到,查了一下,只有在超低功耗,独立看门狗,时间戳等场景采用。所以这里先暂时不写了。
至于PLL,这个基本上是纯硬件的,可以参考之前的文章。https://blog.csdn.net/fanged/article/details/156566722
在待机时,PLL将被禁止。然后STM32还有一个PLL2S,是给I2S提供时钟的,这个后面再看吧。
3 分频
这个倒是STM32的特色之一吧。就是 AHB、APB1、APB2这几个。
HCLK: 供给 CPU 核心、内存、DMA,通常也是 168MHz。
PCLK1 (APB1): 低速总线,最高 84MHz。连接串口 2-5、定时器等。
PCLK2 (APB2): 高速总线,最高 168MHz。连接 GPIO、串口 1、ADC 等。
| 外设名称 | 所属总线 | 配置频率 | 说明 |
|---|---|---|---|
| UART1 | APB2 | 84 MHz | UART1 是高速串口,通常挂在更快的 APB2 上。 |
| UART2 ~ UART6 | APB1 | 42 MHz | 其余串口(如 UART2, 3, 4, 5)为了节能通常挂在 APB1。 |
| I2C1, I2C2, I2C3 | APB1 | 42 MHz | 所有的 I2C 接口在 F4 系列中都属于低速总线 APB1。 |
| ADC1, 2, 3 | APB2 | 84 MHz | ADC 需要高速时钟进行高频采样,固定在 APB2。 |
但是I2C实际的速率是400KHz。

所以这里还会再芯片里面再分,把42M切成400K。这部分是硬件完成的。
4 代码
在界面上配置了时钟,其实就是CubeMX帮你完成了代码,最后还是得回到代码的流程,在mian.h中。其实核心的也就10来行,和上面的配置图完全对应。真不多。重在理解。
cpp
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
这里再对比一下树莓派Pico的时钟初始化。
cpp
#include "hardware/pll.h"
#include "hardware/clocks.h"
#include "hardware/xosc.h"
void init_clock_manual() {
// 1. 初始化外部晶振 (XOSC) - Pico 板载通常是 12MHz
xosc_init();
// 2. 配置 PLL_SYS (系统倍频器)
// 公式: Fout = (Fref / REFDIV) * FBDIV / (POSTDIV1 * POSTDIV2)
// 这里将 12MHz 经过 PLL 变成 125MHz
pll_init(pll_sys, 1, 1500 * MHZ, 6, 2);
// 3. 切换系统时钟源到 PLL_SYS
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
125 * MHZ,
125 * MHZ);
// 4. 配置外设时钟 (clk_peri)
// 外设时钟通常直接挂在 clk_sys 上,用于串口、SPI、I2C 等
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
125 * MHZ,
125 * MHZ);
}
可以看出,树莓派Pico的总线只有1个,就是clk_sys。而STM32使用了APB1,甚至APB2。查了一下资料据说是可以更好控制能耗。而Pico用了多层AHB总线矩阵(Multi-layer AHB Crossbar)可以避免一条总线带来的问题。。
好了,先到这吧。。。