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

参考博客: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 <stdio.h>
#include <stdint.h>
#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 *)&params, 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, &params);
        
        //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 <= sector_max_num; i++) 
    {
        port_trace("erase sector %d.\n", i);
        flash_erase_sector(FLASH_TYPE_INTERNAL, i);
    }
		
	// copy data from the extend flash to inner flash
    port_trace("Copy data Start\n");
    remain_length = bin_length;
    index = 0;
		
    while (1) 
    {
        if (remain_length > 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) <= md5_check_flag_size)) {
        return false;
    }

    end_address -= md5_check_flag_size;
    for (; end_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,操作更方便哦

  • 44
    点赞
  • 324
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: STM32 Bootloader 是一种用于 STM32 微控制器的串口升级工具。它可以通过串口接口将新的固件程序烧录到 STM32 芯片中,从而实现固件升级的功能。使用 STM32 Bootloader 可以方便地进行固件升级,而不需要使用专门的编程器或者烧录器。同时,STM32 Bootloader 还支持多种协议,如 Xmodem、Ymodem、Zmodem 等,可以满足不同的应用需求。 ### 回答2: stm32bootloader是一种在STM32设备上进行串口升级的工具,它允许用户通过串口通信的方式将新的固件文件更新到设备上,同时具有简便、快速的特点,因此受到了许多开发者的欢迎。 使用stm32bootloader进行串口升级需要先准备好开发板和固件文件,然后将开发板通过USB线与电脑相连,并启动stm32bootloader工具。在工具中设置相应的参数,例如串口号、波特率和固件文件路径等,然后将开发板复位,进入bootloader模式后即可开始升级过程。在升级过程中,stm32bootloader会通过串口通信将固件文件发送给设备,并进行验证和存储。升级成功后,开发板将重新启动,新的固件即可生效。 需要注意的是,在进行stm32bootloader串口升级时,要确保开发板与电脑之间的连接稳定,同时要正确设置串口参数,否则可能会导致升级失败。此外,为了保证升级过程的安全性,也应该为固件文件进行签名,并进行验证。总之,stm32bootloader是一种非常便捷的串口升级工具,在开发和生产中都有很广泛的应用。 ### 回答3: STM32Bootloader 是一个在 STM32 微控制器上的固件程序,它特别设计用于在 STM32 芯片内部的 Flash 内存上进行 Bootloader 应用程序的更新升级STM32Bootloader 采用串口升级的方式进行升级,其特点是操作简单,使用方便,且不需要外部的可烧写接口,减少了外部设备成本。 下我们来重点介绍一下 STM32Bootloader 的串口升级流程: 1. 第一步:硬件连接 需要在开发板上串口 USART1 中的 Tx 和 Rx 引脚连接 PC 上相应的 Tx 接收端和 Rx 发送端。 2. 第二步:STM32Bootloader 设置 需要将 STM32Bootloader 自己的应用程序烧写到 STM32 微控制器内部 Flash 中,使得 STM32 芯片能够启动 STM32Bootloader。在设置 STM32Bootloader 前,需要确认 STM32Bootloader 版本,及 USART1 串口的波特率。具体设置方法如下: (1)STM32Bootloader 版本确认方法:将开发板按下 Reset 按键,上位机用串口调试器向开发板发送“V”字符,开发板返回版本号,确认 STM32Bootloader 版本是否正确。 (2)USART1 波特率设置方法:用串口调试器设置比特率为 115200,数据位为 8 位,停止位为 1 位,校验位为无。 3. 第三步:PC 上位机 STM32 Flash 烧录工具设置 PC 上位机通过串口向 STM32 微控制器进行编程,需要使用对应的烧录软件。下STM32F107 开发板为例讲述如何使用 IAP 升级方式升级 STM32 应用程序。 (1)安装并打开 Keil 烧录软件 (2)设置 Debug 和 Flash 烧录方式:Project->Options for Target->Debug->Connectivity:选择 J-LINK/J-TRACE;Project->Options for Target->Utilities->Flash Download:选择 Load image。 (3)设置 Flash 烧录工具:Options->Flash Download->Settings,在“Port”和“Baudrate”选项中与上述 USART1 波特率相同。 (4)将新应用程序下载到烧录工具中,点击“Download” 4. 第四步:升级 STM32 应用程序 (1)在上位机打开对应的 IAP 测试程序,点击“烧录”进入烧录模式。 (2)重启开发板,STM32Bootloader 软件将会自动启动。 (3)使用 IAP 升级方式升级 STM32 应用程序。 以上就是 STM32Bootloader 串口升级的具体内容,该升级方式具有简单、便捷、低成本等优点,所以在实际应用中得到广泛的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值