2. CAN FD基础
做Bootloader升级,说白了就是给ECU装一套「双系统」——一个负责升级的迷你系统,一个负责跑业务的大系统。这两个系统怎么共存、怎么切换、怎么保证不出事,就是架构设计的核心。
我最早做Bootloader时,踩过一个坑:App跑着跑着突然要升级,结果跳转过去发现向量表没改对,直接跑飞。从那以后,我对架构设计就特别较真。今天咱们把分区、启动流程、跳转机制、向量表重映射这四个点彻底讲透。
2.1 分区规划:Flash怎么分才合理?
一个典型的CAN FD Bootloader,Flash分区大概长这样:
| 区域 | 起始地址 | 大小 | 内容 |
|---|---|---|---|
| Bootloader区 | 0x08000000 | 32KB | 启动代码、升级协议栈、Flash驱动 |
| 参数区 | 0x08008000 | 4KB | 升级标志、版本号、校验值 |
| App区 | 0x08009000 | 256KB | 应用程序代码 |
| 备份区 | 0x08049000 | 256KB | App备份(可选) |
我个人习惯把Bootloader放在最前面,因为MCU上电默认从0x08000000取向量表。参数区紧挨着Bootloader,方便读写。App区从0x08009000开始,这个偏移量要跟App工程的链接脚本对齐。
2.2 启动流程:上电后发生了什么?
MCU上电复位后,Bootloader先跑。流程其实不复杂:
- 硬件初始化:关中断、关看门狗、初始化时钟和RAM。
- 检查升级标志:读参数区的标志位。如果是0xAA55,说明有升级请求。
- 校验App:如果不需要升级,就校验App的CRC。通过则跳转,不通过则留在Bootloader。
- 进入升级模式:有升级请求或App校验失败,就初始化CAN FD,等待升级指令。
- 跳转到App:一切正常,执行跳转。
嗯,这里要注意:看门狗必须在初始化阶段就喂上。我见过有人把喂狗放在App里,结果Bootloader跑太久,狗叫了,复位——死循环。
2.3 跳转机制:从Bootloader到App的临门一脚
跳转不是简单的goto。你得把MCU的状态「打扫干净」,再交给App。我常用的跳转代码长这样:
typedef void (*AppEntry)(void);
void JumpToApp(uint32_t appAddr)
{
// 1. 关全局中断
__disable_irq();
// 2. 关外设中断(定时器、DMA、CAN等)
for (int i = 0; i < 8; i++) {
NVIC->ICER[i] = 0xFFFFFFFF;
NVIC->ICPR[i] = 0xFFFFFFFF;
}
// 3. 关SysTick
SysTick->CTRL = 0;
// 4. 复位所有外设时钟(可选,看芯片)
RCC->AHB1RSTR = 0xFFFFFFFF;
RCC->AHB1RSTR = 0;
// ... 其他总线同理
// 5. 设置主栈指针
uint32_t msp = *(uint32_t*)appAddr;
__set_MSP(msp);
// 6. 跳转到复位向量
AppEntry app = (AppEntry)(*(uint32_t*)(appAddr + 4));
app();
}
这段代码我用了好几年,基本没出过问题。核心就三步:关中断 → 清外设 → 设栈指针 → 跳。
2.4 向量表重映射:App的中断去哪了?
App的向量表不在0x08000000了,它在0x08009000。如果MCU发生中断,它还是去0x08000000找向量表——那不就找错了吗?
解决办法有两个:
- 硬件重映射:STM32有VTOR寄存器,把它的值改成App的起始地址就行。
- 软件跳转表:在Bootloader里放一个跳转表,把中断转发到App。
我推荐用硬件重映射,简单可靠。在App的启动代码里加上:
// 在SystemInit或Reset_Handler里
SCB->VTOR = 0x08009000;
就这么一行。但要注意:必须在App启动后第一时间设置。如果App开了中断才改VTOR,那第一个中断就可能跑飞。
__app_base = 0x08009000,然后在代码里用这个符号设置VTOR。这样改分区时,只改链接脚本就行,不用动C代码。
2.5 架构总览:一张图说清楚
下面这张图把整个架构串起来了。你仔细看,每个模块的职责都很清晰:
这张图把Flash分区、启动流程、跳转和向量表重映射都串起来了。你仔细看:左侧是静态的Flash布局,中间是动态的启动流程,右侧是支撑整个架构的关键机制。三者缺一不可。
2.6 避坑总结
做Bootloader架构设计,有几个坑我反复踩过,今天一并告诉你:
- 分区大小要留余量:Bootloader别卡着32KB做,留个4KB给未来扩展。我见过有人把Bootloader塞得满满当当,后来加个功能都加不进去。
- 升级标志要防误判:参数区上电默认值最好是0xFFFF或0x0000,别跟有效标志冲突。我习惯用0xAA55和0x55AA交替写,防止单bit翻转误触发。
- 跳转前一定要关中断:这个我说过,但还是要强调。不关中断,跳转后App的中断向量还没设好,一个中断进来就飞了。
- VTOR设置要趁早:App启动后第一件事就是设VTOR。别等初始化完外设再设,那时候中断可能已经来了。