前言
此代碼兼容STM32F103全系列
為避免添加上升級程序造成內存不足,請使用128KB Flash及其以上的型號
這篇文章是為了能夠讓大家快速移植我的升級模板程序到自己的項目
BootLoader 程序制作
拷貝文件到自己的項目


拷貝到自己的項目(我准備了一個工程作為敘述)


在自己工程的定時器里面添加以下信息

if(IAPStructValue.PutDataFlage && IAPStructValue.UpdateFlage)IAPStructValue.DownloadTimeout++; else IAPStructValue.DownloadTimeout=0; IAPStructValue.MainTimeout++;

在自己工程的主函數添加如下信息

#include "IAP.h" IAP();
IAPLoadAPPProgram(); IAPDownloadTimeoutFunction(); IAPMainTimeoutFunction(); IAPWriteData();
大家把當前的程序下載到單片機,然后看一下串口1的打印信息

user1ROMStart: 0x8004000 用戶程序1 Flash存儲的開始地址
user1ROMSize : 0x5c00 用戶程序1 程序大小
user2ROMStart: 0x8009c00 用戶程序2 Flash存儲的開始地址
user2ROMSize : 0x5c00 用戶程序2 程序大小
大家可以在下面這個文件根據自己的芯片進行設置

所選芯片Flash大小:這個根據自己的芯片設置
BootLoader程序大小: BootLoader程序產生的bin文件大小
假設自己的BootLoader程序的bin文件大小是 15K
則可以設置上面的值 為16,18,20等
假設自己的BootLoader程序的bin文件大小是 20K
則可以設置上面的值 為22,24,26等
存儲用戶數據所用Flash大小: 這個根據自己需要的設置,
但是必須設置,因為咱升級的時候也需要記錄數據
可以是2,4,6,8等等等等
設置好以后系統便會根據大家的設置打印出來APP用戶程序的信息
當前Flash存儲分配如下圖

BootLoader程序占用 16KB
兩份用戶程序各占23KB,
第一份APP用戶程序從0x08004000開始存儲
第二份APP用戶程序從0x08009C00開始存儲
剩余的2KB用來存儲其它信息
然后接着說
這節我就用串口來模擬一下
1.每隔3S詢問下升級信息


2.串口調試助手需要返回的升級信息格式
{"version":"1.0.456","SumBin1":103,"SumBin2":244}
前面是版本號,后面兩個是兩份程序文件的校驗和(下一節會說明校驗和怎么獲取值)
然后需要把信息交給 IAPVersionDispose 函數處理

3.大家需要進入 IAPVersionDispose 函數添加一些內容


4.因為我是用串口模擬的,所以我直接這樣寫

5.測試

在發送給模塊升級信息以后,模塊先提取升級信息里面的版本號
如果和當前的不一致,再嘗試提取bin文件的校驗和
然后,接着發送詢問哪一份文件的指令
6.需要在串口中斷中寫上以下程序
以下程序就是把接收的程序寫到環形隊列里面
主循環會提取里面的數據,然后寫到flash里面
if(IAPStructValue.PutDataFlage && (IAPStructValue.PutDataFlage^IAPStructValue.Overflow))//可以往環形隊列里面寫數據,同時沒有溢出 { if(PutData(&rb_tIAP,NULL,&Res,1) == -1) //&Res :為數據地址 1:寫一個數據,可以寫多個 { IAPStructValue.Overflow = 1;//環形隊列溢出 } }


IAPStructValue.PutDataFlage 是允許寫入的標志
這個標志是在這里允許的

7.還需要做個處理
在程序接收超時(接收完成)的地方寫上

因為我是用的串口接收數據,所以如果串口進入了空閑中斷
我就認為程序接收完了
說一下流程

其實現在就需要把對應的程序文件發給單片機了
不過咱還沒有制作好用戶程序,所以需要到下一節再接着測試步驟
現在看細節處理
雖然我的升級模板可以保證可靠的把程序寫入Flash並且如果檢測有問題則自動切換到上一份程序運行,
但是需要避免另一件事情(用戶程序本身執行了一段時間以后出問題了.....)
結果造成了不停的重啟....
解決方案是利用一個按鈕
我一直按着那個按鈕.
在進入BootLoader程序的時候,我檢測下那個按鈕是不是按下了,如果按下了就不嘗試加載用戶程序了

但是咱還需要控制程序重新升級
按下按鈕超過3S,
指示燈快閃,
松開按鈕,
快閃3S后,寫入升級標志,清除版本號,重啟

BootLoader判斷有升級標志,則不會嘗試加載用戶程序

就會執行獲取升級信息->獲取程序文件
然后就完成了重新升級
大家會有疑惑,如果運行了用戶程序怎么控制升級呢???
其實用戶程序可以定時獲取下程序版本,如果不一致,就置位一下升級標志,然后重啟即可
提醒
越往后我的源碼基本上都再次做了封裝!
我只告訴大家如何使用我的源碼,使用哪些接口!
大家想研究我的源碼,就去看前面幾節

