0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

分享一种适用于嵌入式单片机的差分升级通用库

电子工程师笔记 来源:CSDN-风吹花中花吹风 2023-09-15 09:46 次阅读

1. 什么是差分/增量升级?

借用网上的介绍:适合嵌入式的差分升级又叫增量升级,顾名思义就是通过差分算法将源版本与目标版本之间差异的部分提取出来制作成差分包,然后在设备通过还原算法将差异部分在源版本上进行还原从而升级成目标版本的过程。

差分升级方案不仅可以节省MCU内部的资源空间、还可以节省下载流程及下载和升级过程中的功耗。

也就是说,差分升级是拿以前旧设备内的bin,和当前新版本的bin用某种算法算出他们的差异部分,然后在借助压缩算法,生产一个极其小的差分包,然后将这个差分包下载到设备中,设备在根据解压算法、差分还原算法,生产一个完整的新版本bin,然后将这个新版本bin刷到执行区执行代码。

差分升级一般来说,可以极大的减少下载量,特别是对于嵌入式STM32单片机来说,可以极大的减少维护成本,因为嵌入式设备的升级维护一般都是空中ota升级,比如蓝牙红外等,下载速度受到波特率、包长等限制,更新固件包非常的慢,而差分升级可以让下载的过程极大的缩小。

正常的维护版本,即使改的再多,生成的差分包bin理论上在原bin的5%左右,比如一个300k的bin,改的很多的情况下差分包也不过15k左右,而我实际测试,版本维护平均都在5~10k左右。

2. 差分升级实现原理

差分升级过程:

1、使用旧版本bin文件和新版本bin文件制作差分包

2、将差分包下载到设备内

3、设备使用差分算法还原出新版本bin

4、设备将新版本bin进行crc验证后刷到代码执行区

5、设备重启并以新版本运行

在过程中有2个关键点:

第一:如何使用旧版本bin文件和新版本bin文件制作差分包?

该过程我们使用稳定的开源差分算法bsdiff+lzma生成差分包,该算法被大量使用,稳定安全,并且我们已在项目中批量使用,经过长时间的验证无任何问题。一般来说,该过程都是使用上位机来完成,嵌入式设备无需关心,我们已经做好了上位机软件,可以供大家随意使用,稍后会进行介绍。

第二:设备收到差分包后如何还原出新版本的bin文件?

该过程就是我们要讲解的重点过程,相对应的,嵌入式设备中,我们依然使用开源差分算法bsdiff+lzma来还原新版本文件,代码全开源,并且我已做成了库、抽象出了极简的接口,移植起来费不了多少功夫。

基本是市场上所有的单片机如stm32、瑞萨、华大、复旦微等都可以使用,但是有内存限制,要求ram至少要10k以上,然后是该库本身的消耗大概是5k的rom

3. 关键点一:差分包制作过程

对于差分包的制作,我已经开发好了上位机软件,界面如下图所示:

fe15f436-52f6-11ee-a25d-92fbcf53809c.png

上位机这边主要实现使用开源算法bsdiff制作旧版本bin和新版本bin的差分包,然后在使用lzma压缩算法来压缩差分包,最终生成一个差分bin,使用方法上位机界面提示的很清楚,最终效果如下图所示:

fe65ae18-52f6-11ee-a25d-92fbcf53809c.png

4. 关键点二:嵌入式设备中差分算法库的移植(还原差分包)

4.1. 移植开关算法库代码

代码已开源,地址:

https://gitee.com/qq791314247/mcu_bsdiff_upgrade

整体代码如下图所示:

feb4e30c-52f6-11ee-a25d-92fbcf53809c.png

如上图所示,99%的代码用户都不用去关心,用户只需要提供一个flash写入接口即可,也就是该库给定用户flash地址、数据内容指针、数据内容长度,用户将该段数据写入到flash即可,移植起来特别简单,花不了几分钟的功夫,这也是我花大力气抽象接口的原因。

4.2. 使用该库的流程

4.2.1. 使用库的接口

对于整个库的代码,我们只需要关心一个接口iap_patch,iap_patch在文件”user_interface.h”中。

ff01d32e-52f6-11ee-a25d-92fbcf53809c.png

该接口介绍也比较清晰,差分包的还原,只需要调用这一个接口即可。

/**
*@brief用户使用差分升级时唯一需要关心的接口
*
*@paramold设备中执行区代码所在的地址,用户可指定flash执行区的地址,方便算法读出来当前
*运行中的代码
*@paramoldsize设备中执行区代码的长度,用户可在差分包bin头获取
*@parampatch设备中已经下载的差分包所在的flash地址,或者ram地址,只要能让算法读出来即可
*注意,下载的差分包自带image_header_t格式的文件头,真正的差分包需要偏
*移sizeof(image_header_t)的长度
*@parampatchsize设备中已经下载的差分包的长度,用户可在差分包bin头获取
*@paramnewfile新文件的大小,用户需填入新版本bin的长度,用户亦可以差分包bin头获取
*@returnint然后错误码,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;/*ImageHeaderMagicNumber*/
uint32_tih_hcrc;/*ImageHeaderCRCChecksum差分包包头校验*/
uint32_tih_time;/*ImageCreationTimestamp*/
uint32_tih_size;/*ImageDataSize差分包的大小*/
uint32_tih_load;/*DataLoadAddress上一版本旧文件的大小*/
uint32_tih_ep;/*EntryPointAddress要升级的新文件的大小*/
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;
/*差分包制作时自带的文件头信息,用户只需要关心中文注释的部分*/

4.2.2. 接口使用例子

我截取一段我工程中的代码来讲解如何使用该接口还原出新版本bin文件:

