|
https://m.toutiao.com/is/iLBDYtSx/?= STM32單片機BootLoader掃盲BootLoader和APP之間的關(guān)系APP就是平時寫的單片機上的應(yīng)用程序,而BootLoader本質(zhì)上和APP一樣,也是平時寫的應(yīng)用程序。BootLoader只不過是擁有從外部接收數(shù)據(jù),更新Flash(也就是APP),跳轉(zhuǎn)至APP功能的特殊APP罷了。 以STM32F103為例,如果沒有BootLoader,flash分布就如下圖左半部分。如果有BootLoader,就如下圖右半部分,將flash分為兩部分(這里舉例用0x800 4000做分界線),存儲了兩個應(yīng)用程序(BootLoader和APP) ![]() 如何從BootLoader跳轉(zhuǎn)到APP從外部接收數(shù)據(jù),更新Flash,普通的APP也會有這樣的需求。不常見的也就是從BootLoader跳轉(zhuǎn)到APP這個功能了。STM32 單片機啟動流程中介紹了單片機上電后的啟動流程,其實也就主要干了兩件事:
然后單片機執(zhí)行復(fù)位中斷服務(wù)函數(shù),在復(fù)位中斷服務(wù)函數(shù)中設(shè)置中斷向量表的偏移地址,準(zhǔn)備C環(huán)境,最后跳轉(zhuǎn)到main()函數(shù)。同理,從Bootloader跳到APP也需要干這兩件事情,只不過上電時是單片機自動加載的MSP和PC,而從Bootloader跳到APP則需要我們編寫函數(shù)進行跳轉(zhuǎn)。 下面是STM32 跳轉(zhuǎn)到APP的具體函數(shù),在BootLoader函數(shù)中調(diào)用即可跳轉(zhuǎn)到APP_ADDR,如果該地址存放了APP的bin文件則會運行APP的復(fù)位中斷服務(wù)函數(shù)(一定要記得在APP中修改中斷向量表偏移地址,如APP中不修改則默認(rèn)使用BootLoader的中斷向量表,APP中發(fā)生中斷時就會去查BootLoader的中斷向量表,從而調(diào)用BootLoader的中斷服務(wù)函數(shù)),進而運行APP的main()函數(shù)。同理,APP跳轉(zhuǎn)到BootLoader,修改跳轉(zhuǎn)地址 APP_ADDR 為BootLoader地址(這里為0x8000000)即可實現(xiàn). /* 開關(guān)全局中斷的宏 */#define ENABLE_INT() __set_PRIMASK(0) /* 使能全局中斷 */#define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中斷 */#define APP_ADDR 0x8004000 /* APP地址 */void (*SysMemBootJump)(void); /* 聲明一個函數(shù)指針 */static uint32_t AppAddr = APP_ADDR; /* APP地址 */ /*********************************************************************************************************** 函 數(shù) 名: JumpToApp* 功能說明: 跳轉(zhuǎn)到APP* 形 參: 無* 返 回 值: 無**********************************************************************************************************/void JumpToApp(void){ /* 如果初始化了外設(shè),需要反向初始化外設(shè) */ /* 設(shè)置所有時鐘到默認(rèn)狀態(tài),使用HSI時鐘 */ HAL_RCC_DeInit(); /* 關(guān)閉滴答定時器,復(fù)位到默認(rèn)值 */ SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; /* 關(guān)閉全局中斷 */ DISABLE_INT(); /* 關(guān)閉所有中斷,清除所有中斷掛起標(biāo)志 */ for (uint32_t i = 0; i < 8; i++) { NVIC->ICER[i]=0xFFFFFFFF; NVIC->ICPR[i]=0xFFFFFFFF; } /* 使能全局中斷 */ ENABLE_INT(); /* 設(shè)置SysMemBootJump為中斷服務(wù)函數(shù)入口地址,首地址是MSP,地址+4是復(fù)位中斷服務(wù)程序地址 */ SysMemBootJump = (void (*)(void)) (*((uint32_t *) (AppAddr + 4))); /* 設(shè)置主堆棧指針 */ __set_MSP(*(uint32_t *)AppAddr); /* 在RTOS工程,這條語句很重要,設(shè)置為特權(quán)級模式,SP使用MSP指針 */ __set_CONTROL(0); /* 進行跳轉(zhuǎn) 也就是將SysMemBootJump 賦值給PC */ SysMemBootJump(); /* 跳轉(zhuǎn)成功的話,不會執(zhí)行到這里,用戶可以在這里添加代碼 */ while (1) { }}常用的BootLoader+APP框架單片機升級的原理大致是:收到升級指令——>MCU復(fù)位或者跳轉(zhuǎn)到Boot程序區(qū)——>擦除對應(yīng)的Flash區(qū)域——>獲取APP數(shù)據(jù)——>寫入FLASH數(shù)據(jù)——>校驗——>跳轉(zhuǎn)到APP應(yīng)用程序區(qū) 單APP![]() 單APP方案將Flash分成了三個部分,Boot+APP+Config,Boot負責(zé)更新APP;APP實現(xiàn)程序的主要功能;Config區(qū)域主要是存放APP是否完整的標(biāo)志位,協(xié)助Boot判斷是否要跳轉(zhuǎn)到APP。單APP有個明顯的缺點:開始升級時就將原來的APP擦除了,如果升級中斷則會一直停留在BootLoader,直至APP升級完成。 ![]() 雙APP![]() 雙APP方案將Flash分成了四個部分,Boot+APP1+APP2+Config,Boot負責(zé)更新APP;APP1實現(xiàn)程序的主要功能;APP2相當(dāng)于緩沖區(qū),Boot升級APP時,會將接收到的APP數(shù)據(jù)包寫入APP2,當(dāng)APP數(shù)據(jù)包全部接收完畢,再將APP2搬運到APP1,這樣就避免了單APP方案,升級中斷一直停留在BootLoader的尷尬; Config區(qū)域主要是存放APP1是否需要升級的標(biāo)志位,協(xié)助Boot判斷是否要將APP2的內(nèi)容搬運至APP1。 ![]() STM32系統(tǒng)BootLoaderSTM32自舉具體信息可以去官網(wǎng)查看手冊AN2606 ![]() 使用系統(tǒng)bootLoader需要在復(fù)位時調(diào)整BOOT0管腳的電平,使用起來還是比較麻煩的。系統(tǒng)BootLoader也是FLash中的一段程序,只不過是官方燒錄的不可修改的,我們可以利用前面說的跳轉(zhuǎn)到APP的方法跳轉(zhuǎn)到系統(tǒng)BootLoader,這樣就可以不修改BOOT0管腳電平,直接進入系統(tǒng)BootLoader,利用系統(tǒng)BootLoader的下載功能下載更新程序。 ![]() 在手冊中的閃存章節(jié),找到系統(tǒng)BootLoader的起始地址0X1FFF FO00,將APP ADDR宏定義改為系統(tǒng)BootLoader地址即可跳轉(zhuǎn)到系統(tǒng)BootLoader 從APP跳轉(zhuǎn)到系統(tǒng)BootLoader后,利用STM32CubeProgrammer工具更新程序 ![]()
|
|
|