bb_update.h
BB_STATE 枚举成员struct BB_UPDATE_CONTENT 字段(#pragma pack(1),无填充)struct BB_CHECK 字段3. 函数清单4. 核心逻辑解析4.1 状态机契约(BB_STATE)4.2 数据布局约定(#pragma pack(1))4.3 通信协议帧格式4.4 与 uCOS-II 的配合4.5 关键全局变量5. 对外接口导出(供外部调用)使用的外部符号(本文件自身依赖)6. 依赖关系7. 备注与风险点源文件路径:
midware/meter/bb_update.h所属模块:midware/meter(电表/备用箱中间件) 代码行数:78
1. 文件职责
这是 BackupBox(BB,备用箱)内置 MCU 串口固件升级模块的对外头文件。它集中声明了升级流程用到的三类信息:协议常量(接收帧类型判别值、单帧最大负载长度)、升级状态机与帧类型的枚举(BB_STATE / BB_INSTRUCTIONS),以及升级上下文结构体(BB_UPDATE_CONTENT)和Flash 校验累加结构体(BB_CHECK)。
在数据流中它处在 midware/meter 中间件层与 net/(服务器固件下发)之间的接口位置:net/ 层把 BB 固件下载进 SDRAM,bb_update.c 用这里定义的结构体做升级上下文,midware/meter/meter.c 的 task_meter() 调用 bb_update() 推进升级。
本文件不包含任何实现,只做类型与接口声明;它被 bb_update.c、meter.c、net/ems.c include。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
__BB_UPDATE_H__ | 宏 | 头文件防重复包含保护 |
DEBUG_BB | 宏 | 0,调试开关;本文件中未被任何代码使用(bb_update.c 用的是 USART1_DEBUG) |
RECV_SEQUENCE_FRAME | 宏 | 0x06,接收侧"序列帧"类型判别值;bb_update.c 未直接引用,实际判定用的是硬编码字节 |
RECV_COMMAND_DATA_FRAME | 宏 | 0x01,接收侧"指令与数据帧"类型判别值;同上未直接引用 |
MAX_PRG_LENGTH | 宏 | 112*2 = 224,单次写 Flash 帧的最大负载字节数(112 个 16bit 字);bb_update.c:185 用它与拼包长度比较 |
BB_INSTRUCTIONS | 枚举 | 帧类型分类:REPLY_FRAME(回复帧)、SEQUENCE_FRAME(序列帧)、COMMAND_DATA_FRAME(指令与数据帧);仅有语义说明作用,代码中未实例化使用 |
BB_STATE | 枚举 | 升级状态机全部状态,见下节;bb_update.c 以整数值存入 BB_UPDATE_CONTENT.u8_insteructions_state |
struct BB_UPDATE_CONTENT | 结构体(#pragma pack(1)) | 升级主上下文,见下节字段表 |
struct BB_CHECK | 结构体(#pragma pack(1)) | Flash 校验累加器,见下节字段表 |
BB_STATE 枚举成员
| 成员 | 隐含值 | 说明 |
|---|---|---|
RESET_INSTRUCTIONS | 0 | 发复位指令(上电初值即此状态) |
BOOT_INSTRUCTIONS | 1 | 发 "PSLAVER\r" 让 BB 进入 Bootloader |
ADJUST_RATE | 2 | 发 0x41 触发波特率自适应 |
ERASE_FLASH | 3 | 擦除 BB 应用区 |
WRITE_FLASH | 4 | 分块写 Flash(主体状态) |
CHECK_FLASH | 5 | 分段校验 Flash |
SEND_KEY_WORD | 6 | 写升级关键字 0x6D83 |
UPGRADE_END | 7 | 发结束指令 |
UPGRADE_INIT | 8 | 清理并重新开始 |
RECV_DATA | 9 | 未被使用,遗留成员 |
struct BB_UPDATE_CONTENT 字段(#pragma pack(1),无填充)
| 字段 | 类型 | 说明 | |
|---|---|---|---|
s32_len_buff | u32 | 本行 HEX 的数据字节数(命名前缀为 s32_ 但实际是 u32) | |
s32_length | u32 | 当前拼包缓冲中已累积的负载字节数,0 表示无待发数据 | |
s32_addr_flash | u32 | 本次要写入 BB Flash 的绝对地址 = `(base<<16) \ | offset` |
u32_old_flash_addr | u32 | 上一次发送的绝对地址,用于判断是否需要对本次数据做校验累加 | |
s32_old_addr_offset | u32 | 上一行 HEX 的地址偏移,用于判断行间是否连续 | |
s32_addr_flash_offset | u32 | 本行 HEX 的地址偏移(hex_file_data.u16_addr) | |
s32_addr_flash_base | u32 | HEX 扩展线性地址(高 16 位基址) | |
u8_buffer[256] | u8[256] | 拼包缓冲;首次拼包时整体预填 0xFF(模拟 Flash 擦除态),再按大端写入各行数据 | |
u8_loop_addr | u8 | 通信序列号,每发一帧递增,UPGRADE_INIT 时清零 | |
u16_check_code | u16 | 校验码字段;在 bb_update.c 中从未被读写,遗留字段 | |
u8_insteructions_state | u8 | 升级状态,取 BB_STATE 值(注意拼写为 insteructions,为源码原有拼写错误) | |
u32_amount_sequence_frame | u32 | 本次序列帧要声明的负载字节数(= s32_length) |
struct BB_CHECK 字段
| 字段 | 类型 | 说明 |
|---|---|---|
u8_time | u8 | 校验轮次计数;bb_update.c:197 只自增、从不读取,属死代码 |
u16_check_length[10] | u16[10] | 每个校验区已累加的 16bit 字数;实际只用下标 0(前半区)、1(后半区) |
u16_check_code[10] | u16[10] | 每个校验区的 16bit 累加和;实际只用下标 0/1/2(下标 2 为关键字区相关校验) |
3. 函数清单
本文件为纯头文件,不包含任何函数定义,仅在文件末尾(:73-75)声明了 3 个由 bb_update.c 实现的导出函数:
| 函数名 | 作用域 | 说明 |
|---|---|---|
void bb_update(void) | 全局(外部实现) | BB 升级主状态机入口,每次调用推进一个状态 |
u8 GetPercentUpgradeBB(void) | 全局(外部实现) | 返回 BB 升级百分比(0-100) |
u32 GetUpgradeFileOffsetBB(void) | 全局(外部实现) | 返回 BB 已传输的固件文本字节数 |
4. 核心逻辑解析
本文件无运行逻辑,此处说明其定义的类型在实现文件中的使用方式,以及由这些类型约定出的状态迁移。
4.1 状态机契约(BB_STATE)
实现文件 bb_update.c:317 用 switch(g_bb_update.u8_insteructions_state) 驱动,迁移关系为:
RESET_INSTRUCTIONS → BOOT_INSTRUCTIONS → ADJUST_RATE → ERASE_FLASH → WRITE_FLASH
▲ │
│ 解析到关键字地址
│ ▼
UPGRADE_INIT ◄── UPGRADE_END ◄── CHECK_FLASH(轮次≥10) ◄── CHECK_FLASH
│ (轮次==2)
▼
SEND_KEY_WORD ──► CHECK_FLASH
- 枚举值 0(
RESET_INSTRUCTIONS)是上电默认状态,因为g_bb_update是未初始化的全局变量(BSS 清零)。 RECV_DATA成员虽在枚举中,但实现文件的switch里没有对应case,会落到default分支被强制回UPGRADE_INIT。- 另有外部看门狗:实现文件在
switch之后判断 60 秒无进展即强制回UPGRADE_INIT。
4.2 数据布局约定(#pragma pack(1))
两个结构体都用 #pragma pack(1) 收紧,随后 #pragma pack() 恢复默认。这表示:
- 结构体不用于串口字节流收发(串口帧是独立的
u8数组),而是作为运行期上下文。使用pack(1)主要是历史习惯,实际收益有限(除了让memset(&g_bb_update, 0, sizeof(g_bb_update))与memcpy语义稳定)。 u8_buffer[256]是结构体中最大的成员,memcpy(g_u8_write_flash+20, g_bb_update.u8_buffer, 256)(bb_update.c:430)会向 256 字节的目标数组越界写入 20 字节,这一点由本文件把缓冲区定为 256 字节直接决定。BB_CHECK的两个数组长度为 10,但实现只用到 3 个下标,属于为未来扩展预留的冗余设计。
4.3 通信协议帧格式
本文件用宏 RECV_SEQUENCE_FRAME=0x06 与 RECV_COMMAND_DATA_FRAME=0x01 描述了接收侧两类帧的识别值,但实现文件(bb_update.c:274-291)并未使用这两个宏,而是硬编码了 14 个字节的常量特征做匹配。因此:
- 协议帧格式(回复帧
06 99 66 <seq> 00 08 00 00、序列帧01 66 99 <seq> <len_H> <len_L> 00 00)在头文件中没有任何体现,全部散落在bb_update.c的全局数组里。 - 无 CRC 字段,校验方式是帧内常量特征 + Flash 内容校验和(
BB_CHECK)。 - 超时重试机制不在头文件约定:由
bb_update.c内部静态计数器(s_u8_send_err_cnt> 3、s_u8_parse_cnt> 3、s_u8_check_time>= 10)与 60 秒总超时共同决定。
4.4 与 uCOS-II 的配合
头文件不涉及任务/信号量/邮箱;所有同步由实现文件中的 OSTimeGet() + OSTimeDlyHMSM() 完成。BB_UPDATE_CONTENT 只是被 task_meter() 单线程访问的普通全局数据,没有互斥保护——GetPercentUpgradeBB() / GetUpgradeFileOffsetBB() 被 net/ 层(可能运行在另一个任务)读取,与升级流程写入构成无锁跨任务共享。
4.5 关键全局变量
DIESEL无关;本文件唯一导出的全局符号是 3 个函数声明。- 结构体类型本身(
struct BB_UPDATE_CONTENT、struct BB_CHECK)是全局可见类型名,但实例(g_bb_update、g_check_code)在bb_update.c:36-37定义,本文件没有extern声明它们。因此其他文件无法直接访问升级上下文,只能通过 3 个导出函数取进度。
5. 对外接口
导出(供外部调用)
| 名称 | 说明 |
|---|---|
void bb_update(void) | 由 midware/meter/meter.c:5738 调用 |
u8 GetPercentUpgradeBB(void) | 由 midware/meter/meter.c:6844、:6857 调用 |
u32 GetUpgradeFileOffsetBB(void) | 由 net/ems.c:6706 调用 |
MAX_PRG_LENGTH | 宏,bb_update.c:185 使用 |
struct BB_UPDATE_CONTENT / struct BB_CHECK | 类型,bb_update.c 用于定义全局实例 |
使用的外部符号(本文件自身依赖)
| 名称 | 来源 |
|---|---|
includes.h | midware/meter/bb_update.h:13 include;提供 u8/u16/u32/s32 等基础类型与全局公共头 |
6. 依赖关系
- 依赖:仅
#include <includes.h>(工程统一公共头,提供基本类型定义与各模块声明)。 - 被依赖:
midware/meter/bb_update.c(唯一实现者)midware/meter/meter.c:25(#include "bb_update.h",调bb_update()/GetPercentUpgradeBB())net/ems.c:50(#include "bb_update.h",调GetUpgradeFileOffsetBB())
7. 备注与风险点
- 命名前缀与实际类型不符:
BB_UPDATE_CONTENT中s32_len_buff、s32_length、s32_addr_flash、s32_old_addr_offset、s32_addr_flash_offset、s32_addr_flash_base全部声明为u32,前缀却是s32_(有符号 32 位)。容易误读为有符号量,而bb_update.c:154的(s32_addr_flash_offset - s32_old_addr_offset) > 0x10正是依赖无符号回绕的比较,前缀误导性明显。只有u32_old_flash_addr用了正确的u32_前缀,同一结构体内风格不统一。 - 拼写错误固化进接口:
u8_insteructions_state(应为instructions)在头文件和实现文件中多处引用,属于无法在不破坏兼容的前提下修改的历史遗留。 u16_check_code为死字段:结构体里保留了一个从未被读写的u16_check_code,容易被误认为有效校验字段。BB_INSTRUCTIONS枚举与RECV_SEQUENCE_FRAME/RECV_COMMAND_DATA_FRAME宏均为"文档型死定义":实现文件全部使用硬编码字节做判定,宏改不动行为,反而给维护者"改这里就生效"的错觉。DEBUG_BB宏无效::16定义了DEBUG_BB,但实现文件的调试开关是USART1_DEBUG(来自includes.h),DEBUG_BB在任何.c中都未被引用。u8_buffer[256]尺寸与使用方式不匹配:MAX_PRG_LENGTH是 224,载荷实际只用 224 字节,但bb_update.c:430整块拷贝 256 字节到g_u8_write_flash+20,造成 20 字节越界写。头文件这边的缓冲区声明应改为[MAX_PRG_LENGTH]或把拷贝长度改为MAX_PRG_LENGTH。BB_CHECK数组长度 10 与实际用 3 不匹配:u8_time字段也是纯自增死代码,属未清理的预留设计。#pragma pack(1)与#pragma pack()配对使用不带参数:#pragma pack()恢复默认对齐是 Keil ARMCC 支持的写法,但跨编译器(如 GCC)行为不同;本工程固定用 Keil MDK,可接受。- 无
extern声明升级上下文:g_bb_update/g_check_code在bb_update.c中是非 static 全局变量(外部链接),但头文件没有extern声明,形成"有外部链接但无对外声明"的状态,仅仅靠 3 个访问函数收口——从封装角度看是有意的,但g_u8_bb_recv_buf同样是外部链接却完全无出口,属于不一致。 - 异常路径未在接口约定:
GetPercentUpgradeBB()除零、bb_update()被并发调用等异常在头文件中没有任何说明,调用方需自行保证时序。