#ifdefBSDIFF_UPGRADE
image_header_trecv_head;
uint32_trecv_hcrc;/*接收到的文件头CRC*/
uint32_tcalculation_crc;/*计算出来的文件头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);
/*差分升级包*/
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;
             }
             /* 下载成功,开始升级 */
             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);
             /* 整体校验成功,确认升级 */
             if (switch_program_stage(STAGE_1))
             {
                 APP_ERR_PRINT("I write switch_program_stage STAGE_0 failed.");  /* 置位升级标志写失败 */
                 goto bsdiff_out;
             }
             APP_PRINT("upgrade success.");
   bsdiff_out:
             SYSTEM_RESET();
         }
审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6032

    文章

    44513

    浏览量

    632743
  • mcu
    mcu
    +关注

    关注

    146

    文章

    16982

    浏览量

    350221
  • 嵌入式
    +关注

    关注

    5068

    文章

    19009

    浏览量

    303052
  • STM32
    +关注

    关注

    2265

    文章

    10870

    浏览量

    354716

原文标题:适用于嵌入式的差分升级通用库!

文章出处:【微信号:电子工程师笔记,微信公众号:电子工程师笔记】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    一种基于单片机的抢占实时嵌入式操作系统设计

    一种基于单片机的抢占实时嵌入式操作系统设计
    发表于 04-22 12:59

    单片机嵌入式系统的区别 单片机嵌入式linux区别

    `在如今高速发展的社会下,电子信息技术的更新迭代相当迅速,想要不被社会淘汰,就要不断更新自己的技术水平。本文简单分析了单片机嵌入式系统的联系、组成结构对比等基础知识,并列举了几种适用于PIC18F
    发表于 03-16 16:22

    到底什么是嵌入式?什么是单片机

    系统、定时器/计数器等功能,塞进块硅片上,变成个超小型的计算机。这么说来,单片机不就是嵌入式系统?别急,我们往下看。“
    发表于 05-08 10:35

    什么是嵌入式?什么是单片机

    、定时器/计数器等功能,塞进块硅片上,变成个超小型的计算机。这么说来,单片机不就是嵌入式系统?别急,我们往下看。“
    发表于 05-19 14:25

    如何去实现基于单片机分升级

    分算法有开源的bsdiff,压缩算法也有开源的单片机就能用的,把这两个结合起来放单片机里就可以实现分升级。由于上位
    发表于 11-19 06:14

    一种适用于嵌入式系统的模块动态加载技术

    嵌入式系统中的模块动态加载技术摘要提出一种适用于嵌入式系统的模块动态加载技术,设计实现简单,占用资源少,开销小,并且成功运用于DeltaOS
    发表于 12-20 06:32

    一种适用于嵌入式数据的新索引机制

    由于嵌入式系统具有内存资源极为有限和处理器速度不高等特点,因此适用于嵌入式数据的索引机制应在尽量减少内存占用量的基础上进步提高数据操作的
    发表于 12-22 10:57 23次下载

    单片机嵌入式键盘接口技术

    本文介绍一种超小尺寸、防水措施良好的单片机嵌入式键盘,作为PC/104CPU模块专用输入设备。其工作原理不仅适用于键盘输入系统,同时也适用于
    发表于 03-25 11:22 16次下载

    什么是嵌入式单片机?嵌入式单片机详情汇总

    嵌入式单片机,即嵌入式微控制器,指以微控制器为核心控制单元的嵌入到对象体系中的专用计算机系统,是应用十分广泛的一种
    发表于 11-13 09:39 6061次阅读

    嵌入式单片机的关系

    单片机是众多嵌入式处理器的一种,目前通用的理解是,嵌入式主要是指ARM\DSP等处理器.而嵌入式
    发表于 10-06 09:59 6168次阅读

    基于单片机分升级

    分算法有开源的bsdiff,压缩算法也有开源的单片机就能用的,把这两个结合起来放单片机里就可以实现分升级。由于上位
    发表于 11-12 21:06 11次下载
    基于<b class='flag-5'>单片机</b>的<b class='flag-5'>差</b><b class='flag-5'>分升级</b>

    单片机嵌入式的区别

    ,价格低,应用领域大多为小家电,终端设备。 嵌入式片上资源丰富,价格高,应用领域广泛,基本可以适用于任何领域。开发模式 单片机般都是裸机开发,程序规模较小,只有在比较高端的芯
    发表于 11-15 12:36 15次下载
    <b class='flag-5'>单片机</b>和<b class='flag-5'>嵌入式</b>的区别

    自编适用于嵌入式单片机Json封包与解析的程序

    @[TOC]**自编适用于嵌入式单片机Json封包与解析的程序**说明:由于网上提供的标准JSON,对向单片机这类的小设备占用资源过多,很
    发表于 12-05 16:51 17次下载
    自编<b class='flag-5'>适用于</b><b class='flag-5'>嵌入式</b><b class='flag-5'>单片机</b>Json封包与解析的程序

    "单片机分升级算法(STM32,M0,M3,M4等芯片都适用)"

    "单片机分升级算法(STM32,M0,M3,M4等芯片都适用)"
    发表于 12-07 14:06 24次下载
    "<b class='flag-5'>单片机</b><b class='flag-5'>差</b><b class='flag-5'>分升级</b>算法(STM32,M0,M3,M4等芯片都<b class='flag-5'>适用</b>)"

    基于STM32单片机分升级(增量升级)算法

    DiffIAP应用背景随着目前物联网,车联网,智能设备的增多,需要远程升级设备程序的场景增多,以往的IAP升级和OTA升级都是整包升级,bin文件过大导致
    发表于 12-09 15:51 19次下载
    基于STM32<b class='flag-5'>单片机</b>的<b class='flag-5'>差</b><b class='flag-5'>分升级</b>(增量<b class='flag-5'>升级</b>)算法