【STM32】BootLoader介绍、编写 以及 OTA常见方案分析(差分升级 全量升级 AB面升级)

您所在的位置:网站首页 stm32分散加载文件 【STM32】BootLoader介绍、编写 以及 OTA常见方案分析(差分升级 全量升级 AB面升级)

【STM32】BootLoader介绍、编写 以及 OTA常见方案分析(差分升级 全量升级 AB面升级)

2023-10-08 05:25| 来源: 网络整理| 查看: 265

参考博客:STM32 BootLoader升级固件_cyang's blog-CSDN博客

STM32固件升级详解(BootLoader)_EmbeddedOsprey-CSDN博客_stm32升级bootloader

一、关于BootLoader 1、BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,也就是单片机选择性的自己给自己下程序。可以更新,也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行。2、BootLoader更新完程序后并不擦除自己,下次启动后依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新。3、在实际的单片机工程项目中,如果加入了BootLoader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新。当然,这就需要创建两个工程项目,一个为BootLoader工程,一个为APP工程。4、BootLoader工程生成的.hex或者.bin文件通常下载到ROM或Flash中的首地址,这样可以保证上电后先运行BootLoader程序。而APP工程生成的.hex或者.bin文件则下载到ROM或Flash中BootLoader后面的地址中。也就是说,存在ROM/Flash中的内容是分为两部分的。5、要实现在同一个ROM/Flash中保存两段程序,并且保证不能相互覆盖,则需要在下载程序时指定地址。如在Keil下,可以进行如下的调整

 (版权声明:本文为CSDN博主「cyang812」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u011303443/article/details/53378602)

二、OTA升级方式:(涉及到BootLoader的编写) 1.全量升级:

完整的下载新版本固件,下载完成后将固件搬运到APP程序运行的位置。(一般来说是将APP从片外flash搬运到片内flash上)。搬运完成后校验通过后重启APP。

2.差分升级: 利用算法,做出原版APP和新版APP程序的差分包,将差分包下载到flash,内部的BootLoader程序在利用算法将新版APP合成,合成后在搬运,搬运后校验,重启。 一般制作出来的差分包只有原包的5%左右。省空间!

服务器端: 生成差分包,bsdiff算法

1.对old文件中所有子字符串形成一个字典;

2.对比old文件和new文件,产生diff string        和extra string;

3.将diff string 和extra string 以及相应的控制        字用zip压缩成一个patch包。    

设备端: 生成new File,bspatch算法

1.接收patch包;

2.解压patch包;

3.还原new文件。

3.原地升级:

相比差分升级,合包的过程,直接搬运。

容错率低。

4.AB面升级:

如图所示:

A/B 系统更新可带来以下好处:

OTA 更新可以在系统运行期间进行,而不会打断用户。用户可以在 OTA 期间继续使用其设备。在更新期间,唯一的一次宕机发生在设备重新启动到更新后的磁盘分区时。更新后,重新启动所用的时间不会超过常规重新启动所用的时间。如果 OTA 无法应用(例如,因为刷机失败),用户将不会受到影响。用户将继续运行旧的操作系统,并且客户端可以重新尝试进行更新。如果 OTA 更新已应用但无法启动,设备将重新启动回旧分区,并且仍然可以使用。客户端可以重新尝试进行更新。任何错误(例如 I/O 错误)都只会影响未使用的分区组,并且用户可以进行重试。由于 I/O 负载被特意控制在较低水平,以免影响用户体验,因此发生此类错误的可能性也会降低。 更新包可以流式传输到 A/B 设备,因此在安装之前不需要先下载更新包。流式更新意味着用户没有必要在 /data 或 /cache 上留出足够的可用空间来存储更新包。缓存分区不再用于存储 OTA 更新包,因此无需确保缓存分区的大小要足以应对日后的更新。dm-verity 可保证设备将使用未损坏的启动映像。如果设备因 OTA 错误或 dm-verity问题而无法启动,则可以重新启动到旧映像。(Android 验证启动不需要 A/B 更新。)

