EMS 知识库
首页 / knowledge/代码解析/midware/meter

meter_upgrade.h

源文件路径:midware/meter/meter_upgrade.h 所属模块:midware/meter(电表中间件) 代码行数:71

1. 文件职责

这是外部电表(电网表 / PV 表 / 正泰无线电表)固件在线升级模块的对外头文件。它声明了三类内容:升级协议常量与调度参数(命令字 0x23、单帧最大数据 250、起始 Flash 地址 0、无线升级位域常量)、升级状态机与命令码的两套枚举,以及升级上下文结构体 METER_UPGRADE_T。

在数据流中它处在 net/(固件下载、置升级标志)与 midware/meter/meter.c(task_meter() 驱动升级)之间的契约位置:meter_upgrade.c 是唯一实现者,meter.c 通过本头文件调用 meter_upgrade_handle() 并读取升级进度上报云端。

本文件不含任何实现代码。

2. 关键数据结构与宏

名称类型说明
__METER_UPDATE_H__宏头文件防重复包含保护
DEBUG_METER_UPDATE宏0,调试打印开关;meter_upgrade.c 用 #if (DEBUG_METER_UPDATE) 包裹所有 printf
METER_UPGRADE_MAX_DATA宏250,单帧最大固件数据字节数;实现文件中用于 METER_WRITE_FLASH 的分块长度与总帧数计算
METER_UPGRADE_BEGIN_ADDR宏0x00000000,电表侧 Flash 起始烧写地址
RECV_SEQUENCE_FRAME宏0x06,响应帧中"序列/数据帧"标识;实现文件用 recv[7] 与该值比对判有效
METER_UPGRADE_CMD_CODE宏0x23,协议命令字;请求帧 [1] 固定填入该值
METER_BUF_LEN宏1024;全工程无任何引用,死宏
METER_UPDATE_VALID_LEN宏500;全工程无任何引用,死宏
CHNT_WIRELESS_UPDATE_POP宏7,正泰无线升级传入的"子节点状态位域"(0b111:位 1、位 2 置位 → STA1、STA2 都入栈)
METER_UPGRADE_STATE_E枚举主状态机 7 个状态,见下节;类型名本身未被任何文件引用,仅使用其枚举常量
METER_UPGRADE_CMD_E枚举5 个命令码,见下节;类型名本身未被任何文件引用
METER_UPGRADE_T结构体(#pragma pack(1))升级上下文,见下节
meter_upgrade_handle函数声明升级主状态机入口
get_percent_upgrade_meter函数声明返回升级百分比
get_upgrade_file_offset_meter函数声明返回已发送的固件字节偏移

METER_UPGRADE_STATE_E 枚举成员

成员值说明
METER_UPGRADE_INIT0初始化/确定目标设备地址;也是 BSS 清零后的上电初值
METER_BOOT_ENTER1发命令码 0,让电表进入 Bootloader
METER_HAND_SHAKE2发命令码 1,握手
METER_ERASE_FLASH3发命令码 2,擦除电表应用区;成功后初始化文件指针/总帧数/起始地址
METER_WRITE_FLASH4发命令码 3,分块写数据(主体状态,每帧 250 字节)
METER_BOOT_EXIT5发命令码 4,退出 Bootloader 并让电表跳转到新固件
METER_UPGRADE_END6收尾:复位上下文、处理无线多节点、清除升级标志

METER_UPGRADE_CMD_E 枚举成员

成员值说明
METERCMD_BOOT_ENTER0进 Boot(错误门限 5 次)
METERCMD_HAND_SHAKE1握手(门限 3 次)
METERCMD_ERASE_FLASH2擦除(门限 3 次)
METERCMD_WRITE_FLASH3写 Flash(门限 3 次);同时作为写数据帧 [10] 的操作码
METERCMD_BOOT_EXIT4退 Boot(门限 3 次)

METER_UPGRADE_T 字段(#pragma pack(1))

字段类型说明
u16_lengthu32本次发送的数据字节数(每帧 250、尾块为余数);命名前缀 u16_ 与实际类型 u32 不符
u32_addr_flashu32电表侧目标 Flash 地址,从 METER_UPGRADE_BEGIN_ADDR 起每帧 +250
u32_addr_flash_offsetu32源侧(g_u8_fw_sdram1)已发送字节偏移,是升级进度的唯一来源
u8_upgrade_file_bufu8*固件源指针,擦除成功后指向 g_u8_fw_sdram1
u16_send_file_numu16总帧数 = ceil(get_length_file_updating(UPDT_Meter) / 250)
u8_upgrade_stateu8状态机当前状态(取 METER_UPGRADE_STATE_E 值)
u8_meter_addru8目标从站地址:电网表 0x01、PV 表 0x02 或 0x0B、无线节点 0xE1~0xE3
u8_err_cntu8当前状态连续失败计数,超过门限即跳 METER_UPGRADE_END
u32_sequnou32通信序列号,每次成功发送后自增,填入请求帧 [2]

#pragma pack(1) 使结构体无填充;该结构体是纯运行期上下文(不做串口字节流映射),因此 pack(1) 的作用主要是让 memset(&s_meter_upgrade_tab, 0, sizeof(...)) 的语义稳定。

3. 函数清单

本文件为纯头文件,不包含任何函数定义,仅在 :66-68 声明了 3 个由 meter_upgrade.c 实现的导出函数:

函数名作用域说明
void meter_upgrade_handle(void)全局(外部实现)升级主状态机入口,每次调用推进一个状态
u8 get_percent_upgrade_meter(void)全局(外部实现)返回升级百分比(0–100)
u32 get_upgrade_file_offset_meter(void)全局(外部实现)返回已发送的固件字节偏移

4. 核心逻辑解析

本文件无运行逻辑。以下说明它约定的协议、状态迁移与调度参数如何被实现文件使用。

4.1 状态机契约(METER_UPGRADE_STATE_E)

实现文件 meter_upgrade.c:289 用 switch(s_meter_upgrade_tab.u8_upgrade_state) 驱动:

METER_UPGRADE_INIT → METER_BOOT_ENTER → METER_HAND_SHAKE → METER_ERASE_FLASH
                                                                   │
                                                                   ▼
                        METER_UPGRADE_END ◄── METER_BOOT_EXIT ◄── METER_WRITE_FLASH
                               │                    ▲                 │
                               │                    └─────────────────┘
                               │              (无线表:栈还有地址 → 直接回 BOOT_ENTER 升级下一节点)
                               └──► 清 UPDT_Meter 标志 → METER_UPGRADE_INIT(等待下一次升级请求)

4.2 命令码与错误门限(METER_UPGRADE_CMD_E)

命令码通过 meter_upgrade_send_code(code, timeout) 填入控制帧的 [10] 位置,同时在响应帧的 [6] 位置被回显校验(实现文件 :90:recv[6] == send_buf[10])。这套"请求发操作码 / 响应回显操作码"的机制用于把响应与具体命令关联起来。

各命令的调用超时与错误门限由实现文件决定,并不在同名枚举或宏中表达:

命令调用超时失败门限(超过即结束升级)
METERCMD_BOOT_ENTER5000ms5
METERCMD_HAND_SHAKE5000ms3
METERCMD_ERASE_FLASH5000ms3
METERCMD_WRITE_FLASH2000ms3
METERCMD_BOOT_EXIT5000ms3

4.3 分帧参数(METER_UPGRADE_MAX_DATA / METER_UPGRADE_BEGIN_ADDR)

4.4 收发协议(帧格式)

头文件不定义帧格式,只提供命令字 0x23 与响应帧标识 0x06。完整格式在 meter_upgrade.c 中硬编码:

4.5 定时/调度与 uCOS-II 配合

4.6 关键全局变量

本文件不导出任何全局变量,只导出 3 个函数与若干宏/类型。升级上下文实例 s_meter_upgrade_tab 在 meter_upgrade.c:25 定义为 static,外部无法直接访问,只能通过两个取值函数(百分比、偏移)间接读取。这一点是刻意的封装。

正泰无线多节点升级用到的地址栈 不属于本文件,而是 meter.h:313 的 g_t_meter.g_code_stack(CHNT_WIRELESS_STACK 类型,u8_codes[MAX_CODES+1] + u8_top,MAX_CODES = 3)。头文件 CHNT_WIRELESS_UPDATE_POP 的注释"正泰无线电表升级地址按位域存储"正是指这一栈的填充方式。

5. 对外接口

导出(供外部调用)

名称说明
void meter_upgrade_handle(void)由 midware/meter/meter.c:5742、:5756、:5761 调用
u8 get_percent_upgrade_meter(void)由 midware/meter/meter.c:6848、:6861、:6866 调用
u32 get_upgrade_file_offset_meter(void)由 net/ems.c:6711 调用
METER_UPGRADE_MAX_DATA宏,实现文件用于分块与缓冲尺寸设计
METER_UPGRADE_STATE_E、METER_UPGRADE_CMD_E、METER_UPGRADE_T类型定义

使用的外部符号(本文件自身依赖)

名称来源
includes.h:13 include;提供 u8/u16/u32/s32 等基础类型与工程公共声明

6. 依赖关系

7. 备注与风险点

  1. METER_BUF_LEN(1024) 与 METER_UPDATE_VALID_LEN(500) 是全工程无引用的死宏——极易误导:读者看到 METER_BUF_LEN = 1024 会以为接收缓冲有 1024 字节,实际实现里的接收缓冲只有 11 字节(meter_upgrade.c:24)且不校验长度上限,是一个真实的溢出隐患。建议删除或在头文件中显式声明缓冲尺寸常量。
  2. 缓冲尺寸常量未在头文件定义——METER_UPGRADE_MAX_DATA(250) 与实现文件的 s_meter_upgrade_send_buf[306]、s_meter_upgrade_recv_buf[11] 之间的尺寸约束只存在于实现文件中,头文件没有提供"单帧最大缓冲长度"这样的宏来集中约束,修改 250 时无法从接口层面得到提示。
  3. u16_length 字段名与类型不符——声明为 u32 却叫 u16_length;同一工程 METER_UPGRADE_T 中 u8_upgrade_file_buf 是指针、u32_sequno 命名正常,只有这个字段前缀错位,属历史遗留。
  4. 状态机与命令码的两套枚举都不做类型检查——METER_UPGRADE_STATE_E 与 METER_UPGRADE_CMD_E 的类型名在工程中从未被引用(u8_upgrade_state 是 u8、meter_upgrade_send_code 的形参是 u8 code),枚举退化为常量集合,编译器无法阻止越界状态值;实现文件只能靠 default 分支兜底。
  5. CHNT_WIRELESS_UPDATE_POP 语义可疑——宏名带 POP(出栈)但实现是把它当"子节点在位掩码"传给入栈函数 chnt_wireless_add_update_code();形参名 u8_module_child_nodes_state 与宏名不匹配,注释只说"按位域存储",没有解释为什么是常量 7(而不是运行期探测到的实际在位节点)。当前行为是无条件升级 AP + STA1 + STA2 三个节点。
  6. 错误门限未在头文件表达——5 次(进 Boot)/ 3 次(其余)的门限是"多久放弃"这一关键行为,却完全硬编码在实现文件里;从接口使用者的角度看,meter_upgrade_handle() 可能最长阻塞(5+3+3+3×N+3)次收发时间,没有可查的契约。
  7. 无错误码输出接口——本文件只导出"进度百分比"与"已发偏移"两个观测点,没有任何"升级失败原因/失败设备地址"的查询接口。升级失败时只把 UPDT_Meter 标志清零,云端与 HMI 无从得知具体环节(是进 Boot 失败、擦除失败还是写 Flash 失败),现场排障只能依靠串口日志(且 DEBUG_METER_UPDATE 默认为 0)。
  8. u8_err_cnt 不加注释的语义——该字段在实现中对不同状态有不同含义的门限(>5 或 >3),头文件没有标注。
  9. #pragma pack(1) 与 #pragma pack() 无参数成对使用——Keil ARMCC 支持,跨编译器行为不同;本工程固定 Keil MDK 编译,可接受,但同工程其它头文件(如 diesel_generator.h)完全不使用 pack,风格不统一。
  10. METER_UPGRADE_T 内混用"目标侧"与"源侧"偏移——u32_addr_flash 是电表侧地址、u32_addr_flash_offset 是源侧(SDRAM)偏移,两者名字极为相似且都带 addr_flash,仅靠 _offset 后缀区分。头文件没有区分性注释,是实现者容易混用的点。