2.10. 双备份升级介绍

杰理双备份升级(Dual-Bank Update),核心是使能双备份功能、工具生成正确格式固件、通过自定义OTA接收并写入备用分区、校验成功后切换启动。全程不影响当前运行、升级失败可回退

2.10.1. 原理(快速理解)

  • Flash 划分 UBOOT 区域, APP_CPDE 区域(包含code和资源),预留区域(包含VM、自定义预留区等)

  • 双备份使能后 APP_CODE 区域将划分为 APP_CODE0 / APP_CODE1 两个等大分区

  • 运行 A 区 → 升级包写入 B 区;校验通过 → 切换启动标志 → 重启进 B 区

  • 中断 / 失败:仍从原 A 区启动,不会变砖

2.10.2. 升级流程

  • 和上位机通过串口/SPI/BT/WIFI等连接

  • 上位机与小机通信,把 db_update_data.bin 传输到小机端

  • 固件自动写入非当前运行分区

  • 写入 + 校验成功 → 置升级标志 → 自动 / 手动重启

  • 重启 → Bootloader 检查标志 → 从新分区启动

2.10.3. 开发端:启用双备份(SDK 配置)

使能双备份升级,ini文件会自动配置输出 db_update_data.bin,该文件即为需要升级的文件
升级流程可参考 dual_bank_demo.c 文件
#define TCFG_DUAL_BANK_ENABLE                               ENABLE_THIS_MOUDLE //双备份升级使能

2.10.4. 升级相关的API介绍

2.10.4.1. dual_bank_passive_update_init

  • 函数原型:

    u32 dual_bank_passive_update_init(u32 fw_crc, u32 fw_size, u16 max_pkt_len, void *priv);
    
  • 功能说明:升级初始化,并设置新固件的CRC校验值和文件总大小;

  • 参数说明:

    参数

    说明

    fw_crc

    新固件文件的CRC校验值(用于升级完成后校验固件完整性) - 可传0

    fw_size

    新固件文件的总大小(字节数)

    max_pkt_len

    单次烧写支持的最大数据包长度,决定每次烧写的最大数据量,最大256byte

    priv

    保留,直接传 NULL

  • 返回值:

    返回值

    说明

    0

    成功

    非0

    失败

  • 示例:

    1u16 code_crc = 0x55aa;//升级文件CRC,通过对db_update_data.bin获取到(与上位机通信得到)
    2u32 code_len = 0x8000;//升级文件大小,通过计算db_update_data.bin的长度得到(与上位机通信得到)
    3u32 ret = dual_bank_passive_update_init(code_crc, code_len, 256, NULL);
    

2.10.4.2. dual_bank_update_allow_check

  • 函数原型:

    u32 dual_bank_update_allow_check(u32 fw_size);
    
  • 功能说明:判断Flash是否有足够空间存放新固件文件;

  • 参数说明:

    参数

    说明

    fw_size

    新固件文件的大小(字节数)

  • 返回值:

    返回值

    说明

    0

    正确:空间足够,可以升级

    非0

    错误:空间不足,无法升级

  • note: 必须在调用 dual_bank_passive_update_init(…) 之后使用

  • 示例:

    1u32 code_len = 0x8000;//升级文件大小,通过计算db_update_data.bin的长度得到(与上位机通信得到)
    2u32 ret = dual_bank_update_allow_check(code_crc, code_len, 256, NULL);
    

2.10.4.3. dual_bank_passive_update_exit

  • 函数原型:

    u32 dual_bank_passive_update_exit(void *priv);
    
  • 功能说明:终止当前的双备份升级流程,释放资源;

  • 参数说明:

    参数

    说明

    priv

    保留参数,传 NULL 即可

  • 返回值:

    返回值

    说明

    0

    成功

    非0

    失败

  • 示例:

    1dual_bank_passive_update_exit(NULL);
    

2.10.4.4. dual_bank_update_write

  • 函数原型:

    u32 dual_bank_update_write(void *data, u16 len, int (*write_complete_cb)(void *priv));
    
  • 功能说明:把缓冲区数据写入非易失性存储(Flash)并可通过回调函数告知应用层写入成功or失败

  • 参数说明:

    参数

    说明

    data

    下载数据的指针(就是你通过上位机收到的升级包数据缓存)

    len

    本次要写入的数据长度(字节数),必须 ≤ 初始化时的 max_pkt_len

    write_complete_cb

    本次烧写完成回调函数,执行成功 priv = 0,执行失败 priv != 0;非必要可传NULL

  • 返回值:

    返回值

    说明

    0

    写入成功

    非0

    写入失败

  • 示例:

    1// 示例:收到一包数据,调用写入
    2u8 buf[256];        // 接收数据缓存
    3u16 rx_len = 256;   // 实际收到长度
    4// 写入升级数据(回调传 NULL 也可以)
    5u32 ret = dual_bank_update_write(buf, rx_len, NULL);
    