几种OTA方式的对比:

优点

缺点

差分升级

1.差分包小(5%),下载更快,节省OTA流程的时间。

1.一个差分包只能由特定的原包升级到特定的新包。2.保证基础包的一致性。若原包数据损毁,得到差分包也无法升级。

全量升级

1.拿到新的包就能升级。不需要指定原包。2.不需保证基础包的一致性。

1.全量包的size更大,下载相比差分包需要更长时间。

AB面升级

1.容错率高,更能避免固件升级出错

2.OTA过程中不影响用户APP程序的运行

3.下载完成后不需要搬运,不需要差分包还原

1.需要最多的flash空间的OTA方式。

原地升级

1.最节省flash空间的方式,不需要合包存储,直接根据差分包搬运到APP的位置

2.容错率低,若搬运过程中断电,则设备变砖

关于差分升级的缺点,如图所示,你懂得(懒得打字了......)

三、编写BootLoader

1.外设驱动-----CubeMX工具生成代码:GPIO uart(debug uart) SPI flash

2.md5库添加 、w25q80驱动文件添加

3.Boot_Start()函数

bootloader.h 

#ifndef BOOTLOADER_H #define BOOTLOADER_H #include #include #include "flash.h" #define MD5_CHECK_MSG_FLAG "firmware-header" #pragma pack(1) typedef struct { uint8_t project_id; uint16_t vendor_id; uint16_t software_version; uint16_t hardware_version; uint32_t buildtime; uint32_t filesize; uint8_t md5[16]; } mcu_update_info_t; #pragma pack() #define APP_START_ADDRESS (0x08008000) //APP main //map ResetHandle :0800825d //0x08008000 #define MCU_OTA_FLAG (0xA8A8A8A8) //printf boot: void boot_message_print(); //boot_start : main调用这个函数 void boot_start(void); //check bin file void check_bin_file(void); //update bin file uint8_t update_bin_file(uint32_t addr, HDC01_ota_info_t *params); //go to main void go_to_app_main(uint32_t address); //APP_START_ADDRESS 实参 // bool firmware_verify(uint32_t start_address, uint32_t end_address);

bootloader.c

