前言:
在平常的項(xiàng)目開發(fā)和調(diào)試中,下載程序一般使用的是外部下載器或者串口的方式實(shí)現(xiàn)對單片機(jī)的程序下載和刷新,這種方法在項(xiàng)目的開發(fā)階段是常用的方式。
但是當(dāng)項(xiàng)目開發(fā)完成推向市場的時(shí)候,很多時(shí)候需要對產(chǎn)品進(jìn)行升級,而這個(gè)時(shí)候產(chǎn)品又已經(jīng)是加了外殼的或者被封裝起來了,一般也不會在外面預(yù)留出來下載接口之類的。
如果這個(gè)時(shí)候我想要更新產(chǎn)品的程序的話,可能就得要重新打開產(chǎn)品的外殼,然后通過下載器更新程序,更新完成之后再把外殼裝上,這種做法顯然是不太現(xiàn)實(shí)的。但是我們又必須要給產(chǎn)品進(jìn)行升級,那該怎么辦呢?這個(gè)時(shí)候就可以考慮使用產(chǎn)品本身預(yù)留的一些外部通信接口(如:USB、RS232、ES485、以太網(wǎng)口等)或者內(nèi)部無線(如:wifi、藍(lán)牙、4/5G網(wǎng)絡(luò))等對產(chǎn)品進(jìn)行升級。
上面介紹的這種通過外部有線接口或者無線通信的方式進(jìn)行的更新其實(shí)是一種在線更新的方式,即OTA升級技術(shù)。
那問題來了,到底什么是OTA升級技術(shù)呢?待我慢慢道來!
1、OTA 在線升級
OTA:Over-the-Air Technology,字面意思理解:空中下載技術(shù)。
OTA 在線升級:通過OTA的方式實(shí)現(xiàn)產(chǎn)品軟件更新的一種方式。
所以,簡單而言,通過外部的方式(有線 / 無線)對產(chǎn)品進(jìn)行更新,而不是用傳統(tǒng)的編程器刷入固件的方式就可以稱之為 OTA 在線升級。
嚴(yán)格意義上來講,OTA 指的是空中下載,即只有通過無線的方式進(jìn)行更新的才稱之為 OTA 升級;而那種通過外部的接口接線來實(shí)現(xiàn)的更新,應(yīng)該稱之為本地升級。這兩者還是有點(diǎn)區(qū)別的,只是一般我們都沒有那么嚴(yán)格去區(qū)分罷了!
2、實(shí)現(xiàn)方式
那既然理解了升級的概念了,該怎么去實(shí)現(xiàn)呢?
我們一般的做法是會將這個(gè)升級功能進(jìn)行劃分,分為兩部分:
1) 接收新的升級固件并完成新舊固件的替換,這部分代碼為 BootLoader;
2) 產(chǎn)品功能的正常程序,用于執(zhí)行各種應(yīng)用功能,這部分程序稱為 App。
那就是說,要實(shí)現(xiàn)在線升級,就需要準(zhǔn)備兩份程序,一份是BootLoader ,另一份是App。其中 bootloader 用于將外部傳入的新固件(應(yīng)用程序App)接收到內(nèi)部并存儲,接收完成以后,由 bootloader 用新接收到的固件去替換舊的固件,替換完成之后跳轉(zhuǎn)到新的應(yīng)用程序中進(jìn)行執(zhí)行。這樣就完成了產(chǎn)品的固件更新。
注意:需要將 bootloader 和應(yīng)用程序App的空間分開,兩者是不能發(fā)生重疊的。
3、操作方式
3.1、后臺式升級
后臺式升級的意思是:在進(jìn)行升級的時(shí)候,接收新固件包的方式是在后臺進(jìn)行的,不會影響功能的正常執(zhí)行。等到固件更新完成之后,再跳轉(zhuǎn)到Bootloader中去用新的固件替換舊的固件,替換完成之后呢再跳轉(zhuǎn)到App去執(zhí)行。
比如,現(xiàn)在的智能手機(jī)的在線更新就是后臺式升級的方式。在你升級系統(tǒng)的時(shí)候,接收升級包的過程中,你還是可以正常使用的手機(jī)的,打電話、看視頻、玩游戲等都不耽誤,直到下載完成,你點(diǎn)擊了開始更新之后,手機(jī)才進(jìn)入更新狀態(tài),不讓你操作,等更新完畢之后重啟就又可以繼續(xù)操作了。
3.2、非后臺式式更新
非后臺式升級的意思是:在進(jìn)行升級的時(shí)候,接收固件時(shí)需要跳轉(zhuǎn)到Bootloader,這個(gè)時(shí)候你不能在使用這個(gè)產(chǎn)品的任何功能,只能一直等著它接收并完成更新,完成之后你才能繼續(xù)操作其他的功能。
4、STM32 的在線升級
本文以STM32為例展開講解怎么實(shí)現(xiàn)OTA升級和操作的方法。
4.1、劃分 Flash 區(qū)域
按照前面講述的有關(guān)在線升級的原理,我們知道是要準(zhǔn)備兩份代碼的,一份是BootLoader,另外一份是App。由于這兩份代碼在STM32中都是要存放在Flash中的,而且它們的空間還不能重疊,要獨(dú)立區(qū)分開。
所以,按照這個(gè)原則,我們可以考慮在Flash中分三個(gè)區(qū)域出來,如下圖所示:
上圖中的三塊Flash分別用于:
(1)用于存放Bootloader程序;
(2)用于存放應(yīng)用程序;
(3)用于存放接收到的新固件。(注:這部分可要可不要,根據(jù)你的設(shè)計(jì)選擇)
注意:上圖中(3)這個(gè)Flash區(qū)域是考慮用于保存在線升級的固件的,作為備份固件。方便用于以后系統(tǒng)出現(xiàn)異常時(shí),可以從這個(gè)備份固件中重新加載到App中,防止固件丟失!
4.2、實(shí)操1 - Flash空間地址的劃分
首先,我們要知道,在stm32中,flash的地址空間是從0x08000000開始的,在keil中也是默認(rèn)的從這個(gè)位置開始的。
一般而言,Bootloader 是在上電時(shí)默認(rèn)開始執(zhí)行,因此將Bootloader程序可以存放到STM32默認(rèn)執(zhí)行的位置(keil編譯器默認(rèn)從0x08000000地址開始存放)。
應(yīng)用程序從 Bootloader 后開始存放即可,只要不和BootLoader發(fā)生沖突即可。
假設(shè) Bootloader 的大小為10k,即0x2800字節(jié),那么可以選取 0x08000000 ~ 0x08002800 地址范圍作為BootLoader的存放區(qū)域。
應(yīng)用程序從0x08005000開始存放(至于選多大的范圍,要根據(jù)你的應(yīng)用程序大小進(jìn)行考慮,建議保留一些余量)。
劃分如下圖所示:
4.3、實(shí)操2 - 設(shè)置工程
(1)設(shè)置起始地址和大小
準(zhǔn)備兩個(gè)工程,一個(gè)是bootloader的程序,另外一個(gè)是應(yīng)用程序的工程,并對工程進(jìn)行設(shè)置。bootloader選擇默認(rèn)執(zhí)行的位置,應(yīng)用程序根據(jù)實(shí)際需要設(shè)置開始存放到flash指定的位置。
比如:App的起始地址設(shè)為0x08005000,大小設(shè)為0x1B000(RAM總大小0x20000-0x5000)。設(shè)置如下圖:
BootLoader 也是一樣的設(shè)置方式,只是地址不同而已。
(2)生成 bin 文件
另外需要注意的,應(yīng)用程序需要轉(zhuǎn)換為bin文件才可以寫入Flash中,能發(fā)送到產(chǎn)品中的固件也是要先轉(zhuǎn)為bin格式的文件的。
編譯器可以選擇生成hex文件,再把hex文件轉(zhuǎn)換為bin文件。還可以使用簡單方法,MDK-Keil中點(diǎn)開User選項(xiàng)卡,設(shè)置如下圖:
即在編譯后直接執(zhí)行fromelf.exe命令將.axf文件轉(zhuǎn)換為.bin文件。生成的bin文件在工程目錄下的out文件夾下。
4.4、實(shí)操3 - 接收固件更新包
接收固件更新包的話,就要根據(jù)你的實(shí)際產(chǎn)品進(jìn)行選擇了。
首先,如果你是后臺式更新的話,那接收固件更新包的功能就要在App中實(shí)現(xiàn);如果你是非后臺式更新的話,那接收固件更新包的功能就要在BootLoader中實(shí)現(xiàn)。
其次,要考慮接收固件更新包的方式。無線的話用的是wifi、藍(lán)牙、4/5G網(wǎng)絡(luò)還是啥的;有線的話用的是串口UART、Spi、IIC、CAN、以太網(wǎng)還是啥的。
最后,最好的方式是能夠?qū)邮盏降墓碳掳鲆粋€(gè)校驗(yàn),防止數(shù)據(jù)接收過程中出現(xiàn)錯(cuò)誤,導(dǎo)致升級后出現(xiàn)嚴(yán)重問題。
注意:BootLoader 需要提前刷入,不然無法完成在線升級!
4.5、實(shí)操4 - 拷貝程序至Flash
接收成功固件包之后,需要將數(shù)據(jù)寫入到 Flash 的指定位置(比如 0x08005000)完成固件的更新程序?qū)懭搿?/p>
STM32對Flash的操作過程如下:
1) Flash解鎖。(FlashUnclock)
2) 擦除App所在的Flash頁。
3) 向App的Flash區(qū)域?qū)懭霐?shù)據(jù)。
4) 寫完后,F(xiàn)lash上鎖。(FlashClock)
4.6、實(shí)操5 - 跳轉(zhuǎn)至 App 應(yīng)用程序
將接收到應(yīng)用程序全部正確寫入Flash的App指定位置后,Bootloader 需要完成跳轉(zhuǎn)到應(yīng)用程序App的開始位置的操作。
跳轉(zhuǎn)過程如下:
1)關(guān)閉中斷,防止在跳轉(zhuǎn)過程中有中斷發(fā)生。
2)獲取棧指針。
3)獲取復(fù)位向量,即為棧指針后的四字節(jié)內(nèi)容(32bits)。
4)重定向中斷向量表,以保證應(yīng)用程序中中斷的正常工作。應(yīng)用程序從0x08005000存儲,因此相對于Flash基地址0x08000000偏移了0x5000。
5)設(shè)置新的棧指針,上述中所獲取的棧指針。
6)跳轉(zhuǎn)至復(fù)位向量開始運(yùn)行。
4.7、特別注意 - 設(shè)置向量中斷表偏移
這個(gè)跳轉(zhuǎn)到應(yīng)用程序之后,有一個(gè)非常重要的事情要注意,就是要重新設(shè)置App的中斷向量表,否則不能正常運(yùn)行的。
首先要理解STM32的正常運(yùn)行是怎么樣的,如下圖所示:
STM32的內(nèi)部閃存(FLASH)的地址默認(rèn)是從0x8000000開始的,默認(rèn)也是從這個(gè)位置開始執(zhí)行程序的。并且在STM32的內(nèi)部有一張 “中斷向量表” 用于響應(yīng)中斷,程序在啟動以后首先會從中斷向量表取出復(fù)位中斷程序,執(zhí)行完復(fù)位中斷程序以后才會跳轉(zhuǎn)到main( )函數(shù)開始執(zhí)行。
中斷向量表的位置從0x8000004開始,如果采用的是bootloader和應(yīng)用程序的方式的話,bootloader一般放在默認(rèn)開始的位置,所以它的中斷向量表還是正確的。
而APP應(yīng)用程序是放置在其他的位置,那么APP應(yīng)用程序部分的中斷向量表就要發(fā)生偏移,才能為應(yīng)用程序找到并響應(yīng)中斷。
在應(yīng)用程序中,與程序跳轉(zhuǎn)需要注意的是,單片機(jī)從Bootloader程序跳轉(zhuǎn)至應(yīng)用程序的復(fù)位向量處開始執(zhí)行,單片機(jī)在執(zhí)行main函數(shù)之前會執(zhí)行一些系統(tǒng)初始化程序。在系統(tǒng)初始化函數(shù)void SystemInit (void);在該函數(shù)中有對中斷向量表的設(shè)置,如下:
在上圖中VECT_TAB_OFFSET默認(rèn)為0x0,而應(yīng)用程序的中斷向量表需要定位到0x08005000,因此這里需要將VECT_TAB_OFFSET值修改為0x5000。(這里可能跟跳轉(zhuǎn)程序中的設(shè)置中斷向量表的過程重復(fù),所以需要慎重考慮)。
如下:
/* Configure the Vector Table location add offset address -配置中斷向量表的起始地址-*/
#ifdef VECT_TAB_SRAM //默認(rèn)沒有定義
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
修改這個(gè)偏移:
#define VECT_TAB_OFFSET 0x5000 /*向量表基本偏移量字段。此值必須是0x200的倍數(shù) */
注意:這個(gè)中斷向量表的偏移一定要在初始化階段去完成!
到此,在線升級的原理和STM32的操作已經(jīng)講完,如果有講的不正確的地方,還請指正!
評論
查看更多