一、时钟树


一、目标
- 外部晶振 HSE = 25 MHz
- 最终系统主频 sys_clk = 600 MHz
- 需要通过 PLL 升频,选择合适的 PLL 参数
- 确保各模块时钟合理分配(CPU、总线、外设等)
📐 二、时钟源与 PLL 架构解析(基于图2)
✅ 1. 时钟源输入
| 源 | 频率 | 类型 |
|---|---|---|
| HSE (OSC_IN) | 25 MHz | 外部高速晶振 |
| MSI | 16 MHz | 内部RC |
| HSI | 64 MHz | 内部高速RC |
| LSE | 32.768 kHz | 外部低速晶振 |
| LSI | 32 kHz | 内部低速RC |
我们选用 HSE = 25 MHz 作为 PLL 的输入源。
✅ 2. PLL 结构(图2右半部分)
芯片支持多个 PLL:
- PLL1 → 输出
pll1_a,pll1_b,pll1_c(用于 CPU 和总线) - PLL2 , PLL3 → 可能用于图像、音频等
- SHRPLL → 用于高速接口(如 PHY)
我们重点关注 PLL1 ,因为它驱动了 M7 CPU 和 AXI bus clock。
PLL1 输入选择(PLLSRC)
PLLSRC: 选择 HSE (25 MHz)
PLL1 内部结构:
- VCO 输入频率范围:440 MHz ~ 800 MHz
- VCO 输出频率 = (HSE * NF) / NR
- 输出频率 = VCO / ND
其中:
*NF:倍频系数(Numerator of Multiplication)/NR:分频系数(Pre-divider)/ND:后分频(Post-divider)
⚙️ 三、计算:如何用 25 MHz 得到 600 MHz?
目标:sys_clk = 600 MHz
在图1中可以看到:
sys_clk来自 MUX,可以选择pll1_a_clk或pll2_a_clksys_clk经过 Divider 后成为sys_div_clk(即 600 MHz)- 最终给 M7 CPU 的时钟是
M7 CPU clock,它来自sys_div_clk,通常不额外分频
所以我们需要:
pll1_a_clk = 600 MHz
而 pll1_a_clk 是由 PLL1 的 s_pll1_a_clk 经过 CLK_DIV 分频得到的。
但注意:
- 图2 中
s_pll1_a_clk是 VCO 输出 经过PLL1ADIV分频后的结果 - 所以我们要先让 VCO 输出足够高,再分频到 600 MHz
设计 PLL1 参数
步骤1:确定 VCO 输出频率
我们希望 s_pll1_a_clk = 600 MHz
假设 PLL1ADIV = 1(不分频),则 VCO 必须输出 600 MHz
VCO 范围:440 MHz ~ 800 MHz → ✅ 600 MHz 在范围内
所以:
VCO_freq = 600 MHz
步骤2:计算 PLL1 倍频因子
输入 = HSE = 25 MHz
VCO = (HSE × NF) / NR = 600 MHz
(25 × NF) / NR = 600
→ NF / NR = 600 / 25 = 24
所以可以取:
NF = 24NR = 1
✅ 满足条件
注意:某些芯片要求 NF ≥ 8,且 NR 为整数。24/1 是合法值。
步骤3:设置 PLL1ADIV
为了得到 s_pll1_a_clk = 600 MHz,我们不需要分频:
PLL1ADIV = 1(即 CLK_DIV = 1)
✅ PLL1 配置总结
表格
| 参数 | 值 |
|---|---|
| PLL1SRC | HSE (25 MHz) |
| NF | 24 |
| NR | 1 |
| ND (for s_pll1_a_clk) | 1 |
| PLL1ADIV | 1 |
| PLL1EN | Enable |
→ 输出 s_pll1_a_clk = 600 MHz
🔄 四、系统时钟路径(图1)
1. 主系统时钟选择
在图1顶部:
sys_clk选择器(MUX)连接到:pll1a_clk→ ✅ 我们选这个pll2a_clk,pll3a_clk,shrpll_clk,hsi_clk,msi_clk,lse_clk
所以我们配置:
sys_clk = pll1a_clk = 600 MHz
2. 系统分频(sys_div_clk)
图1中:
sys_clk进入一个 Divider ,输出sys_div_clk- 默认可能不除(divider=1),所以
sys_div_clk = 600 MHz
→ 用于驱动:
- M7 CPU clock
- AXI bus clock
✅ 所以 M7 CPU 运行在 600 MHz
3. 总线时钟配置
图1底部显示:
- AHBx, APBx 等总线时钟都来源于
sys_div_clk或其分频
例如:
AHB9 Bus clock= 300 MHz(由sys_div_clk / 2得来)APB1 Bus clock= 150 MHz(sys_div_clk / 4)
这些可以通过 ICG(Clock Gating)控制
4. 其他重要模块时钟
表格
| 模块 | 时钟来源 | 频率 |
|---|---|---|
| M4 CPU | M4 CPU clock = 300 MHz(来自 sys_div_clk / 2) |
300 MHz |
| AXI SRAM | AXI bus clock = 600 MHz |
600 MHz |
| DCMU | sys_div_clk |
600 MHz |
| EXT1 | sys_div_clk |
600 MHz |
| I/O Pads | IO clocks |
可配置,一般 ≤ 100 MHz |
🔧 五、完整时钟树配置表(基于 25MHz HSE → 600MHz)
表格
| 模块 | 时钟源 | 频率 | 配置说明 |
|---|---|---|---|
| HSE | 外部晶振 | 25 MHz | 使用 25 MHz 晶振 |
| PLL1 Input | HSE | 25 MHz | PLL1SRC = HSE |
| PLL1 VCO | (25 × 24) / 1 = 600 MHz | 600 MHz | NF=24, NR=1 |
| s_pll1_a_clk | VCO / 1 | 600 MHz | PLL1ADIV = 1 |
| sys_clk | MUX → pll1a_clk | 600 MHz | 选择 PLL1A 输出 |
| sys_div_clk | sys_clk / 1 | 600 MHz | 不分频 |
| M7 CPU Clock | sys_div_clk | 600 MHz | 主处理器运行频率 |
| AXI Bus Clock | sys_div_clk | 600 MHz | 高速总线 |
| M4 CPU Clock | sys_div_clk / 2 | 300 MHz | 二级 CPU |
| AHBx Bus Clock | sys_div_clk / 2 | 300 MHz | 外设总线 |
| APBx Bus Clock | sys_div_clk / 4 | 150 MHz | 低速外设 |
| I/O Pad Clock | IO clocks | ≤100 MHz | 通常由独立分频器生 |
cpp
ErrorStatus ConfigureSystemClock_600MHz(void)
{
RCC_DeInit(); // 复位时钟系统
/* 1. 使能并等待HSE时钟稳定 */
RCC_ConfigHse(RCC_HSE_ENABLE); // 使能 HSE
if(RCC_WaitHseStable() == SUCCESS) // 等待HSE时钟起振稳定
{
/* 2. 配置系统时钟分频 */
RCC_ConfigSysclkDivider(RCC_SYSCLK_DIV1); // 系统时钟 = 600 MHz
RCC_ConfigSysbusDivider(RCC_BUSCLK_DIV2); // AHB = 300 MHz
/* 3. 配置内核和总线时钟源 */
RCC_ConfigM7Clk(RCC_M7HYPERCLK_SRC_PLL1A);
RCC_ConfigAXIClk(RCC_AXIHYPERCLK_SRC_PLL1A);
/* 4. 配置总线分频 */
RCC_ConfigAXIclkDivider(RCC_AXICLK_DIV2); // AXI 时钟 = 300 MHz
RCC_ConfigAXIHyperDivider(RCC_AXICLK_HYP_DIV2); // AXI HyperRAM 时钟
__RCC_DELAY_US(1); // 等待分频器稳定
/* 5. 配置APB总线分频 */
RCC_ConfigAPBclkDivider(RCC_APB1CLK_DIV2, RCC_APB2CLK_DIV2,
RCC_APB5CLK_DIV2, RCC_APB6CLK_DIV2);
RCC_ConfigPll1(RCC_PLL_SRC_HSE, 25000000, 600000000, ENABLE);
/* 9. 等待PLL1锁定 */
__RCC_DELAY_US(10);
/* 10. 配置PLL1分频器 */
RCC_ConfigPLL1ADivider(RCC_PLLA_DIV1); // PLL1A = 600MHz
RCC_ConfigPLL1BDivider(RCC_PLLB_DIV1); // PLL1B = 600MHz
RCC_ConfigPLL1CDivider(RCC_PLLC_DIV1); // PLL1C = 600MHz
/* 11. 等待分频器稳定 */
__RCC_DELAY_US(2);
/* 12. 切换系统时钟源到PLL1A */
RCC_ConfigSysclk(RCC_SYSCLK_SRC_PLL1A);
/* 13. 等待时钟切换完成 */
uint32_t timeout = 1000; // 超时计数器
while(RCC_GetSysclkSrc() != RCC_SYSCLK_STS_PLL1A)
{
timeout--;
if(timeout == 0)
{
// 时钟切换失败处理
// Error_Handler();
break;
}
}
/* 14. 更新SystemCoreClock全局变量 */
SystemCoreClock = 600000000; // 更新系统时钟频率
SystemD2Clock = 300000000; // 更新D2域时钟频率
return SUCCESS;
} // HSE稳定成功
else
{
// HSE启动失败处理
return ERROR;
}
}
cpp
#define __RCC_DELAY_US(usec) do{ \
uint32_t delay_end; \
CPU_DELAY_INTI(); \
/* Delay*/ \
delay_end = DWT_CYCCNT + (usec * (600000000/1000000)); \
while(DWT_CYCCNT < delay_end){}; \
CPU_DELAY_DISABLE(); \
}while(0)
void MCU_Init(void)
{
if(ConfigureSystemClock_600MHz()==SUCCESS)
{
USART1_Config(115200);
usart1_printf("USE HSE!\r\n");
}
else
{
USART1_Config(115200);
usart1_printf("USE HSI!\r\n");
RCC_SetSysClkToMode0();
}
TIM1_Config();
TIM3_Config();
GetClkFreqTime =0;
}