2.10.4.5. dual_bank_update_verify

  • 函数原型:

    u32 dual_bank_update_verify( void (*crc_init_hdl)(void), u32(*crc_calc_hdl)(u32 init_crc, const void *data, u32 len), int (*verify_result_hdl)(int calc_crc));
    
  • 功能说明:计算所有已经烧写的固件数据的CRC值,并与升级初始化时传入的CRC值进行对比校验

  • 参数说明:

    参数

    说明

    crc_init_hdl

    如果传NULL,使用内部默认CRC算法(CRC16-CCITT标准);传自定义函数则使用用户CRC初始化

    crc_calc_hdl

    如果传NULL,使用内部默认CRC计算;传自定义函数则使用用户CRC计算

    verify_result_hdl

    校验完成的结果回调; calc_crc = 1:校验成功; calc_crc = 0:校验失败;

  • 返回值:

    返回值

    说明

    0

    校验成功

    非0

    校验失败

  • 示例:

     1// 1. 先定义校验结果回调函数
     2static int dual_verify_result_hdl(int calc_crc)
     3{
     4    if(calc_crc == 1){
     5        // CRC 校验成功 → 可以进行下一步:升级确认 + 重启
     6        printf("固件校验成功\n");
     7    }else{
     8        // 校验失败 → 升级作废,不切换分区,旧系统继续用
     9        printf("固件校验失败\n");
    10    }
    11    return 0;
    12}
    13
    14// 2. 调用校验函数(全部用内部默认CRC,最省心)
    15dual_bank_update_verify(NULL, NULL, dual_verify_result_hdl);
    

2.10.4.6. dual_bank_update_burn_boot_info

  • 函数原型:

    u32 dual_bank_update_burn_boot_info(int (*burn_boot_info_result_hdl)(int err));
    
  • 功能说明:新固件校验成功后,调用该接口烧写新的启动引导信息(作用:告诉芯片下次重启从新固件分区启动)

  • 参数说明:

    参数

    说明

    burn_boot_info_result_hdl

    烧写启动信息结果回调 err = 0:烧写成功 err ≠ 0:烧写失败

  • 返回值:

    返回值

    说明

    0

    烧写启动信息成功

    非0

    烧写启动信息失败

  • 示例:

     1// 1. 定义启动信息烧写结果回调
     2static int dual_burn_boot_info_result_hdl(int err)
     3{
     4    if(err == 0){
     5        // 启动信息烧写成功 → 可以重启了
     6        printf("启动标志更新成功,即将重启生效\n");
     7
     8        // 这里可以延时后调用系统重启
     9        // sys_reset();
    10    }else{
    11        // 烧写失败 → 不切换,继续用旧固件
    12        printf("更新启动信息失败\n");
    13    }
    14    return 0;
    15}
    16
    17// 2. 在校验成功后调用
    18dual_bank_update_burn_boot_info(dual_burn_boot_info_result_hdl);
    

2.10.4.7. dual_bank_update_verify_without_crc

  • 函数原型:

    u8 dual_bank_update_verify_without_crc(void);
    
  • 功能说明:适用于:上位机端没有下发新固件的CRC校验值的场景,函数会自动读取Flash里的新固件文件,进行完整性校验

  • 参数说明:无传参

  • 返回值:

    返回值

    说明

    0

    校验成功

    非0

    校验失败

  • 示例:

    1// 写完所有升级数据后,直接调用
    2u8 verify_res = dual_bank_update_verify_without_crc();
    3if(verify_res == 0) {
    4    // 校验成功 → 烧写启动信息,准备重启
    5    dual_bank_update_burn_boot_info(dual_burn_boot_info_result_hdl);
    6} else {
    7    // 校验失败 → 升级终止,保留旧固件
    8    printf("新固件校验失败\n");
    9}
    

2.10.4.8. flash_update_clr_boot_info

  • 函数原型:

    int flash_update_clr_boot_info(u8 type);
    
  • 功能说明:此API用于擦除指定分区的启动信息,使用时需非常谨慎!

  • 参数说明:

    参数

    说明

    type

    0:擦除当前运行分区的启动信息 1:擦除升级分区的启动信息

  • 返回值:

    返回值

    说明

    0

    擦除成功

    非0

    擦除失败

  • 示例:

    1// 注意擦除前后需要加解除和添加写保护,慎用!!!
    2norflash_set_write_protect(0, boot_info.vm.vm_saddr);
    3flash_update_clr_boot_info(1);
    4norflash_set_write_protect(1, boot_info.vm.vm_saddr);
    