#include "bootloader.h" #include "stm32f4xx_hal.h" #include "flash.h" #include "lib_md5.h" char version[] = {"V00.00"}; typedef void (*pFunction)(void); pFunction JumpToApplication; void go_to_app_main(uint32_t address) //APP_START_ADDRESS 实参 { __IO uint32_t fun; fun = (*(__IO uint32_t *)APP_START_ADDRESS); // check the Vactor Table Head // 检查栈顶地址是否合法 if ((fun & 0x2FFE0000) == 0x20000000) { fun = *(__IO uint32_t *)(APP_START_ADDRESS + 4); JumpToApplication = (pFunction)fun; // set Vactor Table address __set_MSP(*(__IO uint32_t *)APP_START_ADDRESS); printf("Set Vactor Table address ...\n"); // start Jumping and go to App_main printf("Real Jump and go to App_main...\r\n\r\n"); //关闭 总中断 __disable_irq(); JumpToApplication(); } else { // app file error printf("App Bin file size err 0x%8X...\r\n", fun); // go to start HAL_NVIC_SystemReset(); } } //printf boot: void boot_message_print(void ) { printf("Start Bootloader,version = %s...\n",version); } //boot_start : main use void boot_start(void) { boot_message_print(); check_bin_file(); go_to_app_main(APP_START_ADDRESS); } //check bin file / updata bin void check_bin_file(void) { HDC01_ota_info_t params; //1. read firmware upadte flag flash_read(FLASH_TYPE_EXTERNAL,(uint8_t *)¶ms, HDC01_MCU_OTA_INFO_START_ADDR,sizeof(HDC01_ota_info_t)); //2.check the bin file exception flag if (params.magic == MCU_OTA_FLAG) { // Need update port_trace("We need update the new firmware. len:%d\n", params.firmware_len); // update the bin file update_bin_file(HDC01_MCU_OTA_DATA_START_ADDR, ¶ms); //erase OTA flag flash_erase_sector(FLASH_TYPE_EXTERNAL, HDC01_MCU_OTA_INFO_SECTOR); } else { port_trace("Normal firmware start...\n"); } } //update the bin file uint8_t buff[2048]; uint8_t check[2048]; uint8_t update_bin_file(uint32_t addr, HDC01_ota_info_t *params) { bool ret; uint32_t remain_length = 0, write_num; uint32_t index = 0; uint32_t i = 0; uint32_t bin_length = 0; bin_length = params->firmware_len; if (bin_length > (224 * 1024)) { // error of bin file length port_trace("Read Firmware length err: 0x%8X\n", bin_length); return 0; } // erase all code segment uint16_t sector_max_num = GetMaxSectorNum(bin_length); for (i = 2; i 2048) write_num = 2048; else write_num = remain_length; flash_read(FLASH_TYPE_EXTERNAL, buff, HDC01_MCU_OTA_DATA_START_ADDR + index, write_num); flash_write(FLASH_TYPE_INTERNAL, buff, APP_START_ADDRESS + index, write_num); // 写入后立刻读出,并与外部Flash中的数据做校验。 flash_read(FLASH_TYPE_INTERNAL, check, APP_START_ADDRESS + index, write_num); for (i = 0; i < write_num; i++) { if (buff[i] != check[i]) { // 一旦出现问题,随后再次重启。 HAL_NVIC_SystemReset(); } } index = index + write_num; remain_length = remain_length - write_num; if (remain_length == 0) break; } //ret = firmware_verify(APP_START_ADDRESS, APP_START_ADDRESS + params->firmware_len); // if (ret == false) // { // port_trace("md5 check fail.\n"); // // 一旦出现问题,随后再次重启。 // HAL_NVIC_SystemReset(); // } // else // { // port_trace("md5 check success.\n"); // } port_trace("Copy data finish.\n"); return 1; } /* check app validity */ static uint32_t boot_timeout_starting_time = 0; /* ------------------------------------------------------------------------ */ /* @Description: check app validity * @parameters: start_address: app start addr end_address: app end addr */ /* ------------------------------------------------------------------------ */ bool firmware_verify(uint32_t start_address, uint32_t end_address) { uint8_t md5_check_flag_size = sizeof(MD5_CHECK_MSG_FLAG) - 1; if ((start_address >= end_address) || ((end_address - start_address) start_address; end_address--) { if (memcmp(MD5_CHECK_MSG_FLAG, (uint8_t *)end_address, md5_check_flag_size) == 0) { mcu_update_info_t *md5_and_size = (mcu_update_info_t *)(end_address + md5_check_flag_size); if (md5_and_size->filesize == (end_address - start_address)) { MD5_CTX md5; uint8_t md5_value[16] = {0}; port_trace("......APP_buildtime-->%d\n", md5_and_size->buildtime); port_trace("......firmware_size-->%d\n", md5_and_size->filesize); port_trace("find_md5-->"); //port_dump(md5_and_size->md5, 16); MD5Init(&md5); MD5Update(&md5, (uint8_t *)start_address, md5_and_size->filesize); MD5Final(md5_value, &md5); port_trace("flash_md5-->"); //port_dump(md5_value, 16); if (memcmp(md5_value, md5_and_size->md5, 16) == 0) { return true; } } } } port_trace("......No_find_APP_flag-->error\n"); return false; }

完整工程:CSDN下载:stm32f407-BootLoader程序_stm32f407bootloader跳转-嵌入式文档类资源-CSDN下载

百度云网盘:链接:https://pan.baidu.com/s/1dwrG1nA5voIhGBAimJgKDg  提取码:yls4  复制这段内容后打开百度网盘手机App,操作更方便哦



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3