本文主要講解在線升級IAP的基礎(chǔ)知識, 主要是針對IAP從原理分析,分區(qū)劃分, 到代碼編寫和實驗驗證等過程闡述這一過程. 幫助大家加深對在線升級的認(rèn)識。
1.在線升級知識
什么是BootLoader?
BootLoader可以理解成是引導(dǎo)程序, 它的作用是啟動正式的App應(yīng)用程序. 換言之,BootLoader是一個程序,App也是一個程序,BootLoader程序是用于啟動App程序的.
STM32中的程序在哪兒?
正常情況下, 我們寫的程序都是放在STM32片內(nèi)Flash中(暫不考慮外擴(kuò)Flash). 我們寫的代碼最終會變成二進(jìn)制文件, 放進(jìn)Flash中 感興趣的話可以在Keil>>>Debug>>>Memory中查看, 右邊Memory窗口存儲的就是代碼

接下來就可以進(jìn)入正題了.
進(jìn)行分區(qū)
既然我們寫的程序都會變成二進(jìn)制文件存放到Flash中, 那么我們就可以進(jìn)一步對我們程序進(jìn)行分區(qū). 我使用的是F103RB-NUCLEO開發(fā)板,他的Flash一共128頁, 每頁1K.見下圖:

以它為例, 我將它分為三個區(qū).BootLoader區(qū)、App1區(qū)、App2區(qū)(備份區(qū))具體劃分如下圖:
-
BootLoader區(qū)存放啟動代碼 -
App1區(qū)存放應(yīng)用代碼 -
App2區(qū)存放暫存的升級代碼

總體流程圖
-
先執(zhí)行
BootLoader程序, 先去檢查APP2區(qū)有沒有程序, 如果有就將App2區(qū)(備份區(qū))的程序拷貝到App1區(qū), 然后再跳轉(zhuǎn)去執(zhí)行App1的程序. -
然后執(zhí)行
App1程序, 因為BootLoader和App1這兩個程序的向量表不一樣, 所以跳轉(zhuǎn)到App1之后第一步是先去更改程序的向量表. 然后再去執(zhí)行其他的應(yīng)用程序. -
在應(yīng)用程序里面會加入程序升級的部分, 這部分主要工作是拿到升級程序, 然后將他們放到
App2區(qū)(備份區(qū)), 以便下次啟動的時候通過BootLoader更新App1的程序. 流程圖如下圖所示:

2. BootLoader的編寫
本節(jié)主要講解在線升級的BooLoader的編寫,我將以我例程的BootLoader為例, 講解BootLoader(文末會提供免費(fèi)的代碼下載鏈接),其他的大體上原理都差不多。
流程圖分析
以我例程的BootLoader為例:
我將App2區(qū)的最后一個字節(jié)(0x0801FFFC)用來表示App2區(qū)是否有升級程序, STM32在擦除之后Flash的數(shù)據(jù)存放的都是0xFFFFFFFF, 如果有, 我們將這個地址存放0xAAAAAAAA. 具體的流程圖見下圖所示

