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个等待周期,配置过少会导致程序跑飞。