在OTA中,FLASH通常被划分为以下几种类型
但在分区跳转过程中遇到过使用不同的编译器不能跳转的情况,例如在keil中使用v5编译器可以正常跳转,但是使用v6编译器就无法跳转了。
void JumpToCode(uint32_t addr) {
uint32_t *inputAddr = (uint32_t *)addr;
uint32_t jumpAddr = *(uint32_t *)(addr + 4);
if ((*inputAddr & 0x2FFE0000) == 0x20000000) {
__set_MSP(*inputAddr);
((void (*)(void))jumpAddr)();
}
}
发现v6和v5的编译优化不一样,v6编译执行__set_MSP后,跳转地址变量jumpAddr
被释放,就不能正确跳转了。把地址相关的变量声明为全局变量就可以正常跳转了
uint32_t *inputAddr; // !声明为全局变量,防止执行__set_MSP后,变量被释放
uint32_t jumpAddr; // !设置MSP后改变了栈底地址,导致原来的局部变量范围出了新栈的空间,被系统释放
void JumpToCode(uint32_t addr) {
inputAddr = (uint32_t *)addr;
jumpAddr = *(uint32_t *)(addr + 4);
if ((*inputAddr & 0x2FFE0000) == 0x20000000) {
__set_MSP(*inputAddr);
((void (*)(void))jumpAddr)();
}
}
裸机时可以正常跳转,但是开启freertos后分区无法相互跳转,度娘说要跳转前需要关闭全局中断、关闭外设。我采用的分区方式是bootloader+iap+app,boot跳转前关闭中断和外设后,跳转freertos的app分区没有问题,而跳转裸机的iap分区时无法运行,发现卡在初始化中。为什么跳freertos就ok呢?最后发现MX_FREERTOS_Init的时候自动把中断打开了,原来跳转后在main函数中需要重新开启中断,在其他所有裸机的main函数的while前添加__set_FAULTMASK(0)
开启中断即可,freertos不需要。修改后的跳转代码如下:
uint32_t *inputAddr; // !声明为全局变量,防止执行__set_MSP后,变量被释放
uint32_t jumpAddr; // !设置MSP后改变了栈底地址,导致原来的局部变量范围出了新栈的空间,被系统释放
void JumpToCode(uint32_t addr) {
inputAddr = (uint32_t *)addr;
jumpAddr = *(uint32_t *)(addr + 4);
if ((*inputAddr & 0x2FFE0000) == 0x20000000) {
HAL_RCC_DeInit();
HAL_DeInit();
__set_FAULTMASK(1);
__set_MSP(*inputAddr);
((void (*)(void))jumpAddr)();
}
}
boot可以跳iap和app了,但是!但是app无法跳iap,最后发现FREERTOS运行在PSP模式,而裸机运行在MSP模式,尝试跳转前设定MSP就正常了,添加__set_CONTROL(0)
,最终的跳转如下:
uint32_t *inputAddr; // !声明为全局变量,防止执行__set_MSP后,变量被释放
uint32_t jumpAddr; // !设置MSP后改变了栈底地址,导致原来的局部变量范围出了新栈的空间,被系统释放
void JumpToCode(uint32_t addr) {
inputAddr = (uint32_t *)addr;
jumpAddr = *(uint32_t *)(addr + 4);
if ((*inputAddr & 0x2FFE0000) == 0x20000000) {
HAL_RCC_DeInit();
HAL_DeInit();
__set_FAULTMASK(1);
__set_CONTROL(0);
__set_MSP(*inputAddr);
((void (*)(void))jumpAddr)();
}
}