高柱榮,蔣昌茂,劉洪林
(1.桂林市利通電子科技有限責(zé)任公司,廣西 桂林 541004;2.桂林師范高等??茖W(xué)校,廣西 桂林 541001;3.桂林航天工業(yè)學(xué)院計算機(jī)科學(xué)與工程學(xué)院,廣西 桂林 541004)
智能燃?xì)獗硪殉蔀榫用裆钣脷獾囊环N主要計量儀表,被廣泛應(yīng)用并分散地安裝在各戶居民家中,其設(shè)計缺陷修補(bǔ)、功能改進(jìn)或性能提升等需求,在物聯(lián)網(wǎng)時代將變得簡單可行。本文基于窄帶物聯(lián)網(wǎng)(narrow band-intemet of things,NB-IoT)燃?xì)獗眄?xiàng)目,研究設(shè)計了一種安全、可靠的遠(yuǎn)程升級方案。
物聯(lián)網(wǎng)燃?xì)獗碛芍骺匚⒖刂破?micro controller,MCU)、窄帶(narrow band,NB)通信模組、計量單元、閥控單元、液晶顯示器(liquid crystal display,LCD)、蜂鳴器、按鍵、實(shí)時時鐘、供電單元等組成,主要實(shí)現(xiàn)燃?xì)馐褂昧康牟杉嬃?、連網(wǎng)上報數(shù)據(jù)和接收服務(wù)器下發(fā)的命令數(shù)據(jù)等功能。而遠(yuǎn)程升級功能則是利用物聯(lián)網(wǎng)燃?xì)獗碜陨砭哂械木W(wǎng)絡(luò)連接特性,通過遠(yuǎn)程操控實(shí)現(xiàn)對燃?xì)獗砉碳绦虻母拢杭锤轮骺豈CU內(nèi)的程序,以達(dá)到改進(jìn)和提升的目的。本文的升級系統(tǒng)由業(yè)務(wù)服務(wù)器、燃?xì)獗斫K端和升級服務(wù)器三部分組成。
STM32L0 MCU是意法半導(dǎo)體公司推出的、基于ARM Cortex-M0+內(nèi)核的系列微控制器[1],具有集成外設(shè)單元豐富、高性能與超低能耗完美平衡等特性,同時具有低成本的優(yōu)勢,非常適合由電池供電或由能量收集供電的物聯(lián)網(wǎng)應(yīng)用。本文選擇STM32L073RZ作為物聯(lián)網(wǎng)燃?xì)獗淼闹骺豈CU。
應(yīng)用程序編程(in application programming,IAP)技術(shù)是指MCU允許正在運(yùn)行的程序?qū)ζ瑑?nèi)Flash的部分區(qū)域進(jìn)行編程更新,更新后可以執(zhí)行新的程序,從而實(shí)現(xiàn)程序的升級[2-4]。STM32L0芯片有三種啟動模式可選,需要配置為:從Flash主存儲器啟動,才能啟用IAP功能。
升級系統(tǒng)組成如圖1所示。
圖1 升級系統(tǒng)組成圖
IAP的具體應(yīng)用可通過多種不同的方式實(shí)現(xiàn)。文獻(xiàn)[5]~文獻(xiàn)[6]把Flash分成一個Bootloader程序區(qū)和一個APP程序區(qū)。在APP程序中接收升級數(shù)據(jù)包,臨時存儲在EEPROM中,接收完成后重啟系統(tǒng)。在Bootloader程序中,將數(shù)據(jù)從EEPROM拷貝到Flash的APP程序區(qū)完成升級[5-6]。該方案的缺點(diǎn)是需要額外的存儲器硬件、增加了成本,而且升級固件存儲在EEPROM中容易被非法訪問和復(fù)制。對比文獻(xiàn)[5],文獻(xiàn)[7]~文獻(xiàn)[8]在Flash中多劃分出了一個文件存儲區(qū),代替EEPROM存儲升級數(shù)據(jù)包[7-8],較好地解決了額外增加硬件成本和固件數(shù)據(jù)文件安全的問題。但該方法仍然需要在Bootloader程序中更新APP程序,增加了Bootloader程序的復(fù)雜性和升級失敗機(jī)率。
針對上述方案的不足,本文作了進(jìn)一步的改進(jìn),程序設(shè)計成Bootloader和APP兩個獨(dú)立的工程。APP工程在編譯前通過設(shè)置不同的起始地址,編譯生成兩個固件:APP-A和APP-B。升級時,如果MCU正在運(yùn)行的是APP-A,則僅更新最新版本APP-B到其對應(yīng)的Flash地址中,不更改原版APP-A固件程序,反之亦然。該設(shè)計可徹底解決升級過程中因意外失敗,導(dǎo)致不可回退到原版本程序的問題。
STM32L073RZ芯片屬于大容量產(chǎn)品,具有192 KB的片內(nèi)Flash存儲器和20 KB的 RAM。Flash的起止地址為0x08000000~0x0802FFFF,共分為1 536頁。每頁大小為128 B。而每扇區(qū)則由32頁4 KB組成。
為實(shí)現(xiàn)IAP升級,需要把Flash人為地分成幾個部分:一部分存儲引導(dǎo)程序,即Bootloader程序區(qū);另一部分存儲用戶應(yīng)用程序,即APP程序區(qū);還有一部分存儲升級標(biāo)志或用戶程序標(biāo)志,簡稱配置信息區(qū)。本設(shè)計把Flash分成Bootloader程序區(qū)、配置信息區(qū)、APP-A程序區(qū)和APP-B程序區(qū)四個部分。Flash存儲器分區(qū)如表1所示。
表1 Flash存儲器分區(qū)
在Keil MDK-ARM開發(fā)環(huán)境中,編譯BootLoader程序工程時,其起始地址需設(shè)置為0x8000000,空間大小為0x3000。
APP程序工程則需要編譯兩次,得到兩個固件程序。APP-A程序的起始地址設(shè)置為0x8004000,空間大小為0x16000。APP-B程序的起始地址設(shè)置為0x801A000,空間大小為0x16000。另外,還需要將APP程序工程編譯生成的Axf文件,轉(zhuǎn)換成升級固件程序Bin文件,用于實(shí)際燒入MCU。設(shè)置User選項(xiàng)卡下的After Build/Rebuildr的Run #1選項(xiàng)為:C:Keil_v5ARMARMCCinfromelf.exe-bin-o.ReleaseTG134_SRC.bin.ReleaseTG134_SRC.axf。編譯后自動轉(zhuǎn)換生成一個TG134_SRC.bin二進(jìn)制文件。
Bootloader程序工程編譯生成的Bootloader.hex文件,需要與APP程序工程生成的APP-A.hex文件合并,生成一個新的HEX文件,用于產(chǎn)品生產(chǎn)時燒錄到MCU中。推薦使用優(yōu)秀的合并工具SEGGER J-Flash進(jìn)行合并。經(jīng)轉(zhuǎn)換生成的APP-A.bin和APP-B.bin文件,需要再經(jīng)過加密才能用于升級,而且加密算法最好使用3DES或SM4以上級別的算法,安全才有保障。
在通常的升級方案中,Bootloader程序是整個方案中的關(guān)鍵部分,需完成初始化程序、升級文件檢測、固件更新以及程序跳轉(zhuǎn)等工作。特別是固件更新,一般耗時較長,容易受到電源不穩(wěn)或外部干擾的影響,造成不可回退的升級失敗。本方案中,Bootloader程序僅從配置信息區(qū)讀取最新版APP程序的存儲地址,然后把程序跳轉(zhuǎn)到對應(yīng)的地址執(zhí)行APP程序;其他的升級功能操作放到APP程序中實(shí)現(xiàn)。
以下是程序完整的Bootloader代碼。
Typedef void(*pFunction)(void);
//定義函數(shù)指針類型
pFunction JumpToApplication;
//定義函數(shù)指針
uint32_t u32JumpAddress;
//32位跳轉(zhuǎn)地址
#define CONFIG_ADDRESS(uint32_t)0x08003000
#define APP_A_ADDRESS (uint32_t)0x08004000
#define APP_B_ADDRESS (uint32_t)0x0801A000
void IAP_StartUserApp(uint32_t appAddr)
{
//STM32L073RZ的棧頂?shù)刂分当仨氃诖朔秶鷥?nèi)
if((*(__IO uint32_t *)appAddr >=0x20000000)&&\(*(__IO uint32_t *)appAddr<=0x20004FFF))
{
//讀取程序跳轉(zhuǎn)地址
u32JumpAddress =*(__IO uint32_t *)(appAddr+4);
//初始化函數(shù)指針
JumpToApplication=(pFunction)u32JumpAddress;
//初始化棧頂指針
_set_MSP(*(__IO uint32_t *)appAddr);JumpToApplication();
//跳轉(zhuǎn)到用戶應(yīng)用程序
}
}
int main(void)
{
第四,從上述政治與文學(xué)的關(guān)系研究進(jìn)一步拓展,則體現(xiàn)為東方主義視域下的明治文學(xué)家/文人(如:夏目漱石、內(nèi)藤湖南、依田學(xué)海)的中國觀(包括中國形象)或戰(zhàn)爭觀研究。如:李雁南、泊功、宋剛等學(xué)者的研究。[56-58]
uint32_t u32AppAddress = 0;
HAL_Init();
SystemClock_Config();
//讀取最新版本APP程序的存儲地址
u32AppAddress=*(__IO uint32_t *)CONFIG_ADDRESS;
if(APP_B_ADDRESS == u32AppAddress)
//跳轉(zhuǎn)到APP-B程序執(zhí)行
IAP_StartUserApp(APP_B_ADDRESS);else
//跳轉(zhuǎn)到APP-A程序執(zhí)行
IAP_StartUserApp(APP_A_ADDRESS);while(1){}
}
3.3.1 重定向中斷向量表
STM32芯片內(nèi)部通過中斷向量表響應(yīng)中斷。中斷向量表的起始地址是0x08000004。在程序正常運(yùn)行過程中,如果有中斷發(fā)生,芯片內(nèi)部硬件機(jī)制會將PC指針強(qiáng)制指向中斷向量表,并根據(jù)中斷源取出對應(yīng)中斷向量執(zhí)行中斷服務(wù)程序[9]。然而,APP程序是一套完整獨(dú)立的程序,自有另外一張中斷向量表。其起始地址是APP程序首地址+4,與原Flash中存儲的中斷向量表已發(fā)生了偏移。在APP程序執(zhí)行過程中,如果有中斷請求發(fā)生,PC指針仍然被強(qiáng)制跳轉(zhuǎn)到0x08000004地址的中斷向量表,而不是APP程序自己的中斷向量表。因此,需要對中斷向量表進(jìn)行重定向,即配置向量表偏移量寄存器,使之指向APP程序的向量表[10]。
在APP程序的main()函數(shù)中,第一件要做的事情就是重定向中斷向量表,從配置信息區(qū)讀取當(dāng)前APP程序自身的存儲地址,把地址值設(shè)置為APP程序的中斷向量表偏移量。
程序代碼如下。
int main(void)
{
sIAP.CurrAppAddr = *(__IO uint32_t *)CONFIG_ADDRESS;
//讀取當(dāng)前APP程序的存儲地址
if((APP_A_ADDRESS != sIAP.CurrAppAddr)&& \(APP_B_ADDRESS != sIAP.CurrAppAddr))
{
//未設(shè)置則默認(rèn)為APP_A
sIAP.CurrAppAddr = APP_A_ADDRESS;IAP_WriteConfig(CONFIG_ADDRESS,sIAP.CurrAppAddr);
//把APP-A的址址寫入配置信息區(qū)
}
//設(shè)置中斷向量表偏移量
SCB->VTOR = sIAP.CurrAppAddr;HAL_Init();
SystemClock_Config();
//以下是應(yīng)用業(yè)務(wù)和升級包處理程序
}
3.3.2 固件安全傳輸與更新
升級流程如圖2所示。
圖2 升級流程圖
固件升級服務(wù)的安全直接關(guān)系到整個系統(tǒng)的安全,因?yàn)榇朔?wù)能徹底更改終端的運(yùn)行程序。首先,必須保證新版本固件程序本身經(jīng)過嚴(yán)格測試才能發(fā)布到升級服務(wù)器中。特別是新固件程序自身的再次升級的功能,必須確保升級后的程序,即使有錯誤也有繼續(xù)升級的能力。另一個重點(diǎn)是對內(nèi)部和外部惡意攻擊的提防,如嗅探攻擊、癱瘓升級服務(wù)器攻擊和提供假冒升級固件程序攻擊等。
因此,升級服務(wù)由業(yè)務(wù)系統(tǒng)對特定的終端實(shí)施單獨(dú)開放,僅有業(yè)務(wù)系統(tǒng)發(fā)送升級命令,并為該終端標(biāo)記升級允許標(biāo)志后,才允許該終端進(jìn)行升級。升級服務(wù)器的IP、端口等信息也由業(yè)務(wù)系統(tǒng)伴隨升級命令一起發(fā)送給終端,終端內(nèi)不固定存儲升級服務(wù)器的IP及端口。固件程序必須經(jīng)過加密和驗(yàn)證后,才能存入升級服務(wù)器:即服務(wù)器內(nèi)不存儲固件程序Bin文件,僅存儲加密后的Bin文件,保障服務(wù)器端不泄露程序的Bin文件。
燃?xì)獗斫K端主動與業(yè)務(wù)服務(wù)器連接,在完成正常的抄表、充值等業(yè)務(wù)之后,如果有新版本固件需要升級到終端,則開啟升級。
①由業(yè)務(wù)服務(wù)器發(fā)送允許升級命令給終端,通知終端在一定的時間內(nèi)允許升級。
②收到終端的應(yīng)答之后,業(yè)務(wù)服務(wù)器再通知升級服務(wù)器在一定的時間內(nèi)允許某個終端來申請升級。
③收到升級服務(wù)器的應(yīng)答之后,業(yè)務(wù)服務(wù)器給終端發(fā)送升級命令,同時把升級服務(wù)器的IP和端口號通知終端。
④終端主動斷開與業(yè)務(wù)服務(wù)器的連接,重新連接到升級服務(wù)器,并發(fā)送自己當(dāng)前的固件版本信息給升級服務(wù)器,申請升級。
⑤升級服務(wù)器根據(jù)終端當(dāng)前版本信息,選擇一個新版APP固件程序。如果終端當(dāng)前版本為APP-A,則選擇新版APP-B;否則,選擇新版APP-A,把選擇的新版固件程序文件大小等信息反饋給終端。
⑥終端從邏輯地址0開始,按合適的長度請求下載第一包固件數(shù)據(jù),升級服務(wù)器按請求地址和長度取出固件數(shù)據(jù)返回給終端。
⑦終端把接收到的固件數(shù)據(jù),經(jīng)過校驗(yàn)和解密之后,寫入Flash中對應(yīng)的APP程序區(qū)內(nèi),繼續(xù)請求下載下一包固件數(shù)據(jù)。
⑧當(dāng)終端接收到最后一包固件數(shù)據(jù)并寫入Flash之后,校驗(yàn)整個新版APP固件程序的合法性。最后,更新配置信息區(qū)的最新版APP程序存儲地址選項(xiàng)的值。這是整個升級過程最為關(guān)鍵的一步。但只更新一個字的內(nèi)容,失敗的機(jī)率可以忽略不計。即使失敗了,終端還可以繼續(xù)運(yùn)行舊版的程序。
⑨終端主動斷開與升級服務(wù)器的連接,復(fù)位系統(tǒng)。
⑩終端重啟后,連接到業(yè)務(wù)服務(wù)器,通知業(yè)務(wù)服務(wù)器該終端已成功完成升級。
升級過程中,由于網(wǎng)絡(luò)信號質(zhì)量或其他的原因,終端有可能會收不到來自服務(wù)器的應(yīng)答,或收到不完整的數(shù)據(jù)。因此,終端需要做很多的容錯措施,如最多3次請求下載同一個數(shù)據(jù)包等。如果本次升級沒有成功,則可等待到下一次連接時再進(jìn)行升級。
本課題項(xiàng)目已在國內(nèi)某市安裝的NB-IoT物聯(lián)網(wǎng)燃?xì)獗砩线M(jìn)行了驗(yàn)證測試,一共測試100臺。一次連接升級就成功的有97臺,其中有92臺的升級耗時在4 min之內(nèi),另外5臺耗時在4~6 min之間。升級失敗的3臺,再次啟動連接進(jìn)行第2次升級測試,在6 min內(nèi)全部成功完成升級。最后,查看服務(wù)器后臺數(shù)據(jù),發(fā)現(xiàn)未能在4 min內(nèi)完成升級的燃?xì)獗碛?臺。其信號強(qiáng)度值都沒有超過10(0~31,值越大表示信號越好),并且都有多次請求下載同一數(shù)據(jù)包的情況;同時,且其安裝位置環(huán)境都是較為封閉、屏蔽物較多。由此可知,物聯(lián)網(wǎng)燃?xì)獗淼木W(wǎng)絡(luò)信號質(zhì)量對升級的成功與否影響較大。
本文主要根據(jù)安全和可靠的要求,充分考慮了終端設(shè)備的安全、固件程序的安全、升級服務(wù)器的安全和升級意外失敗的回退等多個方面的需求,開展整個升級方案的設(shè)計工作。對Flash進(jìn)行了科學(xué)的分區(qū),在程序設(shè)計上把不可更新的Bootloader程序極簡化,把可重復(fù)升級更新的APP程序精準(zhǔn)化。對終端的升級操作引入了時效性,而升級服務(wù)器IP地址則隱藏化。該方案已在實(shí)際應(yīng)用中得到了驗(yàn)證,對其他物聯(lián)網(wǎng)終端的遠(yuǎn)程升級設(shè)計有一定的參考價值。