程序編寫和分析
所需STM32的資源有:
- 發(fā)送USART數(shù)據(jù)和printf重定向
- Flash的讀寫
-
程序跳轉(zhuǎn)指令,可以參考如下代碼:
/*采用匯編設(shè)置棧的值*/ __asmvoidMSR_MSP(uint32_tulAddr) { MSRMSP,r0//設(shè)置MainStack的值 BXr14 } /*程序跳轉(zhuǎn)函數(shù)*/ typedefvoid(*Jump_Fun)(void); voidIAP_ExecuteApp(uint32_tApp_Addr) { Jump_FunJumpToApp; if(((*(__IOuint32_t*)App_Addr)&0x2FFE0000)==0x20000000)//檢查棧頂?shù)刂肥欠窈戏? { JumpToApp=(Jump_Fun)*(__IOuint32_t*)(App_Addr+4);//用戶代碼區(qū)第二個字為程序開始地址(復(fù)位地址) MSR_MSP(*(__IOuint32_t*)App_Addr);//初始化APP堆棧指針(用戶代碼區(qū)的第一個字用于存放棧頂?shù)刂? JumpToApp();//跳轉(zhuǎn)到APP. } } -
在需要跳轉(zhuǎn)的地方執(zhí)行這個函數(shù)就可以了
IAP_ExecuteApp(Application_1_Addr); -
其他的代碼請參考
BootLoader源代碼
3. APP的編寫
本節(jié)主要講解在線升級(OTA)的App1的編寫以及整個流程的說明,我將以我例程的App為例, 采用Ymodem協(xié)議進(jìn)行串口傳輸,講解App的編寫(后面會提供免費(fèi)的代碼下載鏈接), 其他的協(xié)議原理大體上都差不多, 都是通過某種協(xié)議拿到升級的代碼。
流程圖分析
以我例程的App1為例:
- 先修改向量表, 因為本程序是由BootLoader跳轉(zhuǎn)過來的, 不修改向量表后面會出現(xiàn)問題;
- 打印版本信息, 方便查看不同的App版本;
- 本例程的升級程序采用串口的Ymoderm協(xié)議進(jìn)行傳輸bin文件. 具體的流程圖見下圖所示:

程序編寫和分析
所需STM32的資源有:
- 發(fā)送USART數(shù)據(jù)和printf重定向
- Flash的讀寫
- 串口的DMA收發(fā)
- YModem協(xié)議相關(guān)
Ymodem協(xié)議
- 百度百科[Ymodem協(xié)議]
- 具體流程可自行查找相關(guān)文檔, 這兒提供一個我找到的 XYmodem.pdf(文末和源碼一起提供).
- Ymodem協(xié)議相關(guān)介紹可參考我的這篇教程 YModem介紹
(https://blog.csdn.net/weixin_41294615/article/details/104652105).
代碼分析
-
代碼大多數(shù)都是通過串口實現(xiàn)Ymodem協(xié)議的接收, 這兒就不詳細(xì)說明
-
后面放了我的源代碼, 詳情請參考我的源代碼.
-
主函數(shù)添加修改向量表的指令

- 打印版本信息以及跳轉(zhuǎn)指令

-
YModem相關(guān)的文件接收部分
/** *@bieafYModem升級 * *@paramnone *@returnnone */ voidymodem_fun(void) { inti; if(Get_state()==TO_START) { send_command(CCC); HAL_Delay(1000); } if(Rx_Flag)//Receiveflag { Rx_Flag=0;//cleanflag /*拷貝*/ temp_len=Rx_Len; for(i=0;iswitch(temp_buf[0]) { caseSOH:///<數(shù)據(jù)包開始 { staticunsignedchardata_state=0; staticunsignedintapp2_size=0; if(Check_CRC(temp_buf,temp_len)==1)/// { if((Get_state()==TO_START)&&(temp_buf[1]==0x00)&&(temp_buf[2]==(unsignedchar)(~temp_buf[1])))/// { printf(">Receivestart... "); Set_state(TO_RECEIVE_DATA); data_state=0x01; send_command(ACK); send_command(CCC); /*擦除App2*/ Erase_page(Application_2_Addr,40); } elseif((Get_state()==TO_RECEIVE_END)&&(temp_buf[1]==0x00)&&(temp_buf[2]==(unsignedchar)(~temp_buf[1])))/// { printf(">Receiveend... "); Set_Update_Down(); Set_state(TO_START); send_command(ACK); HAL_NVIC_SystemReset(); } elseif((Get_state()==TO_RECEIVE_DATA)&&(temp_buf[1]==data_state)&&(temp_buf[2]==(unsignedchar)(~temp_buf[1])))/// { printf(">Receivedatabag:%dbyte ",data_state*128); /*燒錄程序*/ WriteFlash((Application_2_Addr+(data_state-1)*128),(uint32_t*)(&temp_buf[3]),32); data_state++; send_command(ACK); } } else { printf(">Notpasscrc "); } }break; caseEOT://數(shù)據(jù)包開始 { if(Get_state()==TO_RECEIVE_DATA) { printf(">ReceiveEOT1... "); Set_state(TO_RECEIVE_EOT2); send_command(NACK); } elseif(Get_state()==TO_RECEIVE_EOT2) { printf(">ReceiveEOT2... "); Set_state(TO_RECEIVE_END); send_command(ACK); send_command(CCC); } else { printf(">ReceiveEOT,Buterror... "); } }break; } } } - 其中部分函數(shù)未在以上代碼中展現(xiàn), 詳情請參看文末給出的源碼鏈接.
4. 整體測試
本節(jié)主要對前三節(jié)的教程做測試驗證BootLoader+App的升級功能。
源代碼
BootLoader源代碼和App1源代碼可以在原作者的gitee獲?。?/span>
https://gitee.com/leafguo/leaf_notes/STM32CubeMX/STM32CubeMx_OTA
代碼的下載
- 由下圖可知兩份代碼的下載區(qū)域是不一樣的,所以他們「下載的區(qū)域也不一樣」。

BootLoader的下載
- BootLoader的代碼默認(rèn)是最開始的所以不需要特別設(shè)置代碼的下載位置
-
按照下圖, 修改擦除方式為
Erase Sectors, 大小限制在0X5000(20K)

- 燒錄代碼
- 運(yùn)行, 通過串口1打印輸出, 會看到以下打印消息
- 說明BootLoader已經(jīng)成功運(yùn)行

App1的下載
-
App1稍微復(fù)雜一點(diǎn), 需要將代碼的起始位置設(shè)置為
0x08005000 -
同時也要修改擦除方式為
Erase Sectors, 見下圖


- 燒錄代碼
- 運(yùn)行, 通過串口1打印輸出, 會看到以下打印消息
-
說明
BootLoader已經(jīng)成功跳轉(zhuǎn)到版本號為0.0.1的App1

生成App2的.bin文件
- Keil如何生成.bin文件, 請參考這篇博文 Keil如何生成.bin文件
https://blog.csdn.net/weixin_41294615/article/details/104656577
-
修改代碼, 把版本號改為0.0.2, 并且編譯并且生成.bin文件
-
生成好之后你會得到一個.bin結(jié)尾的文件, 這就是我們待會兒YModem要傳輸?shù)奈募?/span>

使用Xshell進(jìn)行文件傳輸
- 打開Xshell
- 代碼中, 串口1進(jìn)行調(diào)試信息的打印, 串口2進(jìn)行YModem升級的
-
所以使用Xshell打開串口2進(jìn)行文件傳輸, 串口1則可以通過串口調(diào)試助手查看調(diào)試消息

- 你會看到App的版本成功升級到0.0.2了.
- 如果你到了這一步.
- 那么恭喜你! 你已經(jīng)能夠使用在線升級了!
5. 總結(jié)
通過本幾節(jié)的教程,想必你已經(jīng)會使用在線升級了,只要原理知道了其他的問題都可以迎刃而解了,除了使用YModem協(xié)議傳輸.bin文件,你還可以通過藍(lán)牙、WIFI等其他協(xié)議傳輸,只要能夠?qū)?bin文件傳輸過去,那其他的部分原理都差不多。
審核編輯 :李倩
-
二進(jìn)制
+關(guān)注
關(guān)注
2文章
808瀏覽量
42740 -
STM32
+關(guān)注
關(guān)注
2301文章
11103瀏覽量
370109 -
IAP
+關(guān)注
關(guān)注
2文章
165瀏覽量
25707
原文標(biāo)題:干貨 | 詳解 stm32 在線 IAP 升級
文章出處:【微信號:混說Linux,微信公眾號:混說Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
如何使用 Bootloader 通過 BLE 模塊升級 APROM?
STM32CubeProgrammer怎么才能在cubeprogrammer上實現(xiàn)部分升級?
AT32 MCU如何使用IAP
AT32 IAP using the USART
STM32項目分享:智能家居(機(jī)智云)升級版
程序燒錄原理
有沒有哪位大神提供一個STM32H523或者STM32H563的串口升級的IAP程序?
CUBEMX在線升級,保存在軟件子目錄下的工程文件夾自動給刪掉了,為什么?

詳解stm32在線IAP升級
評論