1. 什么是差分/增量升級(jí)?
借用網(wǎng)上的介紹:適合嵌入式的差分升級(jí)又叫增量升級(jí),顧名思義就是通過差分算法將源版本與目標(biāo)版本之間差異的部分提取出來制作成差分包,然后在設(shè)備通過還原算法將差異部分在源版本上進(jìn)行還原從而升級(jí)成目標(biāo)版本的過程。
差分升級(jí)方案不僅可以節(jié)省MCU內(nèi)部的資源空間、還可以節(jié)省下載流程及下載和升級(jí)過程中的功耗。
也就是說,差分升級(jí)是拿以前舊設(shè)備內(nèi)的bin,和當(dāng)前新版本的bin用某種算法算出他們的差異部分,然后在借助壓縮算法,生產(chǎn)一個(gè)極其小的差分包,然后將這個(gè)差分包下載到設(shè)備中,設(shè)備在根據(jù)解壓算法、差分還原算法,生產(chǎn)一個(gè)完整的新版本bin,然后將這個(gè)新版本bin刷到執(zhí)行區(qū)執(zhí)行代碼。
差分升級(jí)一般來說,可以極大的減少下載量,特別是對(duì)于嵌入式STM32等單片機(jī)來說,可以極大的減少維護(hù)成本,因?yàn)榍度胧皆O(shè)備的升級(jí)維護(hù)一般都是空中ota升級(jí),比如藍(lán)牙、紅外等,下載速度受到波特率、包長(zhǎng)等限制,更新固件包非常的慢,而差分升級(jí)可以讓下載的過程極大的縮小。
正常的維護(hù)版本,即使改的再多,生成的差分包bin理論上在原bin的5%左右,比如一個(gè)300k的bin,改的很多的情況下差分包也不過15k左右,而我實(shí)際測(cè)試,版本維護(hù)平均都在5~10k左右。
2. 差分升級(jí)實(shí)現(xiàn)原理
差分升級(jí)過程:
1、使用舊版本bin文件和新版本bin文件制作差分包
2、將差分包下載到設(shè)備內(nèi)
3、設(shè)備使用差分算法還原出新版本bin
4、設(shè)備將新版本bin進(jìn)行crc驗(yàn)證后刷到代碼執(zhí)行區(qū)
5、設(shè)備重啟并以新版本運(yùn)行
在過程中有2個(gè)關(guān)鍵點(diǎn):
第一:如何使用舊版本bin文件和新版本bin文件制作差分包?
該過程我們使用穩(wěn)定的開源差分算法bsdiff+lzma生成差分包,該算法被大量使用,穩(wěn)定安全,并且我們已在項(xiàng)目中批量使用,經(jīng)過長(zhǎng)時(shí)間的驗(yàn)證無任何問題。一般來說,該過程都是使用上位機(jī)來完成,嵌入式設(shè)備無需關(guān)心,我們已經(jīng)做好了上位機(jī)軟件,可以供大家隨意使用,稍后會(huì)進(jìn)行介紹。
第二:設(shè)備收到差分包后如何還原出新版本的bin文件?
該過程就是我們要講解的重點(diǎn)過程,相對(duì)應(yīng)的,嵌入式設(shè)備中,我們依然使用開源差分算法bsdiff+lzma來還原新版本文件,代碼全開源,并且我已做成了庫(kù)、抽象出了極簡(jiǎn)的接口,移植起來費(fèi)不了多少功夫。
基本是市場(chǎng)上所有的單片機(jī)如stm32、瑞薩、華大、復(fù)旦微等都可以使用,但是有內(nèi)存限制,要求ram至少要10k以上,然后是該庫(kù)本身的消耗大概是5k的rom。
3. 關(guān)鍵點(diǎn)一:差分包制作過程
對(duì)于差分包的制作,我已經(jīng)開發(fā)好了上位機(jī)軟件,界面如下圖所示:
上位機(jī)這邊主要實(shí)現(xiàn)使用開源算法bsdiff制作舊版本bin和新版本bin的差分包,然后在使用lzma壓縮算法來壓縮差分包,最終生成一個(gè)差分bin,使用方法上位機(jī)界面提示的很清楚,最終效果如下圖所示:
4. 關(guān)鍵點(diǎn)二:嵌入式設(shè)備中差分算法庫(kù)的移植(還原差分包)
4.1. 移植開關(guān)算法庫(kù)代碼
代碼已開源,地址:
https://gitee.com/qq791314247/mcu_bsdiff_upgrade
整體代碼如下圖所示:
如上圖所示,99%的代碼用戶都不用去關(guān)心,用戶只需要提供一個(gè)flash寫入接口即可,也就是該庫(kù)給定用戶flash地址、數(shù)據(jù)內(nèi)容指針、數(shù)據(jù)內(nèi)容長(zhǎng)度,用戶將該段數(shù)據(jù)寫入到flash即可,移植起來特別簡(jiǎn)單,花不了幾分鐘的功夫,這也是我花大力氣抽象接口的原因。
4.2. 使用該庫(kù)的流程
4.2.1. 使用庫(kù)的接口
對(duì)于整個(gè)庫(kù)的代碼,我們只需要關(guān)心一個(gè)接口iap_patch,iap_patch在文件”user_interface.h”中。
該接口介紹也比較清晰,差分包的還原,只需要調(diào)用這一個(gè)接口即可。
/** *@brief用戶使用差分升級(jí)時(shí)唯一需要關(guān)心的接口 * *@paramold設(shè)備中執(zhí)行區(qū)代碼所在的地址,用戶可指定flash執(zhí)行區(qū)的地址,方便算法讀出來當(dāng)前 *運(yùn)行中的代碼 *@paramoldsize設(shè)備中執(zhí)行區(qū)代碼的長(zhǎng)度,用戶可在差分包bin頭獲取 *@parampatch設(shè)備中已經(jīng)下載的差分包所在的flash地址,或者ram地址,只要能讓算法讀出來即可 *注意,下載的差分包自帶image_header_t格式的文件頭,真正的差分包需要偏 *移sizeof(image_header_t)的長(zhǎng)度 *@parampatchsize設(shè)備中已經(jīng)下載的差分包的長(zhǎng)度,用戶可在差分包bin頭獲取 *@paramnewfile新文件的大小,用戶需填入新版本bin的長(zhǎng)度,用戶亦可以差分包bin頭獲取 *@returnint然后錯(cuò)誤碼,0成功,1失敗 */ externintiap_patch(constuint8_t*old,uint32_toldsize,constuint8_t*patch, uint32_tpatchsize,uint32_tnewfile);
另外,使用該接口還原時(shí)所需要的一些信息可以在差分包文件頭中獲取,上位機(jī)在制作差分包時(shí),會(huì)自動(dòng)在差分包的bin頭加上64字節(jié)的文件頭,以便于告訴嵌入式設(shè)備舊/新版本bin文件的CRC校驗(yàn)、長(zhǎng)度等信息。所以用戶在收到差分包頭時(shí),偏移掉這64個(gè)字節(jié)的文件頭的地址才是需要給到iap_patch接口的真正的bin文件。
文件頭格式如下代碼,用戶只需要關(guān)心中文注釋的部分,其余的都是預(yù)留的信息。
/*差分包制作時(shí)自帶的文件頭信息,用戶只需要關(guān)心中文注釋的部分*/ typedefstructimage_header { uint32_tih_magic;/*ImageHeaderMagicNumber*/ uint32_tih_hcrc;/*ImageHeaderCRCChecksum差分包包頭校驗(yàn)*/ uint32_tih_time;/*ImageCreationTimestamp*/ uint32_tih_size;/*ImageDataSize差分包的大小*/ uint32_tih_load;/*DataLoadAddress上一版本舊文件的大小*/ uint32_tih_ep;/*EntryPointAddress要升級(jí)的新文件的大小*/ uint32_tih_dcrc;/*ImageDataCRCChecksum新文件的CRC*/ uint8_tih_os;/*OperatingSystem*/ uint8_tih_arch;/*CPUarchitecture*/ uint8_tih_type;/*ImageType*/ uint8_tih_comp;/*CompressionType*/ uint8_tih_name[IH_NMLEN];/*ImageName*/ uint32_tih_ocrc;/*OldImageDataCRCChecksum上一版本舊文件的CRC*/ }image_header_t; /*差分包制作時(shí)自帶的文件頭信息,用戶只需要關(guān)心中文注釋的部分*/
4.2.2. 接口使用例子
我截取一段我工程中的代碼來講解如何使用該接口還原出新版本bin文件:
#ifdefBSDIFF_UPGRADE image_header_trecv_head; uint32_trecv_hcrc;/*接收到的文件頭CRC*/ uint32_tcalculation_crc;/*計(jì)算出來的文件頭CRC*/ uint32_tspi_flash_addr=UPGRADE_PROGRAM_ADDR; memcpy(&recv_head,(uint8_t*)APPLICATION_A,sizeof(image_header_t)); recv_hcrc=BigtoLittle32(recv_head.ih_hcrc); recv_head.ih_hcrc=0; calculation_crc=crc32((uint8_t*)&recv_head,sizeof(image_header_t)); if(recv_hcrc==calculation_crc) { recv_head.ih_hcrc=recv_hcrc; recv_head.ih_time=BigtoLittle32(recv_head.ih_time); recv_head.ih_size=BigtoLittle32(recv_head.ih_size); recv_head.ih_dcrc=BigtoLittle32(recv_head.ih_dcrc); recv_head.ih_ocrc=BigtoLittle32(recv_head.ih_ocrc); /*差分升級(jí)包*/ recv_head.ih_hcrc=calculation_crc; if(crc32((uint8_t*)APPLICATION_RUN,recv_head.ih_load)!=recv_head.ih_ocrc) { APP_ERR_PRINT("fileoldcrcerr,calcrc:0X%08X,ih_oldbin_crc:0X%08X,", crc32((uint8_t*)APPLICATION_RUN, recv_head.ih_load),recv_head.ih_ocrc); gotobsdiff_out; } RTOS_LOCK(); disable_task_monitoring(ALL_TASK_RUNFLAG_BIT,true); //flash_erase_sector(UPGRADE_PROGRAM_ADDR,UPGRADE_PROGRAM_PAGE); recv_hcrc=iap_patch((uint8_t*)APPLICATION_RUN,recv_head.ih_load, (uint8_t*)(APPLICATION_A+sizeof(image_header_t)), recv_head.ih_size,UPGRADE_PROGRAM_ADDR); if(recv_hcrc!=recv_head.ih_ep) { APP_ERR_PRINT("iap_patchlenerr."); APP_ERR_PRINT("iap_patchlen:%lu,new_len:%lu",recv_hcrc,recv_head.ih_ep); gotobsdiff_out; } if(erase_program(APPLICATION_A)) { APP_ERR_PRINT("Ieraseprogramfailed."); gotobsdiff_out; } current_flash_write_addr=APPLICATION_A; for(uint32_ti=0;i(recv_head.ih_ep?/?1024);?i++) ?????????????{ ?????????????????xmq25qxx_read(spi_flash_addr,?spi_read_cache,?1024); ?????????????????if?(xflash_write(current_flash_write_addr,?spi_read_cache,?1024)) ?????????????????{ ?????????????????????APP_ERR_PRINT("I?write?program?failed."); ?????????????????????goto?bsdiff_out; ?????????????????} ?????????????????spi_flash_addr?+=?1024; ?????????????????current_flash_write_addr?+=?1024; ?????????????????APP_PRINT("current_flash_write_addr:?0X%08X",?current_flash_write_addr); ?????????????} ?????????????if?(recv_head.ih_ep?%?1024?!=?0) ?????????????{ ?????????????????memset(spi_read_cache,?0XFF,?1024); ?????????????????xmq25qxx_read(spi_flash_addr,?spi_read_cache,?recv_head.ih_ep?%?1024); ? ?????????????????if?(xflash_write(current_flash_write_addr,?spi_read_cache,?1024)) ?????????????????{ ?????????????????????APP_ERR_PRINT("I?write?program?failed."); ?????????????????????goto?bsdiff_out; ?????????????????} ?????????????} ?????????????if?(crc32((uint8_t?*)APPLICATION_A,?recv_head.ih_ep)?!=?recv_head.ih_dcrc) ?????????????{ ?????????????????APP_ERR_PRINT("file?newcrc?err,calcrc:0X%08X,?newcrc:0X%08X,?len:?%lu",? ??????????????????????????????????????????crc32((uint8_t?*)APPLICATION_A,?recv_head.ih_ep),? ??????????????????????????????????????????recv_head.ih_load,?recv_head.ih_dcrc); ?????????????????goto?bsdiff_out; ?????????????} ?????????????/*?下載成功,開始升級(jí)?*/ ?????????????if?(check_bin_file((bin_info_t?*)(APPLICATION_A?+?BIN_INFO_OFFSET)))?/*?bin文件非法?*/ ?????????????{ ?????????????????APP_ERR_PRINT("check_bin_file?err."); ?????????????????goto?bsdiff_out; ?????????????} ?????????????recv_head.ih_dcrc?=?CRT_CRC16_check(0,?(uint8_t?*)APPLICATION_A,? ?????????????????????????????????????????????????recv_head.ih_ep); ?????????????readwrite_app_run_bin_info(0,?&recv_head.ih_ep,?(uint16_t?*)&recv_head.ih_dcrc); ?????????????/*?整體校驗(yàn)成功,確認(rèn)升級(jí)?*/ ?????????????if?(switch_program_stage(STAGE_1)) ?????????????{ ?????????????????APP_ERR_PRINT("I?write?switch_program_stage?STAGE_0?failed.");??/*?置位升級(jí)標(biāo)志寫失敗?*/ ?????????????????goto?bsdiff_out; ?????????????} ?????????????APP_PRINT("upgrade?success."); ???bsdiff_out: ?????????????SYSTEM_RESET(); ?????????}審核編輯:湯梓紅
-
單片機(jī)
+關(guān)注
關(guān)注
6058文章
44821瀏覽量
644710 -
mcu
+關(guān)注
關(guān)注
146文章
17718瀏覽量
358209 -
嵌入式
+關(guān)注
關(guān)注
5125文章
19438瀏覽量
313072 -
STM32
+關(guān)注
關(guān)注
2283文章
10986瀏覽量
361299
原文標(biāo)題:適用于嵌入式的差分升級(jí)通用庫(kù)!
文章出處:【微信號(hào):電子工程師筆記,微信公眾號(hào):電子工程師筆記】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
一種基于單片機(jī)的搶占式實(shí)時(shí)嵌入式操作系統(tǒng)設(shè)計(jì)
單片機(jī)與嵌入式系統(tǒng)的區(qū)別 單片機(jī)和嵌入式linux區(qū)別
到底什么是嵌入式?什么是單片機(jī)?
什么是嵌入式?什么是單片機(jī)?
如何去實(shí)現(xiàn)基于單片機(jī)的差分升級(jí)
一種適用于嵌入式系統(tǒng)的模塊動(dòng)態(tài)加載技術(shù)
一種適用于嵌入式數(shù)據(jù)庫(kù)的新索引機(jī)制
單片機(jī)嵌入式鍵盤接口技術(shù)
什么是嵌入式單片機(jī)?嵌入式單片機(jī)詳情匯總
嵌入式和單片機(jī)的關(guān)系
基于單片機(jī)的差分升級(jí)

單片機(jī)和嵌入式的區(qū)別

自編適用于嵌入式單片機(jī)Json封包與解析的程序

"單片機(jī)差分升級(jí)算法(STM32,M0,M3,M4等芯片都適用)"

基于STM32單片機(jī)的差分升級(jí)(增量升級(jí))算法

評(píng)論