ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

IAP升级死机元凶:中断向量表重映射详解与避坑指南

IAP升级死机元凶:中断向量表重映射详解与避坑指南 1. IAP升级死机背后的真凶从一个真实案例说起做嵌入式这行的朋友IAP升级功能几乎是绕不开的一道坎。不管是产品出厂后的固件更新还是现场部署设备的远程维护IAP都是基础设施级别的存在。但就是这个看起来“bootloader跳app”的简单动作坑了无数人。我自己在早期做GD32F103的IAP方案时就遇到过一个极其典型的问题bootloader里跳转一切正常app单独烧录运行也毫无问题但一旦通过IAP方式从boot跳转到app程序跑不了几秒就死机有时候甚至直接进HardFault。排查了时钟配置、堆栈指针、外设初始化顺序全都排除了。最后定位到的问题根源就是中断向量表重映射没有做对。这个点说起来简单但里面的细节和禁忌值得单独拿出来讲透。这篇文章面向的是正在做或准备做IAP功能的嵌入式开发者不管你是用STM32、GD32、HC32还是其他Cortex-M内核的芯片只要涉及IAP中断向量表的处理就是必修课。我会从原理讲到实操从常见错误讲到排查方法把这块内容彻底拆开揉碎。文章里涉及的具体芯片以GD32F103和STM32F103为主因为这两个在社区里讨论最多但原理对所有Cortex-M系列芯片通用。先给不太熟悉的朋友快速对齐一下概念。IAP全称In-Application Programming说白了就是程序自己能在运行过程中去改写Flash里的另一段程序。通常的做法是Flash里放两段代码一段是bootloader负责接收新固件、擦写Flash、跳转另一段是app就是实际干活的业务代码。bootloader通过串口、CAN、以太网等接口拿到新固件数据写到app区域然后跳过去执行。整个过程不需要仿真器不需要拆机对现场设备来说非常实用。但问题就出在“跳过去执行”这一步。Cortex-M内核的中断向量表默认固定在Flash起始地址通常是0x08000000bootloader在这它的向量表也在这。app被烧到了比如0x08008000的位置它的向量表应该在这个偏移处。如果你不告诉CPU“向量表搬家了”那CPU在响应中断时还是会去0x08000000找中断服务函数的入口地址——那里现在是bootloader的向量表指向的是bootloader的中断处理函数跟app完全对不上。结果就是中断一触发程序就跑飞了。这就是中断向量表重映射要解决的问题。Cortex-M内核提供了一个专门的寄存器叫VTORVector Table Offset Register你把app向量表的实际地址写进去CPU就知道去哪找中断入口了。听起来很简单对吧但实际操作中写VTOR的时机、地址对齐、bootloader里的中断处理每一个环节都有坑。2. 中断向量表重映射的核心原理拆解2.1 Cortex-M的中断响应机制到底怎么工作的要理解VTOR的作用得先搞清楚Cortex-M内核响应一个中断时到底做了什么。当外设触发中断且该中断被使能后内核硬件会自动完成一系列动作首先把当前正在执行的指令完成或者中断它然后自动压栈保存现场——把xPSR、PC、LR、R12、R3、R2、R1、R0这八个寄存器压入当前使用的栈MSP或PSP。接着内核从中断向量表中取出对应中断号的入口地址把这个地址赋给PC同时更新LR为一个特殊值EXC_RETURN这样中断返回时就知道该回到哪。关键就在“从中断向量表中取出入口地址”这一步。中断向量表本质上是一个32位地址数组放在内存的某个位置。第一个字是初始MSP值第二个字是复位向量Reset_Handler的地址从第三个字开始依次是NMI、HardFault等系统异常的处理函数地址再往后是各个外设中断的处理函数地址。内核怎么知道向量表在哪答案就在VTOR寄存器。复位后VTOR的默认值通常是0x00000000但由于Cortex-M的地址映射机制0x00000000通常被映射到Flash的起始地址比如0x08000000。所以复位后内核从Flash起始处读取向量表。当你把VTOR改成app向量表的地址后后续所有中断和异常都会去新地址取入口。这里有个容易忽略的点VTOR的修改不会影响已经在执行的中断它只影响修改之后发生的中断。所以如果你在中断服务函数里改VTOR当前中断的返回不受影响但下一个中断就会用新的向量表。这个特性在某些场景下有用但大多数IAP场景下我们是在跳转前就改好VTOR。2.2 为什么IAP场景下必须做向量表重映射有人可能会想我bootloader里不使能任何中断跳转前关掉所有中断跳到app后app自己重新初始化是不是就不用管VTOR了理论上如果你能保证跳转后到app设置VTOR之前这段时间内绝对不发生任何中断和异常那确实可以。但现实中这几乎不可能。首先SysTick定时器通常是在bootloader里就配好的跳转后它还在跑如果SysTick中断使能了跳过去立刻就会触发。其次即使用app里第一件事就是关中断、设VTOR但编译器生成的启动代码里可能有一些初始化操作会触发异常比如访问未对齐地址触发HardFault。再者有些芯片的bootloader里会用到看门狗看门狗中断也可能在跳转后触发。所以最稳妥的做法是在bootloader跳转前就把VTOR设成app向量表的地址同时确保app的向量表已经正确烧录在对应位置。这样即使跳转后立刻来中断CPU也能找到正确的入口。但这里又引出一个新问题bootloader跳转前设了VTOR指向app的向量表那如果app还没烧录或者烧录不完整向量表内容是空的0xFFFFFFFF中断一来CPU取到的入口地址就是非法值直接HardFault。所以实际产品中bootloader在跳转前通常要做固件完整性校验确认app区域有有效代码再跳。2.3 VTOR寄存器的位定义与对齐要求VTOR寄存器的地址是0xE000ED08它是可读写的。但并不是所有位都能随便写。Cortex-M3和M4的VTOR定义略有差异以Cortex-M3为例VTOR的bit[31:7]是可写的偏移地址bit[6:0]保留读为0写忽略。这意味着向量表的地址必须是128字节对齐的。Cortex-M4和M7通常要求对齐到更大边界具体看芯片手册有些要求256字节甚至512字节对齐。为什么有对齐要求因为向量表本身是一个数组每个入口占4字节如果地址不对齐硬件取向量时可能跨Cache行或者跨总线边界影响性能甚至出错。所以你在链接脚本里给app分配起始地址时必须保证这个地址满足VTOR的对齐要求。以GD32F103为例它的Flash起始是0x08000000假设bootloader占32KBapp从0x08008000开始。0x08008000的低7位是0满足128字节对齐没问题。但如果你把app起始地址设成0x08008100低7位是0x00也满足。但如果设成0x08008080低7位是0x00也满足128字节对齐。实际上只要地址是128的整数倍就行。不过为了页擦除方便通常app起始地址会按Flash页大小对齐GD32F103的页大小是1KB或2KB看具体型号所以一般不会出现对齐问题。但有一种情况要注意有些开发者为了省空间把app起始地址设得很紧凑比如bootloader刚好占30KBapp从0x08007800开始。0x08007800是128的整数倍吗0x7800 3072030720 / 128 240是整数所以对齐没问题。但如果bootloader占30.5KBapp从0x08007A00开始0x7A00 3123231232 / 128 244也是整数。实际上只要地址低7位为0就对齐而Flash页大小通常是1KB或2KB都是128的倍数所以按页对齐的地址天然满足VTOR对齐要求。3. IAP升级中向量表重映射的实操要点3.1 Bootloader端的跳转前准备在bootloader里跳转到app之前需要做几件事顺序很重要。我以GD32F103的实操为例把关键步骤和代码逻辑讲清楚。第一步确认app区域有有效固件。通常的做法是检查app起始地址处的栈顶值是否在合法RAM范围内以及复位向量是否在app的Flash范围内。比如app起始地址是0x08008000读取该地址的32位值作为栈顶应该在0x20000000到0x20005000之间GD32F103的RAM是20KB。再读0x08008004处的值作为复位向量应该在0x08008000到0x08020000之间。两个条件都满足才认为固件有效。第二步关闭所有使能的中断。这一步很关键因为跳转过程中如果来中断而VTOR还没改CPU会去bootloader的向量表取入口执行bootloader的中断处理函数可能引发不可预期的行为。通常调用__disable_irq()关闭全局中断然后逐个关闭外设中断使能位。但更彻底的做法是直接复位所有外设不过那样会丢失一些配置看具体需求。第三步设置VTOR寄存器。把app向量表的地址写入VTOR。代码很简单#define APP_BASE_ADDR 0x08008000 SCB-VTOR APP_BASE_ADDR;但这里有个细节SCB-VTOR的写入需要确保地址对齐。如果你用的app起始地址不是128字节对齐的写入后低7位会被硬件忽略实际生效的地址可能跟你预期的不一样。所以务必确认对齐。第四步设置主栈指针MSP。app的向量表第一个字就是初始MSP值跳转前需要把这个值加载到MSP。因为bootloader和app可能使用不同的栈空间如果不重设MSPapp的栈可能跟bootloader的栈冲突。第五步跳转到app的复位向量。读取app向量表的第二个字复位向量地址然后通过函数指针调用。typedef void (*pFunction)(void); pFunction app_entry; uint32_t app_msp *(volatile uint32_t*)APP_BASE_ADDR; uint32_t app_reset *(volatile uint32_t*)(APP_BASE_ADDR 4); __set_MSP(app_msp); app_entry (pFunction)app_reset; app_entry();这五步的顺序不能乱。特别是设VTOR和关中断的顺序一定要先关中断再设VTOR否则设VTOR的瞬间如果来中断CPU会去新向量表取入口但此时app可能还没准备好同样会出问题。3.2 App端的向量表配置app端也需要做配合。首先链接脚本里要指定app的起始地址。以GCC为例在链接脚本里把Flash的起始地址改成0x08008000长度改成实际app区域大小。这样编译出来的向量表就放在0x08008000处。其次app的启动代码里通常会有SystemInit函数它可能会重新设置VTOR。如果你在bootloader里已经设好了VTORapp里再设一次也没问题只要地址一致。但要注意有些芯片的SystemInit会把VTOR设回默认值这就会导致问题。所以建议在app的main函数开头再确认一次VTOR的值SCB-VTOR APP_BASE_ADDR;这行代码放在main的最开始确保万无一失。另外app的中断服务函数命名要和启动文件里的向量表一致。如果你用的是芯片厂商提供的启动文件通常已经定义好了。但如果你自己写了向量表要确保每个中断入口都正确。3.3 链接脚本与分散加载文件的配置细节不同开发环境的链接配置方式不同。Keil MDK用的是分散加载文件.sctIAR用的是.icf文件GCC用的是.ld文件。不管哪种核心都是把app的ROM起始地址改成实际偏移。以Keil MDK为例默认的分散加载文件可能是这样的LR_IROM1 0x08000000 0x00020000 { ER_IROM1 0x08000000 0x00020000 { *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) } RW_IRAM1 0x20000000 0x00005000 { .ANY (RW ZI) } }改成IAP模式后app的分散加载文件应该是LR_IROM1 0x08008000 0x00018000 { ER_IROM1 0x08008000 0x00018000 { *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) } RW_IRAM1 0x20000000 0x00005000 { .ANY (RW ZI) } }注意起始地址和长度的变化。bootloader占32KB所以app从0x08008000开始总Flash是128KB的话app区域就是0x08008000到0x08020000长度0x18000。GCC的链接脚本类似修改MEMORY区域的ORIGIN和LENGTHMEMORY { FLASH (rx) : ORIGIN 0x08008000, LENGTH 96K RAM (rwx) : ORIGIN 0x20000000, LENGTH 20K }IAR的.icf文件里修改ROM区域的起始和大小。这里有个容易踩的坑改了链接脚本后编译出来的bin文件是从0x08008000开始的但如果你用烧录工具直接烧到0x08000000那就全错了。所以IAP升级时bootloader接收到的固件数据应该直接写到0x08008000开始的Flash区域而不是0x08000000。3.4 中断向量表重映射的时机选择VTOR的设置时机有三种常见方案各有优劣。方案一bootloader跳转前设置。这是最常用的方案。在bootloader里关中断后、跳转前把VTOR设成app向量表地址。优点是app端不需要额外处理跳过去就能正常工作。缺点是如果app固件有问题跳转后中断一来就可能HardFault而且bootloader已经交了控制权没法回退。方案二app启动后设置。bootloader不设VTOR跳转后app在main函数最开始设VTOR。优点是app有完全的控制权可以在设VTOR前做一些必要的初始化。缺点是跳转到设VTOR之间这段时间如果有中断触发CPU会去bootloader的向量表取入口可能出问题。所以这种方案要求bootloader跳转前必须关掉所有中断且app设VTOR前不能开中断。方案三两边都设。bootloader跳转前设一次app启动后再设一次。这是最保险的做法我一般推荐这种。虽然多了一次冗余操作但能避免各种边界情况。不管哪种方案核心原则是在VTOR指向正确地址之前绝对不能有任何中断被触发。这是铁律。4. 常见死机问题与排查技巧实录4.1 跳转后立刻HardFault的几种原因跳转后立刻HardFault是最常见的现象。根据我的排查经验原因通常集中在以下几个方面。第一个原因VTOR没设或设错。这是最直接的。如果你忘了设VTORCPU还在用bootloader的向量表app里一开中断就取到bootloader的中断入口执行bootloader的代码很可能访问了不存在的资源或者栈溢出直接HardFault。排查方法很简单在app的HardFault_Handler里读SCB-VTOR的值看是不是你预期的地址。第二个原因MSP没重设。app的栈顶值和bootloader的栈顶值可能不同。如果跳转前没把MSP设成app的栈顶app运行时的栈可能还在bootloader的栈区域而bootloader的栈区域可能已经被app的全局变量覆盖了导致栈数据被破坏。排查方法在app的main函数开头打印MSP的值跟app向量表第一个字对比。第三个原因app向量表内容不对。比如链接脚本没改app编译出来的向量表还在0x08000000处但实际烧到了0x08008000那0x08008000处的数据就是错的。排查方法用烧录工具读取0x08008000处的数据看前两个字是否合理栈顶在RAM范围复位向量在Flash范围。第四个原因时钟配置冲突。bootloader可能把时钟配成了72MHzapp的SystemInit又配一遍如果配置过程中关掉了某些时钟或者改了PLL可能导致Flash访问时序出错取指令失败。排查方法在app的SystemInit前后加打印看卡在哪一步。4.2 中断响应异常但不断言的排查思路有些情况下程序不HardFault但中断响应不正常。比如串口接收中断进不去或者进去了但数据不对。这种问题更隐蔽。首先检查VTOR是否真的生效了。可以在app里主动触发一个软件中断比如设置NVIC的STIR寄存器看是否进入预期的中断处理函数。如果没进说明VTOR没生效或者向量表内容不对。其次检查中断优先级分组。bootloader和app可能设置了不同的优先级分组跳转后分组没重置导致app的中断优先级跟预期不符。Cortex-M的优先级分组是通过SCB-AIRCR的PRIGROUP位设置的app启动后应该重新设置一遍。还要检查外设中断使能位。bootloader里可能使能了某些外设中断跳转前没关app里又没重新初始化这些外设导致中断状态混乱。建议bootloader跳转前把所有外设中断都关掉app里重新初始化。4.3 向量表重映射相关的常见问题速查表问题现象可能原因排查方法解决方案跳转后立刻HardFaultVTOR未设置或设错读SCB-VTOR值在bootloader跳转前设置正确的VTOR跳转后立刻HardFaultMSP未重设对比MSP和app向量表首字跳转前用__set_MSP重设中断进不去向量表内容不对读取app起始地址数据检查链接脚本和烧录地址中断进错函数VTOR指向错误读VTOR并对比预期修正VTOR地址偶发死机跳转过程中来中断在中断里加计数跳转前彻底关中断串口中断异常优先级分组不一致读SCB-AIRCRapp里重新设置优先级分组看门狗复位看门狗未处理检查看门狗配置跳转前喂狗或关闭看门狗这张表是我在实际项目中总结的基本上覆盖了90%以上的向量表相关问题。遇到死机时按表逐项排查效率会高很多。4.4 几个容易忽略的细节陷阱第一个陷阱bootloader里定义的大数组变量。有人在bootloader里定义了一个很大的数组用来接收固件数据比如uint8_t buffer[65536]。这个数组如果是全局变量会占用大量RAM。跳转到app后app的全局变量可能跟这个数组区域重叠导致数据被覆盖。更严重的是如果这个数组在栈上跳转前没释放app的栈可能直接溢出。所以bootloader里的大缓冲区应该用动态分配或者放在特定区域跳转前确保释放。第二个陷阱Flash等待周期。bootloader可能把Flash等待周期设成了适合低频的值app跑高频时如果没改等待周期取指令会出错。GD32F103在72MHz下需要2个等待周期如果bootloader设的是0app跑72MHz就会取指失败。所以app的SystemInit里要重新设置Flash等待周期。第三个陷阱中断向量表的对齐。前面提过VTOR要求128字节对齐但有些开发者把app起始地址设成了非对齐值比如0x08008100。0x8100 3302433024 / 128 258是整数所以对齐没问题。但如果设成0x080080800x8080 3289632896 / 128 257也是整数。实际上只要地址低7位为0就对齐。但如果你设成0x08008040低7位是0x40不对齐VTOR写入后低7位被忽略实际生效地址变成0x08008000跟预期差0x40向量表就错位了。第四个陷阱编译器优化导致的代码重排。有些编译器会把VTOR的设置代码优化掉认为它没有副作用。所以设置VTOR时最好用volatile指针或者加内存屏障。比如*(volatile uint32_t*)0xE000ED08 APP_BASE_ADDR;这样编译器不会优化掉。5. 不同芯片平台的适配经验5.1 GD32F103的IAP向量表处理GD32F103跟STM32F103在IAP处理上基本一致VTOR寄存器地址和位定义相同。但GD32F103的Flash页大小跟STM32F103不同GD32F103的页大小是1KB前4KB或2KB后续而STM32F103是1KB或2KB看型号。这影响的是擦除操作对向量表重映射本身没影响。GD32F103的IAP方案中bootloader通常放在0x08000000开始的区域app从0x08004000或0x08008000开始。我一般建议bootloader至少留16KB因为要包含串口通信、Flash擦写、校验等逻辑16KB比较宽裕。app从0x08004000开始的话VTOR设为0x08004000满足128字节对齐。有个GD32特有的问题GD32F103的Flash在擦写时会阻塞CPU取指如果bootloader和app在同一块Flash上擦写app区域时CPU无法从Flash取指只能从RAM执行擦写代码。所以bootloader里擦写Flash的函数要放到RAM里执行或者用芯片提供的Flash操作库通常已经处理了这个问题。5.2 HC32L136的IAP注意事项HC32L136是华大半导体的一款低功耗MCUCortex-M0内核。M0的VTOR寄存器跟M3/M4略有不同M0的VTOR只支持有限的地址范围具体看芯片手册。HC32L136的Flash起始地址是0x00000000不是0x08000000这点要注意。HC32L136做IAP时bootloader和app的地址分配要参考它的Flash结构。它的Flash通常是64KB或128KB页大小可能是512字节。VTOR的设置方式跟M3类似但M0的中断向量表偏移要求可能不同建议查一下具体手册。另外HC32L136支持低功耗模式IAP跳转前要确保退出低功耗模式否则跳转后可能无法正常唤醒。5.3 STM32F103的经典IAP方案STM32F103的IAP方案在社区里资料最多基本流程跟GD32F103一样。但STM32F103有个特点它的Flash在擦写时也会阻塞CPU所以擦写函数要放RAM。ST官方提供了Flash操作库可以直接用。STM32F103的VTOR设置代码SCB-VTOR 0x08004000;跳转代码__disable_irq(); SCB-VTOR 0x08004000; __set_MSP(*(volatile uint32_t*)0x08004000); ((void (*)(void))(*(volatile uint32_t*)(0x08004004)))();这段代码在STM32F103上实测稳定。5.4 不同内核的VTOR差异对比内核VTOR地址对齐要求可写位备注Cortex-M00xE000ED08128字节bit[31:7]部分M0不支持VTORCortex-M00xE000ED08128字节bit[31:7]支持VTORCortex-M30xE000ED08128字节bit[31:7]标准VTORCortex-M40xE000ED08128字节bit[31:7]标准VTORCortex-M70xE000ED08128字节bit[31:7]可能有Cache影响M0和M0的VTOR支持情况要看具体芯片有些低端M0芯片不支持VTOR那就只能用其他方式做IAP比如把app的中断向量表复制到RAM里然后通过修改向量表基址寄存器如果有或者用软件中断转发。这种情况比较少见但遇到了要知道。6. 实操心得与避坑建议6.1 我的调试工具链配置调试IAP问题时我一般用以下工具组合J-Link或ST-Link做烧录和调试串口打印做运行时日志逻辑分析仪抓中断信号。J-Link的RTT功能特别好用可以在不占用串口的情况下输出调试信息而且速度很快。在Keil MDK里我会在Debug配置里打开“Run to main”选项这样调试时直接停在main函数方便检查VTOR和MSP的值。在Watch窗口里添加SCB-VTOR和__get_MSP()实时监控。如果用的是GCC工具链可以用OpenOCD加GDB调试同样可以查看VTOR和MSP。6.2 固件校验与回滚机制实际产品中IAP升级不能只考虑正常流程还要考虑异常情况。比如升级过程中断电app区域被擦了一半下次上电bootloader发现app无效应该停留在bootloader里等待重新升级而不是跳转到一个半成品的app。我的做法是在app区域末尾放一个标志位升级完成后写入特定值。bootloader跳转前检查这个标志位只有标志位正确才跳转。同时bootloader里做一个超时机制如果上电后一段时间内没收到升级命令且app有效就跳转如果app无效就停留在bootloader等待升级。回滚机制也很重要。如果新固件升级后运行异常应该能回退到旧固件。这需要Flash里保留两个app区域或者备份旧固件。对于Flash空间紧张的芯片可以只保留一个app区域但升级前先把旧固件备份到外部Flash或通过通信接口上传到服务器。6.3 中断向量表重映射的测试用例设计测试IAP功能时我一般设计以下测试用例正常升级流程bootloader接收固件、擦写、跳转、app正常运行升级中断电模拟升级过程中断电重新上电后bootloader能正确识别app无效app固件损坏故意烧录一个损坏的appbootloader能识别并拒绝跳转中断响应测试app里触发各种中断验证VTOR设置正确反复跳转测试bootloader和app之间反复跳转验证没有资源泄漏低功耗测试如果芯片支持低功耗测试跳转后低功耗模式是否正常这些测试用例能覆盖大部分边界情况建议在产品发布前都跑一遍。6.4 几个让我踩过坑的细节第一个坑VTOR设置后没有同步指令。Cortex-M的流水线可能预取了旧向量表的指令设置VTOR后需要执行一个DSB数据同步屏障和ISB指令同步屏障指令确保后续指令从新向量表取。虽然大多数情况下不执行也没事但在某些芯片上确实会出问题。保险起见设置VTOR后加上__DSB(); __ISB();第二个坑bootloader里的中断处理函数没有弱定义。如果bootloader里定义了某个中断处理函数app里也定义了同名的链接时可能冲突。建议bootloader里的中断处理函数都用weak属性app里可以覆盖。第三个坑Flash擦写时的中断响应。bootloader擦写Flash时如果来了中断CPU从Flash取中断向量会失败因为Flash正在擦写。所以擦写期间必须关中断或者把中断向量表复制到RAM里。我一般选择关中断简单可靠。第四个坑app的栈大小设置。app的栈大小在启动文件里定义如果设得太小跳转后稍微深一点的函数调用就会栈溢出。建议app的栈至少1KB复杂应用建议2KB以上。6.5 生产环境中的IAP稳定性建议在产品环境中IAP的稳定性至关重要。我的建议是bootloader尽量简单只做最基本的通信、擦写、跳转功能不要在里面跑复杂的业务逻辑。bootloader越简单出问题的概率越小。通信协议要有校验和重传机制确保固件数据传输无误。Flash擦写要有超时和重试避免因为Flash老化导致擦写失败。跳转前要做完整的固件校验包括CRC校验和向量表合法性检查。另外建议在bootloader里保留一个“强制升级”模式通过特定引脚或通信命令进入这样即使app正常运行也能强制进入bootloader进行升级。7. 关于IAP升级中变量复位行为的补充说明有朋友问过一个问题bootloader里定义的变量跳转到app后复位会怎样这个问题其实涉及IAP的内存管理。bootloader里定义的全局变量在跳转后依然占用RAM空间app的全局变量如果链接到了相同地址就会覆盖bootloader的变量。但bootloader跳转后一般不再运行所以覆盖了也没关系。关键是app的栈不能跟bootloader的栈冲突这就是为什么跳转前要重设MSP。如果bootloader里有些变量需要在跳转后保留比如升级标志那就要把这些变量放在特定的RAM区域并且在链接脚本里让app避开这个区域。这种做法在需要传递参数给app时很有用但大多数IAP场景下不需要。复位后所有RAM内容都会丢失除非有备份域所以bootloader里的变量在复位后自然就没了。如果需要在复位后保留某些信息可以写到Flash的特定区域或者备份寄存器里。IAP升级中中断向量表重映射这个点说大不大说小不小。做对了IAP功能稳稳当当做错了死机问题能折腾你好几天。希望这篇文章能帮你少走些弯路。如果你在实操中遇到其他奇怪的问题欢迎一起交流。
返回列表