補充
在前面的升級程序中串口里面是這樣寫的
void USART1_IRQHandler(void)//串口1中斷服務程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { Res =USART_ReceiveData(USART1); //讀取接收到的數據 Usart1ReadBuff[Usart1ReadCnt] = Res; //接收的數據存入數組 Usart1ReadCnt++; if(Usart1ReadCnt > Usart1ReadLen -10)//防止數組溢出 { Usart1ReadCnt = 0; } Usart1IdleCnt = 0; if(HttpDataStartFlage) { //可以往環形隊列里面寫數據,同時沒有溢出 if(IAPStructValue.PutDataFlage && (IAPStructValue.PutDataFlage^IAPStructValue.Overflow)) { if(PutData(&rb_tIAP,NULL,&Res,1) == -1) { IAPStructValue.Overflow = 1;//環形隊列溢出 } } } //解析http數據-------------------------------Start //HTTP/1.1 200 OK if(!HttpHeadOK && IAPStructValue.PutDataFlage) { if(Res=='H' && HttpHeadCnt==0)HttpHeadCnt++; else if(Res=='T' && HttpHeadCnt==1)HttpHeadCnt++; else if(Res=='T' && HttpHeadCnt==2)HttpHeadCnt++; else if(Res=='P' && HttpHeadCnt==3)HttpHeadCnt++; else if(Res=='/' && HttpHeadCnt==4)HttpHeadCnt++; else if(Res=='1' && HttpHeadCnt==5)HttpHeadCnt++; else if(Res=='.' && HttpHeadCnt==6)HttpHeadCnt++; else if(Res=='1' && HttpHeadCnt==7)HttpHeadCnt++; else if(Res==' ' && HttpHeadCnt==8)HttpHeadCnt++; else if(Res=='2' && HttpHeadCnt==9)HttpHeadCnt++; else if(Res=='0' && HttpHeadCnt==10)HttpHeadCnt++; else if(Res=='0' && HttpHeadCnt==11)HttpHeadCnt++; else if(Res==' ' && HttpHeadCnt==12)HttpHeadCnt++; else if(Res=='O' && HttpHeadCnt==13)HttpHeadCnt++; else if(Res=='K' && HttpHeadCnt==14){HttpHeadOK = 1;HttpHeadCnt=0;HttpDataLength=0;} else { HttpHeadCnt=0; } } #ifdef UserContentLength //Content-Length: XXXXXXXX if(HttpHeadOK && !HttpDataLengthOK)//獲取http發過來的數據個數 { if(Res=='-' && HttpHeadCnt==0) HttpHeadCnt++; else if(Res=='L' && HttpHeadCnt==1)HttpHeadCnt++; else if(Res=='e' && HttpHeadCnt==2)HttpHeadCnt++; else if(Res=='n' && HttpHeadCnt==3)HttpHeadCnt++; else if(Res=='g' && HttpHeadCnt==4)HttpHeadCnt++; else if(Res=='t' && HttpHeadCnt==5)HttpHeadCnt++; else if(Res=='h' && HttpHeadCnt==6)HttpHeadCnt++; else if(Res==':' && HttpHeadCnt==7)HttpHeadCnt++; else if(Res==' ' && HttpHeadCnt==8)HttpHeadCnt++; else if(HttpHeadCnt>=9 && HttpHeadCnt<=16 )//最大99999999個字節. 16:99999999 17:999999999 18:9999999999 { if(Res!=0x0D) { HttpDataLength = HttpDataLength*10 + Res - '0'; HttpHeadCnt++; } else { HttpDataLengthOK = 1; HttpHeadCnt = 0; } } else { HttpHeadCnt = 0; } } if(HttpHeadOK && HttpDataLengthOK && HttpDataLength && !HttpHeadEndOK) #else if(HttpHeadOK && !HttpHeadEndOK) #endif {//0D 0A 0D 0A if(Res==0x0D && HttpHeadCnt==0)HttpHeadCnt++; else if(Res==0x0A && HttpHeadCnt==1)HttpHeadCnt++; else if(Res==0x0D && HttpHeadCnt==2)HttpHeadCnt++; else if(Res==0x0A && HttpHeadCnt==3){HttpHeadEndOK = 1;} else HttpHeadCnt = 0; } if(HttpHeadEndOK == 1)//http數據的head已經過去,后面的是真實數據 { HttpHeadEndOK=0; HttpHeadCnt = 0; HttpDataLengthOK=0; HttpDataStartFlage=1; } //解析http數據-------------------------------end } }
其實大家應該明白為什么這樣寫
畢竟是http訪問,會有數據頭,上面就是為了去掉數據頭
