4. 启动模式与硬件配置:启动引脚配置、启动介质选择、时钟与PLL初始化、看门狗处理
Bootloader 启动的第一步,不是读 Flash,也不是搬代码。
是硬件先认路。
芯片上电后,CPU 核心并不知道自己该从哪启动。它得先看几个引脚的电平,再决定去 NAND、NOR、SD 卡还是 UART 里找代码。这个过程,就是启动模式选择。我当年第一次调 STM32 的启动,焊好板子发现程序死活跑不起来,折腾了半天——原来是 BOOT0 引脚虚焊了。嗯,从那以后,我画板子都会在启动引脚上多放两个测试点。
4.1 启动引脚配置
大多数嵌入式 SoC 都会留出 2~4 个专用引脚,用来编码启动介质。比如常见的 BOOT0 和 BOOT1,组合起来能选 4 种模式。
| BOOT1 | BOOT0 | 启动介质 | 典型应用场景 |
|---|---|---|---|
| X | 0 | 主 Flash(NOR/NAND) | 正常产品运行 |
| 0 | 1 | 系统存储区(BootROM) | 出厂烧录 / 固件恢复 |
| 1 | 1 | SRAM | 调试 / 临时加载 |
为什么这么设计?因为 BootROM 里有一段出厂固化的 loader,它负责从 UART、USB 或 SPI 等接口接收数据并写入 Flash。你想想看,如果芯片 Flash 里是空的,上电后 CPU 总得有个地方能执行代码吧?BootROM 就是那个「最后保底」的地方。
4.2 启动介质选择
不同的启动介质,硬件接口和时序完全不同。Bootloader 在启动初期就要根据引脚状态,初始化对应的控制器。
4.2.1 NAND Flash
- 容量大、成本低,适合存大固件(比如 Linux 内核)。
- 缺点是坏块管理复杂,启动时需要跳过坏块。
- 我踩过坑:某项目用 NAND 启动,第一批板子没问题,第二批换了 Flash 批次,坏块位置变了,Bootloader 直接卡死。后来我强制在 Bootloader 里加了坏块扫描表,才解决。
4.2.2 NOR Flash
- 支持 XIP(原地执行),代码可以直接在 Flash 里跑,不用搬 RAM。
- 适合小容量、高实时性场景。
- 缺点:写入慢、容量做不大。
4.2.3 SD 卡 / eMMC
- 常见于树莓派、全志、瑞芯微等方案。
- BootROM 会先读 SD 卡前几个扇区,找到合法的启动镜像头。
- 注意:SD 卡启动对时序要求高,我曾经因为 SD 卡座接触不良,排查了整整两天。
4.2.4 UART / USB DFU
- 主要用于开发阶段或产线烧录。
- BootROM 里有一段很小的下载程序,通过串口或 USB 接收数据。
- 速度慢,但胜在不需要额外硬件。
4.3 时钟与 PLL 初始化
芯片上电后,默认跑的是内部低速振荡器(比如 8MHz 或 16MHz)。这个频率跑 Bootloader 还行,但要初始化 DDR、NAND 这些高速外设,就太慢了。
所以 Bootloader 的第一步,往往是配置 PLL,把系统时钟倍频到目标频率。
// 伪代码示例:STM32 系列 PLL 初始化
void SystemClock_Init(void) {
// 1. 使能 HSE 外部晶振
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)); // 等待稳定
// 2. 配置 PLL 参数:HSE 8MHz -> PLL 72MHz
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE; // PLL 源选 HSE
RCC->CFGR |= RCC_CFGR_PLLMUL9; // 8MHz * 9 = 72MHz
// 3. 使能 PLL
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
// 4. 切换系统时钟到 PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
时钟树的结构,我画了一张图帮你理解:
4.4 看门狗处理
看门狗(Watchdog)是嵌入式系统的「最后一道防线」。系统跑飞了,看门狗超时就会触发复位。
但问题来了:Bootloader 启动阶段,看门狗可能已经在跑了。
为什么?因为有些芯片上电后默认使能了独立看门狗(IWDG),或者前一个程序(比如 APP)开启了窗口看门狗(WWDG),复位后没有关闭。
看门狗的处理策略,我总结成三步:
- 立即关闭:Bootloader 入口处,无条件关闭 IWDG 和 WWDG。
- 阶段性喂狗:如果 Bootloader 流程很长(比如从 SD 卡加载大镜像),可以在关键步骤后喂一次狗,防止意外复位。
- 移交前恢复:跳转到 APP 之前,根据 APP 的需求重新配置看门狗(或者保持关闭,让 APP 自己管)。
// 伪代码:Bootloader 看门狗处理
void Bootloader_Entry(void) {
// 第一步:关狗
IWDG->KR = 0xCCCC; // 停止 IWDG(STM32 写法)
WWDG->CR = 0; // 关闭 WWDG
// 第二步:初始化硬件
SystemClock_Init();
UART_Init();
NAND_Init();
// 第三步:阶段性喂狗(如果狗没关干净)
// IWDG->KR = 0xAAAA; // 喂狗
// 第四步:加载并跳转 APP
LoadAndJumpToApp();
}
4.5 本章小结
启动模式与硬件配置,是 Bootloader 的「地基」。引脚配置错了,芯片找不到启动介质;时钟没配好,外设跑不起来;看门狗没处理,Bootloader 自己先崩了。
说白了,这四件事做扎实了,后面的代码搬运和跳转才有意义。我见过太多人一上来就写 Flash 驱动,结果发现时钟不对、引脚没拉——白忙活。