3. TRAVEO启动流程与升级入口
在TRAVEO系列MCU(以CYT4BF等Traveo II器件为例)上做固件升级,第一步不是写Flash,而是搞清楚“芯片上电后究竟先执行谁”。如果启动路径没理清,就会出现“升级请求已置位但Bootloader没进”“跳转后App跑飞”“安全启动把自研Bootloader拦在门外”等典型问题。本节把复位向量、启动模式、跳转条件、升级请求标志与安全启动的衔接关系串成一条可落地的判断链。
一、复位向量:启动的第一条指令从哪里取
TRAVEO基于Arm Cortex-M内核,复位后CPU从向量表偏移0x00处读取初始MSP(主栈指针),从0x04处读取Reset_Handler地址。这个向量表的位置由两个因素决定:一是BOOT配置(如SFlash中的启动相关配置区),二是SCB->VTOR(向量表偏移寄存器)的当前值。上电复位时VTOR通常指向默认的Flash起始地址(例如0x1000_0000,具体以器件手册的Flash映射为准),因此复位向量实际来自该地址处的向量表。
关键点在于:Bootloader和App各自拥有独立的向量表。Bootloader的向量表放在Flash起始区,App的向量表放在App分区起始地址(例如0x1002_0000)。当Bootloader决定跳转到App时,必须做两件事:把App向量表的第0个字加载到MSP,把第1个字加载到PC(或通过设置VTOR后执行跳转)。如果只改PC不改MSP,App的栈会沿用Bootloader的栈空间,一旦App栈需求更大就会踩踏Bootloader数据区,表现为“跳转后随机HardFault”。
二、启动模式:从哪个入口开始执行
TRAVEO的启动模式通常由SFlash中的启动配置(如TOC/启动模式字段)和外部引脚(如BOOT引脚电平)共同决定。工程上常见三类入口:
- 正常启动(Normal/Flash Boot):直接从用户Flash的复位向量执行,通常指向Bootloader或直接指向App。
- 升级/特殊启动模式:由BOOT引脚或SFlash配置强制进入Bootloader,常用于产线烧录或恢复。
- 调试/系统模式:由调试器接管,用于SWD/JTAG下载和调试。
需要强调的是,启动模式决定的是“复位后第一条指令的入口”,而升级请求标志决定的是“进入Bootloader后是否执行升级”。两者是串联关系,不是替代关系。很多现场问题就是把“置了升级标志”误当成“一定会进Bootloader”,忽略了启动模式仍指向App的情况。
三、Bootloader跳转条件:什么时候跳、跳之前做什么
Bootloader在完成自身初始化后,需要判断“继续留在Bootloader升级”还是“跳转到App”。典型判断顺序如下:
- 读取升级请求标志(通常存放在备份RAM或专用Flash标志区,带CRC或魔数校验)。
- 若标志有效且请求升级,则停留在Bootloader,进入通信升级流程(如CAN/UART/OTA)。
- 若标志无效,则校验App分区的有效性:向量表首字是否在合法RAM范围、App长度是否非零、App镜像CRC/签名是否通过。
- 校验通过则执行跳转;校验失败则停留在Bootloader并置错误状态,等待重新升级。
跳转本身不是一条goto,而是一段有顺序的收尾动作:关闭Bootloader已开启的中断和外设、清除挂起中断、设置MSP为App向量表首字、设置VTOR为App向量表基址、最后把App复位向量地址装入PC并执行。下面这段示例代码展示了跳转前的关键动作(参数为示例值,实际以器件手册为准):
/* 示例:从Bootloader跳转到App(地址为示例) */
#define APP_BASE 0x10020000UL
typedef void (*app_entry_t)(void);
void jump_to_app(void)
{
uint32_t app_msp = *(volatile uint32_t *)(APP_BASE);
uint32_t app_pc = *(volatile uint32_t *)(APP_BASE + 4U);
/* 1. 关闭全局中断,防止跳转过程中被中断打断 */
__disable_irq();
/* 2. 关闭Bootloader已使用的外设(示例:关闭CAN0、UART0) */
CAN0->CTL = 0U;
UART0->CTL = 0U;
/* 3. 清除所有挂起中断 */
for (uint32_t i = 0U; i < 8U; i++) {
NVIC->ICPR[i] = 0xFFFFFFFFU;
}
/* 4. 设置主栈指针为App向量表首字 */
__set_MSP(app_msp);
/* 5. 设置向量表偏移为App向量表基址 */
SCB->VTOR = APP_BASE;
/* 6. 跳转到App复位向量 */
((app_entry_t)app_pc)();
}
这段代码里最容易出错的是第4步和第5步的顺序。必须先设MSP再设VTOR,最后跳转;如果先跳转再设VTOR,App在中断到来时仍会查Bootloader的向量表,导致中断服务函数错乱。
四、升级请求标志:放在哪里、怎么保证可靠
升级请求标志不能随便放在普通RAM里,因为复位后普通RAM内容可能丢失或被初始化。TRAVEO上常用两类位置:
- 备份RAM(Backup RAM):由VBAT或独立电源域供电,复位不丢失,适合存放“请求升级”“升级成功”“回滚计数”等状态。
- 专用Flash标志区:在Flash中划出一小块区域存放标志,写入前需擦除,适合不频繁变更的状态。
标志本身建议采用“魔数+状态码+CRC”的结构,例如:0xA5A5_0001表示请求升级,0xA5A5_0002表示升级完成待确认,0xA5A5_0003表示升级失败需回滚。Bootloader读取后先校验魔数和CRC,再决定动作。这样即使备份RAM被干扰,也不会误触发升级。
五、与安全启动的衔接:谁先谁后、谁校验谁
TRAVEO支持安全启动(Secure Boot),其典型流程是:复位后由ROM/SFlash中的安全启动代码先校验Bootloader镜像的签名或哈希,校验通过才把控制权交给Bootloader。因此安全启动与Bootloader的关系是“安全启动在前,Bootloader在后”。Bootloader再去校验App镜像,形成两级信任链。
这里有一个工程上必须明确的边界:安全启动校验的是Bootloader,Bootloader校验的是App。如果App镜像没有签名,而安全启动又要求全链路校验,就会出现“Bootloader能跑、App跳过去被拦”的现象。解决办法是在Bootloader中实现App签名校验,并把校验结果写入升级请求标志区,供下次启动判断。
六、把判断链落到工程检查表
结合上面的流程,实际调试时建议按以下顺序逐项确认,避免“跳转失败”时无从下手:
| 检查项 | 观察点 | 正常判据 | 异常处理 |
|---|---|---|---|
| 复位向量 | 0x00/0x04处内容 | MSP在合法RAM范围,PC在Flash范围 | 检查链接脚本与启动配置 |
| 安全启动 | Bootloader签名校验结果 | 校验通过,控制权移交Bootloader | 重新签名或关闭安全启动做对比 |
| 启动模式 | SFlash配置/BOOT引脚 | 与预期入口一致 | 修正配置或引脚电平 |
| 升级请求标志 | 备份RAM/Flash标志区 | 魔数与CRC均正确 | 清除标志后重试 |
| App校验 | App向量表首字、CRC | 首字在RAM范围,CRC匹配 | 重新烧录App镜像 |
| 跳转动作 | MSP、VTOR、PC | 先设MSP,再设VTOR,最后跳PC | 调整跳转函数顺序 |
总结来看,TRAVEO的升级入口不是单一开关,而是一条由复位向量、启动模式、升级请求标志、App校验和安全启动共同构成的判断链。理解每一环的输入、输出和失败表现,才能在OTA升级、产线烧录和现场恢复中做到可控、可查、可回滚。