4. 设置系统时钟
任务目标
在Bootloader早期启动阶段,完成系统时钟从默认复位状态到目标频率的切换。本案例以STM32F407为例,目标是将系统时钟配置为168MHz(HSE 8MHz作为时钟源,通过PLL倍频),并通过对实际时钟标志位的检查验证配置结果。
核心实现
// clock_init.c - Bootloader时钟初始化最小实现
#include "stm32f4xx.h"
// 系统时钟配置:HSE(8MHz) → PLL(×21) → SYSCLK = 168MHz
// AHB = 168MHz, APB1 = 42MHz, APB2 = 84MHz
void SystemClock_Init(void) {
// 1. 开启HSE并等待就绪
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
// 2. 配置电源调压器为VOS1(保证168MHz工作电压)
PWR->CR |= PWR_CR_VOS;
// 3. 配置Flash等待周期(168MHz需5个等待周期)
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
// 4. 配置AHB/APB预分频器
RCC->CFGR = RCC_CFGR_HPRE_DIV1; // AHB = SYSCLK/1 = 168MHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 = 168/4 = 42MHz
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 = 168/2 = 84MHz
// 5. 配置PLL:HSE(8MHz) / 8 × 168 = 168MHz
RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | // PLLM = 8 → 1MHz
(168 << RCC_PLLCFGR_PLLN_Pos) | // PLLN = 168 → 168MHz
(0 << RCC_PLLCFGR_PLLP_Pos) | // PLLP = 2 → 84MHz VCO输出
(7 << RCC_PLLCFGR_PLLQ_Pos) | // PLLQ = 7 (USB用,此处未使用)
RCC_PLLCFGR_PLLSRC_HSE; // 时钟源 = HSE
// 6. 使能PLL并等待锁定
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
// 7. 切换系统时钟到PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
// 验证函数:返回1表示时钟配置正确,0表示错误
int Clock_Verify(void) {
// 检查系统时钟源是否为PLL
if ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
return 0;
// 检查PLL配置寄存器关键位
if ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) != RCC_PLLCFGR_PLLSRC_HSE)
return 0;
if (((RCC->PLLCFGR >> RCC_PLLCFGR_PLLN_Pos) & 0x1FF) != 168)
return 0;
// 检查AHB预分频器(应无分频)
if ((RCC->CFGR & RCC_CFGR_HPRE) != RCC_CFGR_HPRE_DIV1)
return 0;
return 1;
}
调用入口与验证
// main.c - Bootloader入口
#include "stm32f4xx.h"
void SystemClock_Init(void);
int Clock_Verify(void);
int main(void) {
// 复位后默认使用HSI(16MHz),此处切换到HSE+PLL
SystemClock_Init();
// 验证时钟配置
if (Clock_Verify()) {
// 配置正确:可继续执行Bootloader后续流程
// 例如:跳转到APP、外设初始化等
while (1);
} else {
// 配置错误:保持HSI运行,可在此处设置错误标志或进入错误处理
while (1);
}
}
编译与运行
# 使用arm-none-eabi工具链编译(以STM32F407为例)
arm-none-eabi-gcc -c clock_init.c -o clock_init.o -mcpu=cortex-m4 -mthumb
arm-none-eabi-gcc -c main.c -o main.o -mcpu=cortex-m4 -mthumb
arm-none-eabi-ld -T bootloader.ld clock_init.o main.o -o bootloader.elf
arm-none-eabi-objcopy -O binary bootloader.elf bootloader.bin
验证方法与判定标准
- 静态验证:通过调试器(如ST-Link + OpenOCD)连接开发板,在
Clock_Verify()返回后设置断点,检查RCC->CFGR寄存器的SWS位域,应读回0b10(PLL作为系统时钟源)。 - 动态验证:在
main()中Clock_Verify()返回1的分支设置GPIO翻转(如PA5接LED),若LED以预期频率闪烁(168MHz经定时器分频后),说明时钟配置生效。 - 通过条件:
Clock_Verify()返回1,且调试器读取的SWS位域为PLL、PLLCFGR的PLLN=168、PLLSRC=HSE,三者同时满足即判定通过。
关键说明
- 本实现针对STM32F407,若使用其他型号需调整PLL分频系数和Flash等待周期。
- PLLN最大值为432,168在合法范围内;PLLM=8将8MHz HSE分频为1MHz参考时钟,PLLN=168倍频得到168MHz VCO输出,再经PLLP=2分频得到168MHz系统时钟。
- APB1外设时钟最高42MHz、APB2最高84MHz,分频配置必须满足此限制,否则外设工作异常。
- Flash等待周期必须与系统时钟匹配:168MHz对应5个等待周期,配置过少会导致程序跑飞。