2.10.5. 升级示例(伪代码)

  1// 全局/上层传入信息
  2u32 new_fw_crc;      // 上位机发过来的固件CRC
  3u32 new_fw_size;     // 上位机发过来的固件大小
  4u8  frame_buf[256];  // 上位机发过来的数据帧缓存
  5u32 frame_len;       // 上位机发过来的数据帧长度
  6u32 write_len;       // 记录写入flash大小
  7
  8static int burn_boot_info_result(int err)
  9{
 10    if (0 == err) {
 11        // 启动信息烧写成功 → 可以重启了
 12        cpu_reset();
 13    }
 14    return 0;
 15}
 16
 17// ====================== 带 CRC 升级(verify 不传回调)======================
 18void dual_bank_update_demo_with_crc(void)
 19{
 20    // 1. 解除flash写保护
 21    norflash_set_write_protect(0, boot_info.vm.vm_saddr);
 22
 23    // 2. 初始化升级
 24    if (dual_bank_passive_update_init(new_fw_crc, new_fw_size, 256, NULL)) {
 25        //升级终止,初始化失败
 26        goto __err_ret;
 27    }
 28
 29    // 3. 检查Flash空间是否足够
 30    if(dual_bank_update_allow_check(new_fw_size)) {
 31        //升级终止, 空间不足;
 32        goto __err_ret;
 33    }
 34
 35    // 4. 循环接收数据包并写入Flash
 36    write_len = 0;
 37    while(write_len < new_fw_size) {
 38        //接收一包数据 frame_buf, frame_len;
 39        if (dual_bank_update_write(frame_buf, frame_len, NULL)) {
 40            //升级终止,写入失败
 41            goto __err_ret;
 42        }
 43        write_len += frame_len;
 44    }
 45
 46    // 5. 带CRC校验(使用内部CRC,不传入回调函数)
 47    if (dual_bank_update_verify(NULL, NULL, NULL)) {
 48        //升级终止,校验失败
 49        goto __err_ret;
 50    }
 51
 52    // 6. 烧写启动信息
 53    if (dual_bank_update_burn_boot_info(burn_boot_info_result)) {
 54        //升级终止,烧写启动信息失败
 55        goto __err_ret;
 56    }
 57
 58    // 7. 升级成功,如果升级完成在回调函数不重启系统,需要把资源释放及写保护恢复了
 59__err_ret:
 60    // 8. 升级失败, 释放资源重新写保护flash
 61    dual_bank_passive_update_exit();
 62    norflash_set_write_protect(1, boot_info.vm.vm_saddr);
 63}
 64
 65// ====================== 不带 CRC 升级(手机不传 CRC)======================
 66void dual_bank_update_demo_without_crc(void)
 67{
 68    // 1. 解除flash写保护
 69    norflash_set_write_protect(0, boot_info.vm.vm_saddr);
 70
 71    // 2. 初始化升级
 72    if (dual_bank_passive_update_init(0, new_fw_size, 256, NULL)) {
 73        //升级终止,初始化失败
 74        goto __err_ret;
 75    }
 76
 77    // 3. 检查Flash空间是否足够
 78    if(dual_bank_update_allow_check(new_fw_size) != 0) {
 79        //升级终止, 空间不足;
 80        goto __err_ret;
 81    }
 82
 83    // 4. 循环接收数据包并写入Flash
 84    write_len = 0;
 85    while(write_len < new_fw_size) {
 86        //接收一包数据 frame_buf, frame_len;
 87        if (dual_bank_update_write(frame_buf, frame_len, NULL)) {
 88            //升级终止,写入失败
 89            goto __err_ret;
 90        }
 91        write_len += frame_len;
 92    }
 93
 94    // 5. 无CRC自动校验(杰理内部校验固件完整性)
 95    u8 verify_result = dual_bank_update_verify_without_crc();
 96    if (verify_result) {
 97        //升级终止,校验失败
 98        goto __err_ret;
 99    }
100
101    // 6. 烧写启动信息
102    if (dual_bank_update_burn_boot_info(burn_boot_info_result)) {
103        //升级终止,烧写启动信息失败
104        goto __err_ret;
105    }
106
107    // 7. 升级成功,如果升级完成在回调函数不重启系统,需要把资源释放及写保护恢复了
108__err_ret:
109    // 8. 升级失败, 释放资源重新写保护flash
110    dual_bank_passive_update_exit();
111    norflash_set_write_protect(1, boot_info.vm.vm_saddr);
112    return;
113}