文章目录
-
-
-
- **一、引言:为何需要差分升级?**
- **二、工作流程:从制作补丁到设备升级**
-
- [**阶段一:在PC端制作QBoot差分升级包 (`.rbl`)**](#阶段一:在PC端制作QBoot差分升级包 (
.rbl))
- [**阶段一:在PC端制作QBoot差分升级包 (`.rbl`)**](#阶段一:在PC端制作QBoot差分升级包 (
- [**三、核心机制:原地升级(In-Place Update)与缓冲策略**](#三、核心机制:原地升级(In-Place Update)与缓冲策略)
-
- [**策略一:使用Flash作为缓冲区 (Flash Swap)**](#策略一:使用Flash作为缓冲区 (Flash Swap))
- [**策略二:使用RAM作为缓冲区 (RAM Buffer)**](#策略二:使用RAM作为缓冲区 (RAM Buffer))
- **四、设备端配置实战**
-
- [**1. FAL分区规划**](#1. FAL分区规划)
- [**2. Kconfig配置**](#2. Kconfig配置)
- [**3. `rtconfig.h` 宏定义示例 (对应RAM Buffer策略)**](#3.
rtconfig.h宏定义示例 (对应RAM Buffer策略))
- [**五、深入底层:`hpatchlite-wrapper` 的工作原理**](#五、深入底层:
hpatchlite-wrapper的工作原理) - **六、结论**
- **七、相关链接**
- **八、后记**
-
-

一、引言:为何需要差分升级?
在物联网(IoT)设备的生命周期中,固件空中升级(FOTA)是不可或缺的一环。然而,传统的全量包升级方式意味着每次都需要传输完整的固件,这不仅消耗了大量的网络带宽,延长了升级时间,还对设备的Flash存储空间提出了更高的要求。
**差分升级(Differential Update)**通过只传输新旧固件之间的"差异"部分(即补丁包),极大地减小了升级包的体积,完美地解决了上述痛点。本文将详细介绍如何在RT-Thread操作系统中,利用QBoot引导加载程序和轻量级差分库HPatchLite,构建一套完整、高效且资源可控的差分升级方案。
核心组件架构:
云端/PC端 设备端 (RT-Thread) 输入 生成 patch.bin 打包成 patch.rbl 下载升级包 调用 读写Flash hdiffi (差分工具) package_tool.py (打包工具) 新旧固件.bin 下载服务器 QBoot (Bootloader) FAL (Flash抽象层) hpatchlite-wrapper (差分合并引擎)
图1:差分升级方案的整体架构
二、工作流程:从制作补丁到设备升级
整个差分升级流程分为两大阶段:在PC端制作差分包,以及在设备端执行升级。
阶段一:在PC端制作QBoot差分升级包 (.rbl)
这一阶段的目标是生成一个QBoot可以识别和解析的、包含差分信息的升级包。
步骤 1: 生成原始差分补丁 (patch.bin)
-
下载工具 : 从 HPatchLite官方GitHub仓库 下载预编译的二进制工具包 (
HPatchLite_vX.X.X_bin_xxxx.zip) 并解压。 -
准备文件 : 准备好
old.bin(设备当前固件) 和new.bin(新版固件)。 -
执行命令 : 使用
hdiffi.exe工具生成原始差分数据。bash# hdiffi.exe <旧固件> <新固件> <输出的原始补丁> .\hdiffi.exe old.bin new.bin patch.bin(可选)为了进一步减小补丁体积,可以启用
tuz压缩算法。这对于带宽受限的设备至关重要。bash# -c-tuz 参数启用了tuz压缩 .\hdiffi.exe -c-tuz old.bin new.bin patch_compressed.bin
步骤 2: 将原始补丁封装为QBoot格式 (.rbl)
QBoot需要一个带有标准头部信息的升级包。我们使用配套的Python脚本 package_tool.py 来完成封装。
bash
# python package_tool.py <原始补丁> <新固件>
python .\package_tool.py patch.bin new.bin
关键点解析:
- 第一个参数 (
patch.bin) 是上一步生成的原始补丁 ,它将成为.rbl文件的数据主体。 - 第二个参数 (
new.bin) 不会被打包 进.rbl文件。它仅用于计算RBL头部所需的元数据,如新固件的总大小 (raw_size) 和CRC校验值 (raw_crc),供QBoot在升级后进行校验。
执行成功后,生成的 patch.rbl 就是我们可以下发给设备的最终升级包。
三、核心机制:原地升级(In-Place Update)与缓冲策略
差分升级最棘手的问题在于"原地升级":我们需要从app分区读取旧数据,同时又要向同一个app分区写入新数据。由于Flash的物理特性(通常不能在读取一个扇区的同时擦写它),我们必须借助一个**缓冲区(Swap/Buffer)**来完成这个"左右手互搏"的操作。
QBoot与hpatchlite-wrapper巧妙地提供了两种缓冲策略,开发者可以根据设备的RAM和Flash资源进行权衡选择。
原地升级挑战 矛盾点 需要读取旧数据 需要写入新数据 app分区 差分合并算法 无法在同一Flash扇区
同时进行读和写/擦除操作
图2:原地升级的内在矛盾
策略一:使用Flash作为缓冲区 (Flash Swap)
此策略在RAM资源极其宝贵时使用。它会利用一个独立的Flash分区作为临时周转空间。
- Kconfig配置 :
QBOOT_HPATCH_USE_FLASH_SWAP - 工作流程 :
- 当需要修改
app分区的某个扇区S时,首先将S的原始内容拷贝 到swap分区。 - 差分算法从
swap分区读取旧数据,结合补丁数据,生成新的数据。 - 擦除
app分区的扇区S。 - 将新生成的数据从RAM写回
app分区的扇区S。
- 当需要修改
- 优点: 极大地节省了宝贵的RAM。
- 缺点: 速度相对较慢(涉及多次Flash读写),对Flash的磨损也更大。
策略二:使用RAM作为缓冲区 (RAM Buffer)
此策略在RAM资源相对充足时使用,速度更快。
- Kconfig配置 :
QBOOT_HPATCH_USE_RAM_BUFFER - 工作流程 :
- 当需要修改
app分区的某个扇区S时,直接将S的原始内容读入一个较大的RAM缓冲区。 - 差分算法直接在RAM中,从这个缓冲区读取旧数据,生成新数据。
- 擦除
app分区的扇区S。 - 将RAM缓冲区中的新数据写回
app分区的扇区S。
- 当需要修改
- 优点: 速度快,对Flash的磨损小。
- 缺点 : 消耗的RAM较多。注意 :此RAM缓冲区的大小必须大于等于
app分区所在Flash的最小擦除单元(Sector Size)。
四、设备端配置实战
1. FAL分区规划
一个合理的FAL分区规划是成功的前提。
c
/* 示例 fal_cfg.h,Flash扇区大小为128KB */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "app", "onchip_flash_128k", 0, 3*128*1024, 0}, \
{FAL_PART_MAGIC_WORD, "factory", "onchip_flash_128k", 3*128*1024, 2*128*1024, 0}, \
{FAL_PART_MAGIC_WORD, "swap", "onchip_flash_128k", 5*128*1024, 1*128*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", "onchip_flash_128k", 6*128*1024, 1*128*1024, 0}, \
}
app: 差分升级的目标分区。download: 用于存放下载的patch.rbl升级包。swap: 如果使用Flash Swap策略,此分区必须存在 ,且其大小至少要等于app分区的擦除粒度(本例中为128KB)。
2. Kconfig配置
在menuconfig中启用并配置HPatchLite功能。
text
RT-Thread online packages --->
tools packages --->
Qboot: A cross-platform MCU bootloader framework --->
[*] using hpatch-lite decompress
[ ] Use a FLASH partition as swap buffer ---> # 策略一
[*] Use a RAM buffer (Faster, consumes RAM) # 策略二
(4096) RAM buffer size for HPatchLite (must be >= sector size)
3. rtconfig.h 宏定义示例 (对应RAM Buffer策略)
c
#define PKG_USING_QBOOT
#define QBOOT_APP_PART_NAME "app"
#define QBOOT_DOWNLOAD_PART_NAME "download"
// ... 其他QBoot配置 ...
/* --- HPatchLite 关键配置 --- */
#define QBOOT_USING_HPATCHLITE
#define QBOOT_HPATCH_USE_RAM_BUFFER
#define QBOOT_HPATCH_RAM_BUFFER_SIZE 4096
五、深入底层:hpatchlite-wrapper 的工作原理
hpatchlite-wrapper 是连接QBoot上层逻辑与HPatchLite核心算法的桥梁。其核心API hpi_patch 通过回调函数机制,实现了高度的灵活性和平台解耦。
c
/**
* @brief 使用 HPatchLite 库执行差分合并操作
*/
hpi_patch_result_t hpi_patch(hpatchi_listener_t *listener, // 用户上下文
int patch_cache_size,
int decompress_cache_size,
hpi_TInputStream_read _do_read_diff, // 读补丁回调
read_old_t _do_read_old, // 读旧固件回调
write_new_t _do_write_new); // 写新固件回调
在QBoot的实现中,_do_read_old 和 _do_write_new 函数内部就包含了对上述**缓冲策略(RAM或Flash)**的复杂处理逻辑,从而对hpi_patch核心算法屏蔽了底层细节。
特别注意:RAM占用
当使用压缩补丁时,hdiffi工具的输出日志会包含一行至关重要的信息:
requirements memory size: (must) 27065 + (custom cache) 32768
(must) 27065: 这是解压器(如tuz)强制要求 的、必须通过hpi_malloc成功分配的RAM大小。它由补丁数据本身决定。(custom cache) 32768: 这是您在调用hpi_patch时传入的patch_cache_size和decompress_cache_size。
您必须确保设备的堆内存 足以满足这个 (must) 的要求,否则差分升级会因内存分配失败而终止。
六、结论
通过将QBoot的引导框架、FAL的标准化存储接口与HPatchLite的高效差分算法相结合,RT-Thread开发者可以轻松地为项目集成一套健壮、高效且资源可控的差分升级(FOTA)方案。正确理解并选择适合项目资源的缓冲策略 ,以及在制作补丁时关注内存需求,是成功实施此方案的关键。这不仅能极大地提升用户体验,还能显著降低项目的长期运营成本。
七、相关链接
https://github.com/sulfurandcu/hpatchlite-wrapper
https://github.com/qiyongzhong0/rt-thread-qboot/blob/master/src/qboot_hpatchlite.c
八、后记
hpatchlite-wrapper这个软件包发布快一年了,论坛上一个文章和提问也没有.估计下载的也没几个.我也是知道有hpatchlite这个差分升级组件,才去软件包内搜索才知道居然有人已经实现了这个功能了.- 大家发布软件包都推上去连宣传一下也没有,都没人知道什么时候多了一个好用的软件包,是干嘛用的.
- 或许RTT官方应该在每个软件包发布或者更新的时候,来论坛发一个帖子给这些软件包增加一点曝光度.