
文章目录1. 什么是差分/增量升级2. 差分升级实现原理3. 关键点一差分包制作过程4. 关键点二嵌入式设备中差分算法库的移植(还原差分包)4.1. 移植开关算法库代码4.2. 使用该库的流程4.2.1. 使用库的接口4.2.2. 接口使用例子代码已开源地址gitee:https://gitee.com/qq791314247/mcu_bsdiff_upgradegithub:https://github.com/791314247/mcu_bsdiff_upgrade20261030 持续更新中代码库有更新2.0版本接口做了优化有问题可以直接加v讨论下面上位机图片中有。上位机也更新了界面。1. 什么是差分/增量升级借用网上的介绍适合嵌入式的差分升级又叫增量升级顾名思义就是通过差分算法将源版本与目标版本之间差异的部分提取出来制作成差分包然后在设备通过还原算法将差异部分在源版本上进行还原从而升级成目标版本的过程。 差分升级方案不仅可以节省MCU内部的资源空间、还可以节省下载流程及下载和升级过程中的功耗。也就是说差分升级是拿以前旧设备内的bin和当前新版本的bin用某种算法算出他们的差异部分然后在借助压缩算法生产一个极其小的差分包然后将这个差分包下载到设备中设备在根据解压算法、差分还原算法生产一个完整的新版本bin然后将这个新版本bin刷到执行区执行代码。差分升级一般来说可以极大的减少下载量特别是对于嵌入式STM32等单片机来说可以极大的减少维护成本因为嵌入式设备的升级维护一般都是空中ota升级比如蓝牙、红外等下载速度受到波特率、包长等限制更新固件包非常的慢而差分升级可以让下载的过程极大的缩小。正常的维护版本即使改的再多生成的差分包bin理论上在原bin的5%左右比如一个300k的bin改的很多的情况下差分包也不过15k左右而我实际测试版本维护平均都在5~10k左右。2. 差分升级实现原理差分升级过程使用旧版本bin文件和新版本bin文件制作差分包将差分包下载到设备内设备使用差分算法还原出新版本bin设备将新版本bin进行crc验证后刷到代码执行区设备重启并以新版本运行在过程中有2个关键点第一如何使用旧版本bin文件和新版本bin文件制作差分包该过程我们使用稳定的开源差分算法bsdifflzma生成差分包该算法被大量使用稳定安全并且我们已在项目中批量使用经过长时间的验证无任何问题。一般来说该过程都是使用上位机来完成嵌入式设备无需关心我们已经做好了上位机软件可以供大家随意使用稍后会进行介绍。第二设备收到差分包后如何还原出新版本的bin****文件 该过程就是我们要讲解的重点过程相对应的嵌入式设备中我们依然使用开源差分算法bsdifflzma来还原新版本文件代码全开源并且我已做成了库、抽象出了极简的接口移植起来费不了多少功夫基本是市场上所有的单片机如stm32、瑞萨、华大、复旦微等都可以使用但是有内存限制要求ram至少要10k以上然后是该库本身的消耗大概是5k的rom。3. 关键点一差分包制作过程对于差分包的制作我已经开发好了上位机软件界面如下图所示上位机这边主要实现使用开源算法bsdiff制作旧版本bin和新版本bin的差分包然后在使用lzma压缩算法来压缩差分包最终生成一个差分bin使用方法上位机界面提示的很清楚最终效果如下图所示下载链接链接https://pan.baidu.com/s/1nBpftim8OCUI3i32sP3eOA?pwddiff提取码diffPS上面是旧版的上位机有界面更新下载地址点击下载新版上位机4. 关键点二嵌入式设备中差分算法库的移植(还原差分包)4.1. 移植开关算法库代码代码已开源地址gitee:https://gitee.com/qq791314247/mcu_bsdiff_upgradegithub:https://github.com/791314247/mcu_bsdiff_upgrade整体代码如下图所示如上图所示99%的代码用户都不用去关心用户只需要提供一个flash写入接口即可也就是该库给定用户flash地址、数据内容指针、数据内容长度用户将该段数据写入到flash即可移植起来特别简单花不了几分钟的功夫这也是我花大力气抽象接口的原因。4.2. 使用该库的流程4.2.1. 使用库的接口对于整个库的代码我们只需要关心一个接口iap_patchiap_patch在文件”user_interface.h”中。该接口介绍也比较清晰差分包的还原只需要调用这一个接口即可。/** * brief 用户使用差分升级时唯一需要关心的接口 * * param old 设备中执行区代码所在的地址用户可指定flash执行区的地址方便算法读出来当前 * 运行中的代码 * param oldsize 设备中执行区代码的长度用户可在差分包bin头获取 * param patch 设备中已经下载的差分包所在的flash地址或者ram地址只要能让算法读出来即可 * 注意下载的差分包自带image_header_t格式的文件头真正的差分包需要偏 * 移sizeof(image_header_t)的长度 * param patchsize 设备中已经下载的差分包的长度用户可在差分包bin头获取 * param newfile 新文件的大小用户需填入新版本bin的长度用户亦可以差分包bin头获取 * return int 然后错误码0成功1失败 */externintiap_patch(constuint8_t*old,uint32_toldsize,constuint8_t*patch,uint32_tpatchsize,uint32_tnewfile);另外使用该接口还原时所需要的一些信息可以在差分包文件头中获取上位机在制作差分包时会自动在差分包的bin头加上64字节的文件头以便于告诉嵌入式设备旧/新版本bin文件的CRC校验、长度等信息。所以用户在收到差分包头时偏移掉这64个字节的文件头的地址才是需要给到iap_patch接口的真正的bin文件。文件头格式如下代码用户只需要关心中文注释的部分其余的都是预留的信息。/* 差分包制作时自带的文件头信息用户只需要关心中文注释的部分 */typedefstructimage_header{uint32_tih_magic;/* Image Header Magic Number */uint32_tih_hcrc;/* Image Header CRC Checksum 差分包包头校验 */uint32_tih_time;/* Image Creation Timestamp */uint32_tih_size;/* Image Data Size 差分包的大小 */uint32_tih_load;/* Data Load Address 上一版本旧文件的大小 */uint32_tih_ep;/* Entry Point Address 要升级的新文件的大小 */uint32_tih_dcrc;/* Image Data CRC Checksum 新文件的CRC */uint8_tih_os;/* Operating System */uint8_tih_arch;/* CPU architecture */uint8_tih_type;/* Image Type */uint8_tih_comp;/* Compression Type */uint8_tih_name[IH_NMLEN];/* Image Name */uint32_tih_ocrc;/* Old Image Data CRC Checksum 上一版本旧文件的CRC */}image_header_t;/* 差分包制作时自带的文件头信息用户只需要关心中文注释的部分 */4.2.2. 接口使用例子我截取一段我工程中的代码来讲解如何使用该接口还原出新版本bin文件1#ifdef BSDIFF_UPGRADE2image_header_trecv_head;3uint32_trecv_hcrc;/* 接收到的文件头CRC */4uint32_tcalculation_crc;/* 计算出来的文件头CRC */5uint32_tspi_flash_addrUPGRADE_PROGRAM_ADDR;67memcpy(recv_head,(uint8_t*)APPLICATION_A,sizeof(image_header_t));8recv_hcrcBigtoLittle32(recv_head.ih_hcrc);9recv_head.ih_hcrc0;10calculation_crccrc32((uint8_t*)recv_head,sizeof(image_header_t));1112if(recv_hcrccalculation_crc)13{14recv_head.ih_hcrcrecv_hcrc;15recv_head.ih_timeBigtoLittle32(recv_head.ih_time);16recv_head.ih_sizeBigtoLittle32(recv_head.ih_size);17recv_head.ih_dcrcBigtoLittle32(recv_head.ih_dcrc);18recv_head.ih_ocrcBigtoLittle32(recv_head.ih_ocrc);19/* 差分升级包 */20recv_head.ih_hcrccalculation_crc;21if(crc32((uint8_t*)APPLICATION_RUN,recv_head.ih_load)!recv_head.ih_ocrc)22{23APP_ERR_PRINT(file oldcrc err,calcrc:0X%08X, ih_oldbin_crc:0X%08X,,24crc32((uint8_t*)APPLICATION_RUN,25recv_head.ih_load),recv_head.ih_ocrc);26gotobsdiff_out;27}28RTOS_LOCK();29disable_task_monitoring(ALL_TASK_RUNFLAG_BIT,true);30// flash_erase_sector(UPGRADE_PROGRAM_ADDR, UPGRADE_PROGRAM_PAGE);31recv_hcrciap_patch((uint8_t*)APPLICATION_RUN,recv_head.ih_load,32(uint8_t*)(APPLICATION_Asizeof(image_header_t)),33recv_head.ih_size,UPGRADE_PROGRAM_ADDR);34if(recv_hcrc!recv_head.ih_ep)35{36APP_ERR_PRINT(iap_patch len err.);37APP_ERR_PRINT(iap_patch len: %lu, new_len: %lu,recv_hcrc,recv_head.ih_ep);38gotobsdiff_out;39}40if(erase_program(APPLICATION_A))41{42APP_ERR_PRINT(I erase program failed.);43gotobsdiff_out;44}4546current_flash_write_addrAPPLICATION_A;47for(uint32_ti0;i(recv_head.ih_ep/1024);i)48{49xmq25qxx_read(spi_flash_addr,spi_read_cache,1024);50if(xflash_write(current_flash_write_addr,spi_read_cache,1024))51{52APP_ERR_PRINT(I write program failed.);53gotobsdiff_out;54}55spi_flash_addr1024;56current_flash_write_addr1024;57APP_PRINT(current_flash_write_addr: 0X%08X,current_flash_write_addr);58}59if(recv_head.ih_ep%1024!0)60{61memset(spi_read_cache,0XFF,1024);62xmq25qxx_read(spi_flash_addr,spi_read_cache,recv_head.ih_ep%1024);6364if(xflash_write(current_flash_write_addr,spi_read_cache,1024))65{66APP_ERR_PRINT(I write program failed.);67gotobsdiff_out;68}69}70if(crc32((uint8_t*)APPLICATION_A,recv_head.ih_ep)!recv_head.ih_dcrc)71{72APP_ERR_PRINT(file newcrc err,calcrc:0X%08X, newcrc:0X%08X, len: %lu,73crc32((uint8_t*)APPLICATION_A,recv_head.ih_ep),74recv_head.ih_load,recv_head.ih_dcrc);75gotobsdiff_out;76}77/* 下载成功,开始升级 */78if(check_bin_file((bin_info_t*)(APPLICATION_ABIN_INFO_OFFSET)))/* bin文件非法 */79{80APP_ERR_PRINT(check_bin_file err.);81gotobsdiff_out;82}83recv_head.ih_dcrcCRT_CRC16_check(0,(uint8_t*)APPLICATION_A,84recv_head.ih_ep);85readwrite_app_run_bin_info(0,recv_head.ih_ep,(uint16_t*)recv_head.ih_dcrc);86/* 整体校验成功,确认升级 */87if(switch_program_stage(STAGE_1))88{89APP_ERR_PRINT(I write switch_program_stage STAGE_0 failed.);/* 置位升级标志写失败 */90gotobsdiff_out;91}92APP_PRINT(upgrade success.);93bsdiff_out:94SYSTEM_RESET();95}