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”。典型判断顺序如下:

  1. 读取升级请求标志(通常存放在备份RAM或专用Flash标志区,带CRC或魔数校验)。
  2. 若标志有效且请求升级,则停留在Bootloader,进入通信升级流程(如CAN/UART/OTA)。
  3. 若标志无效,则校验App分区的有效性:向量表首字是否在合法RAM范围、App长度是否非零、App镜像CRC/签名是否通过。
  4. 校验通过则执行跳转;校验失败则停留在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签名校验,并把校验结果写入升级请求标志区,供下次启动判断。

TRAVEO 启动与升级入口判断链 复位 读取MSP/PC 安全启动校验 校验Bootloader签名 启动模式判定 SFlash配置/BOOT引脚 Bootloader执行 初始化+读标志 校验失败→停机/恢复模式 读升级请求标志 备份RAM/Flash+CRC 标志有效 且请求升级? 停留Bootloader CAN/UART/OTA升级 是 否 校验App镜像 向量表/CRC/签名 App有效? 是 跳转到App 设MSP→设VTOR→跳PC 否 停留Bootloader,置错误状态 信任链:安全启动→Bootloader→App 标志结构:魔数+状态码+CRC

六、把判断链落到工程检查表

结合上面的流程,实际调试时建议按以下顺序逐项确认,避免“跳转失败”时无从下手:

检查项观察点正常判据异常处理
复位向量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调整跳转函数顺序
调试建议:在Bootloader中把“当前启动阶段”写入备份RAM的一个字节(如0x01=安全启动完成,0x02=进入Bootloader,0x03=准备跳转,0x04=已跳转),复位后用调试器读取该字节,就能快速定位卡在哪一环,比单步跟踪更高效。

总结来看,TRAVEO的升级入口不是单一开关,而是一条由复位向量、启动模式、升级请求标志、App校验和安全启动共同构成的判断链。理解每一环的输入、输出和失败表现,才能在OTA升级、产线烧录和现场恢复中做到可控、可查、可回滚。