2. CMS32 启动流程详解

芯片上电之后到底发生了什么?这个问题,我当年刚入行时也困惑了很久。说白了,启动流程就是芯片从“死”到“活”的第一步。CMS32 的启动路径并不复杂,但有几个关键节点你必须吃透——中断向量表、启动模式选择,还有那个容易踩坑的重映射机制。

2.1 芯片上电复位流程

按下复位键,或者电源刚稳定,CMS32 内部会走一套固定的“开机动作”。我把它拆成四个阶段,你对照着看:

阶段 硬件行为 耗时(典型)
1. 电源稳定 POR(上电复位)检测 VDD 是否达到阈值 ~2 ms
2. 时钟起振 内部高速振荡器(HSI)启动,等待稳定 ~1 ms
3. 向量表加载 从 Flash 起始地址读取栈指针和复位向量 ~10 µs
4. 跳转执行 PC 指向复位向量,开始执行第一条指令 即时
个人习惯: 我每次画板子都会在复位引脚上加一个 10kΩ 上拉和 100nF 电容。别小看这个,曾经有一批板子因为电容太小,上电时复位不彻底,Bootloader 死活进不去。

这里有个细节:CMS32 的复位向量地址是 0x0000_0004,而栈指针在 0x0000_0000。硬件自动完成这两个加载动作,不需要软件干预。嗯,但你要知道——这只是默认情况。

2.2 中断向量表重映射

为什么需要重映射?你想想看,如果 Bootloader 和 App 都抢同一个向量表地址,那不乱套了?

CMS32 提供了一个叫 VTOR(向量表偏移寄存器) 的东西。通过修改它,你可以把向量表搬到任意地址。我一般在 Bootloader 里把 VTOR 设到 0x0000_0000,在 App 里设到 0x0001_0000(假设 App 起始地址)。

核心代码: 设置 VTOR 的写法各家芯片略有不同,CMS32 的库函数是这样的:
/* 设置向量表偏移到 0x0001_0000 */
SCB->VTOR = 0x00010000;

/* 或者用 CMSIS 标准函数 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x10000);
我曾经踩过的坑: 在 Bootloader 跳转到 App 之前,必须把所有中断都关掉。否则跳转瞬间来了一个中断,PC 还在半空中,VTOR 还没切过去——直接 HardFault。别问我怎么知道的。

重映射的时机也很关键。我个人习惯在跳转前的最后一步才改 VTOR,改完立刻执行一条 DSB 指令保证生效,然后再跳。

2.3 启动模式选择:Boot 模式 vs App 模式

CMS32 怎么知道这次该跑 Bootloader 还是 App?说白了,就是看一个标志。这个标志可以放在 RAM 里、备份寄存器里,或者 Flash 的某个固定地址。

我常用的方案是这样的:

  • Boot 模式: 上电后检查到标志为 0xA5A5,或者检测到外部引脚拉低,就留在 Bootloader 等待升级。
  • App 模式: 标志为 0x5A5A,或者超时无操作,直接跳转到 App 入口。

下面这张图是我自己项目里用的启动决策流程,你可以参考:

CMS32 启动模式选择流程 芯片上电复位 读取启动标志(0x0000_FF00) 标志 == 0xA5A5 标志 == 0x5A5A Boot 模式:等待升级 App 模式:跳转执行 超时 → 回退到 App 💡 说明: 启动标志存放在 Flash 最后一个扇区,Bootloader 和 App 都可以读写。 升级完成后,Bootloader 将标志改写为 0x5A5A,然后软复位。

你可能会问:为什么不用 GPIO 电平来判断?当然可以。但用标志位更灵活——你可以通过 OTA 远程切换启动模式,不用改硬件。我在一个量产项目里就靠这个标志位实现了“强制恢复模式”:如果 App 连续三次启动失败,Bootloader 自动把标志改成 0xA5A5,下次就留在 Boot 模式等升级。

2.4 启动流程中的避坑清单

这些年我帮客户排查过不少启动相关的问题,总结下来就这几条:

  • 向量表对齐: CMS32 要求 VTOR 的值必须是 512 字节对齐的。如果你设了个奇数地址,芯片直接忽略低 9 位。
  • 栈指针有效性: 跳转前检查 App 起始地址的前 4 个字节(栈顶指针)是否在 RAM 范围内。我曾经见过有人把 Flash 里的随机数据当栈指针用,一跑就飞。
  • 外设状态清理: Bootloader 里开了看门狗、定时器、DMA,跳转前必须关干净。App 不会帮你做这些。
  • 中断优先级分组: 如果 Bootloader 和 App 用了不同的优先级分组(比如 4 位 vs 3 位),跳转后中断行为会乱掉。我建议统一用 4 位抢占优先级。
一个小技巧: 在 App 的启动代码最前面加一段“自检”逻辑——检查 RAM 的前 64 字节是否被 Bootloader 污染过。如果发现异常数据,主动触发一次软复位,让 Bootloader 重新接管。

嗯,启动流程这部分就这些。你只要把向量表重映射和模式选择这两个点吃透,后面写 Bootloader 和 App 的跳转逻辑就会顺手很多。