4. 固件版本管理
任务目标
实现固件版本号的存储与读取,支持版本比较,为OTA升级提供依据。版本号采用三段式格式 major.minor.patch(如 2.1.0),存储在Flash固定区域,支持写入、读取和比较操作。
版本存储格式
版本号以紧凑二进制格式存储于Flash,占用4字节:
- Byte 0:major(0~255)
- Byte 1:minor(0~255)
- Byte 2:patch(0~255)
- Byte 3:校验字节(前3字节异或值)
校验字节用于检测Flash数据损坏,读取时若校验失败则返回默认版本 0.0.0。
完整实现
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
// Flash模拟存储(实际项目中替换为Flash驱动接口)
#define VERSION_FLASH_ADDR 0x0800FC00 // 示例地址
static uint8_t flash_buf[4]; // 模拟Flash内容
typedef struct {
uint8_t major;
uint8_t minor;
uint8_t patch;
} fw_version_t;
// 版本比较结果
typedef enum {
VER_LOWER = -1,
VER_EQUAL = 0,
VER_HIGHER = 1
} ver_cmp_t;
// 计算校验字节
static uint8_t calc_checksum(const fw_version_t *ver) {
return ver->major ^ ver->minor ^ ver->patch;
}
// 写入版本到Flash
bool version_write(const fw_version_t *ver) {
if (!ver) return false;
flash_buf[0] = ver->major;
flash_buf[1] = ver->minor;
flash_buf[2] = ver->patch;
flash_buf[3] = calc_checksum(ver);
// 实际工程中在此调用Flash擦写驱动
// flash_erase(VERSION_FLASH_ADDR);
// flash_write(VERSION_FLASH_ADDR, flash_buf, 4);
return true;
}
// 从Flash读取版本
fw_version_t version_read(void) {
fw_version_t ver = {0, 0, 0};
// 实际工程中在此调用Flash读取驱动
// flash_read(VERSION_FLASH_ADDR, flash_buf, 4);
// 校验数据有效性
uint8_t checksum = flash_buf[0] ^ flash_buf[1] ^ flash_buf[2];
if (checksum != flash_buf[3]) {
return ver; // 校验失败,返回0.0.0
}
ver.major = flash_buf[0];
ver.minor = flash_buf[1];
ver.patch = flash_buf[2];
return ver;
}
// 比较两个版本号
ver_cmp_t version_compare(const fw_version_t *v1, const fw_version_t *v2) {
if (!v1 || !v2) return VER_EQUAL;
if (v1->major != v2->major)
return (v1->major > v2->major) ? VER_HIGHER : VER_LOWER;
if (v1->minor != v2->minor)
return (v1->minor > v2->minor) ? VER_HIGHER : VER_LOWER;
if (v1->patch != v2->patch)
return (v1->patch > v2->patch) ? VER_HIGHER : VER_LOWER;
return VER_EQUAL;
}
// 字符串解析辅助(可选,用于从固件头解析版本)
bool version_from_string(const char *str, fw_version_t *out) {
if (!str || !out) return false;
unsigned mj, mn, pt;
if (sscanf(str, "%u.%u.%u", &mj, &mn, &pt) != 3) return false;
if (mj > 255 || mn > 255 || pt > 255) return false;
out->major = (uint8_t)mj;
out->minor = (uint8_t)mn;
out->patch = (uint8_t)pt;
return true;
}
// 测试入口
#include <stdio.h>
int main(void) {
// 模拟Flash初始为空(全0xFF)
memset(flash_buf, 0xFF, sizeof(flash_buf));
// 测试1:写入并读取
fw_version_t v1 = {2, 1, 0};
version_write(&v1);
fw_version_t v2 = version_read();
printf("写入: %u.%u.%u, 读取: %u.%u.%u\n",
v1.major, v1.minor, v1.patch,
v2.major, v2.minor, v2.patch);
// 测试2:版本比较
fw_version_t old_ver = {2, 0, 9};
fw_version_t new_ver = {2, 1, 0};
ver_cmp_t cmp = version_compare(&old_ver, &new_ver);
printf("比较结果: %d (期望1,表示new_ver更高)\n", cmp);
// 测试3:校验失败场景
flash_buf[3] = 0x00; // 破坏校验字节
fw_version_t invalid = version_read();
printf("损坏数据读取: %u.%u.%u (期望0.0.0)\n",
invalid.major, invalid.minor, invalid.patch);
return 0;
}
编译与运行
gcc -o version_test version_test.c
./version_test
验证方法
程序输出应满足以下条件即判定通过:
- 写入版本
2.1.0后读取结果一致,校验字节正确 version_compare对2.0.9与2.1.0返回1(新版本更高)- 人为破坏校验字节后,读取返回
0.0.0,不会返回错误版本
OTA集成说明
在实际OTA流程中,固件升级前调用 version_read() 获取当前运行版本,从升级包头部解析出新版本号,通过 version_compare() 判断新版本是否高于当前版本。只有新版本更高时才允许执行升级,升级完成后调用 version_write() 更新存储的版本号。Flash驱动接口(擦除、写入、读取)需根据目标MCU替换为实际实现。