項目場景:
在OTA中,FLASH通常被劃分為以下幾種類型
- bootloader+iap+app
- bootloader+app+app保存區
- bootloader+app1+app2
不同的分區方式有不同的有點,但是共同點都是需要執行分區跳轉
問題1描述
但在分區跳轉過程中遇到過使用不同的編譯器不能跳轉的情況,例如在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)();}
}
問題1原因分析:
發現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)();}
}
問題2描述
裸機時可以正常跳轉,但是開啟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)();}
}
問題3描述
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)();}
}