afci_upgrade.h
TIME_ELAPSED() 宏的语义5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/afci/afci_upgrade.h所属模块:midware / afci(电弧故障断路器 AFCI OTA 升级) 代码行数:61
1. 文件职责
本头文件定义 AFCI OTA(Over-The-Air)升级通道的协议常量与状态机契约:OTA 专用从站地址 0xF8、5 个命令字、单包数据长度 128、进入升级模式的寄存器地址,以及升级状态枚举 AFCI_UPGRADE_SATE_E 和升级上下文结构体 AFCI_UPGRADE_TABLE_T。
它同时把 4 个供 HMI 与云端查询升级进度的函数原型对外暴露,使 hmi/ui/ui_main.c(显示进度)、net/ems.c(上报进度)无需包含 afci_upgrade.c 的实现细节。
文件还提供 TIME_ELAPSED() 宏——这是工程内少数对 OSTimeGet() 回绕安全的超时比较写法。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
__AFCI_UPGRADE_H__ | 宏 | :9-10。包含守卫 |
AFCI_UPGRADE_ADDRESS | 宏 | :16,值 0xF8。OTA 通道从站地址。与 AFCI_SLAVR_ADDRESS(0x01)(常规)、ALPHAESS_SLAVR_ADDRESS(0x03)(逆变器)一起构成 USART2 上的三重寻址;alphaess_comm_usart.c:403 专门为 0x01 / 0xF8 两个首字节做 CRC 校验分支 |
EMS_AFCI_SHAKE_HANDS | 宏 | :18,值 0x01。握手帧命令字,携带总包数与包大小 |
EMS_AFCI_SEND_UPGRADE_TRANSMIT | 宏 | :19,值 0x02。固件数据帧命令字 |
EMS_AFCI_UPGRADE_FINISH | 宏 | :20,值 0x03。结束帧命令字 |
EMS_AFCI_EXIT_OTA | 宏 | :21,值 0x04。退出 OTA 模式。全工程无使用者(.c 中未组帧、无调用) |
EMS_AFCI_RESTART_DEVICE | 宏 | :22,值 0x05。重启设备。全工程无使用者;失败路径实际走的是常规寄存器软重启 set_afci_soft_reboot()(afci_comm.c:623) |
AFCI_UPGRADE_DATA_MAX_LEN | 宏 | :24,值 128。单包固件字节数上限,同时作为握手/结束帧里上报的“包大小”字段值 |
AFCI_ADDR_REG_VERSION_UPDATA_ENABLE / AFCI_NUM_VERSION_UPDATA_ENABLE | 宏 | :26-27。寄存器 0x0023,长度 1。写 1 = 进入升级模式(通过标准 Modbus 0x06 帧下发) |
TIME_ELAPSED(start, ms) | 宏 | :29。((OSTimeGet() - (start)) > (ms))。OSTimeGet() 返回 u32 毫秒计数,无符号相减再比较,对 49.7 天计时器回绕安全;与同文件 afci_upgrade.c:123 使用的 abs() 写法形成对比 |
AFCI_UPGRADE_SATE_E | 枚举 | :33-40(枚举名 SATE 为 STATE 拼写错误)。升级状态机 5 态,隐式取值 0~4:AFCI_UPGRADE_START(0,进入 OTA 模式) / AFCI_UPGRADE_HANDSHAKE(1) / AFCI_SEND_UPGRADE_FILE(2) / AFCI_UPGRADE_END(3) / AFCI_UPGRADE_FAULT(4) |
AFCI_UPGRADE_TABLE_T | 结构体 | :44-51,用 #pragma pack(1)(:43/:54)紧凑打包,无填充字节。升级上下文,见下方字段表 |
AFCI_UPGRADE_TABLE_T 字段表
| 字段 | 类型 | 说明 |
|---|---|---|
u8_upgrade_file_buf | u8* | 固件源数据指针,运行期被赋为 g_u8_fw_sdram1(afci_upgrade.c:261),即 SDRAM 3 MB 固件区 |
u16_send_file_num | u16 | 总包数 = ceil(文件长度 / 128),由 afci_upgrade.c:272-274 计算;握手帧与结束帧都要上报它 |
u8_upgrade_state | u8 | 状态机当前态,取 AFCI_UPGRADE_SATE_E 值;memset 后为 0 = AFCI_UPGRADE_START |
u32_upgrade_file_len | u32 | 固件文件总长,来自 get_length_file_updating(UPDT_AFCI) |
u32_addr_flash_offset | u32 | 当前包在固件中的偏移,同时被 get_percent_upgrade_afci() 与 get_upgrade_file_offset_afci() 用作进度分子 |
字段声明顺序上 u8_upgrade_state(会话状态)排在第 3 位、夹在两个数据字段之间,可读性略差;#pragma pack(1) 在此结构体上并没有必要(成员本就能自然对齐,无跨字节字段需要紧凑),属于从别的升级模块沿袭的写法。
3. 函数清单
本文件不定义函数,只声明以下 4 个原型(全部定义于 afci_upgrade.c):
| 函数名 | 作用域 | 说明 |
|---|---|---|
afci_upgrade_handle | 全局声明 | :55。OTA 主状态机,单次调用推进一步 |
get_percent_upgrade_afci | 全局声明 | :56。返回升级进度百分比 |
get_upgrade_file_offset_afci | 全局声明 | :57。返回当前已传偏移(字节) |
is_upgrade_afci | 全局声明 | :58。返回是否正在向 AFCI 传固件 |
声明缺口:afci_upgrade.c:26 定义的全局变量 s_afci_upgrade_tab 未在本头文件声明(既未 extern 也未改为 static),外部无法正常引用;.c 中的 5 个 static 组帧/等待函数也刻意不对外暴露,封装方向是正确的。
4. 核心逻辑解析
4.1 协议帧格式(由本文件的常量确定)
握手帧 (8B) [0xF8][0x01][总包数_H][总包数_L][0x00][0x80][CRC_L][CRC_H]
数据帧 (≤136B) [0xF8][0x02][序号_H][序号_L][本包长度_H][本包长度_L][数据…][CRC_L][CRC_H]
结束帧 (8B) [0xF8][0x03][总包数_H][总包数_L][0x00][0x80][CRC_L][CRC_H]
升级模式帧 (8B) [0x01][0x06][0x00][0x23][0x00][0x01][CRC_L][CRC_H] // 标准 Modbus 写单寄存器
应答有效性判据(afci_upgrade.c:140-145)
buf[0] == 0xF8 && buf[1] ∈ {0x01, 0x02, 0x03} && buf[2..5] 不全为 0
CRC 为 CRC16-Modbus,低字节在前,覆盖除 CRC 自身外的全部字节。AFCI_UPGRADE_DATA_MAX_LEN = 128 决定帧长与发送缓冲尺寸:最大帧 = 6(地址+命令+序号+长度)+ 128(数据)+ 2(CRC)= 136 字节,落在 s_afci_upgrade_send_buf[150](afci_upgrade.c:29)之内。
4.2 状态机与状态迁移条件
AFCI_UPGRADE_SATE_E 的 5 个状态与迁移条件(实现见 afci_upgrade.c:251-480):
START ──写 0x0023 成功──────────────► HANDSHAKE
└─失败×4 → FAULT
HANDSHAKE ──5000ms 内收到有效应答──► SEND_UPGRADE_FILE
└─失败×4 → FAULT
SEND_UPGRADE_FILE ──中间包成功─────► SEND_UPGRADE_FILE(自环,send_cnt++)
──末包成功───────► END
└─失败×4 → FAULT
END ──500ms 内收到有效应答────────► START(memset 全部上下文 + 清服务器标志)
└─失败×4 → FAULT
FAULT ──首次进入:软重启 AFCI ────► 等 5s
──5s 到,轮数 ≤ 2 → START(重新来一轮)
└─5s 到,轮数 > 2 → memset 上下文 + 清服务器标志(退出,不再被调用)
阈值常量全部以字面量写在 .c 中:帧级重试上限 > 3(即 4 次)、轮级上限 > 2(即 3 轮)、故障等待 5000 ms、握手/结束应答超时 5000/500 ms。它们没有在本头文件中定义成宏,是本模块最需要抽取的常量集。
4.3 状态值与外部标志的耦合
u8_upgrade_state 为 0 时即 AFCI_UPGRADE_START,而 memset(&s_afci_upgrade_tab, ...) 正好把状态复位为 0 —— 因此「成功收尾」afci_upgrade.c:401-402 与「整轮放弃」:449 都借 memset 完成状态复位,:402 后又显式赋了一次 AFCI_UPGRADE_START,属冗余但无害。
真正决定"是否还会被再次调用"的不在状态机里,而是两个服务器侧标志:
| 函数 | 底层字段 | 置位方 | 清除方 |
|---|---|---|---|
is_upgrade_afci() → Get_Status_Is_Updated_From_Server(UPDT_AFCI) | g_stEMSInfo.stUpt[UPDT_AFCI].bIsUpdatedFromServer | net/upd_download.c:412(固件 MD5 校验通过) | afci_upgrade.c:405(END 成功)、:451(放弃) |
get_flag_force_update(UPDT_AFCI) | 同上表项的 bIsUpdateForce | 云端特殊命令(net/server.c:2408) | afci_upgrade.c:406(END 成功)、:452(放弃) |
因为 afci_upgrade_handle() 的调用点 alphaess_comm_usart.c:5444 以 is_afci_firm_download_finished() 为门,所以 END 成功与放弃两条路径都会自然终止流程,不存在"状态机停在 FAULT 里空转"的情况。
4.4 TIME_ELAPSED() 宏的语义
#define TIME_ELAPSED(start, ms) ((OSTimeGet() - (start)) > (ms))
OSTimeGet() 返回 uCOS-II 的 32 位毫秒计数(约 49.7 天回绕一次)。无符号减法天然处理回绕:只要 start 到当前时刻间隔小于 2³² ms,结果就是正确的时间差。因此本宏比工程内常见的 abs(OSTimeGet() - start) > ms 写法更正确(后者把无符号差塞进 int 形参,超过 INT_MAX 时结果错误)。
调用点只有一处:afci_upgrade.c:438(FAULT 态等 5 秒)。同文件 :123 的 abs() 写法与注释掉的 :437 旧写法都未被替换,说明本宏是后加的修正。使用注意:start 为 0(初值)时 OSTimeGet() - 0 会立刻大于 ms,因此不能拿 0 当"未开始"哨兵;afci_upgrade.c:430 正是先判 s_u32_fault_tick == 0 再赋值,才避免了误判。
5. 对外接口
本文件导出:
- 4 个函数原型(见第 3 节),实际调用方:
afci_upgrade_handle←midware/inverter/alphaess/alphaess_comm_usart.c:5447is_upgrade_afci←hmi/ui/ui_main.c:907get_percent_upgrade_afci←hmi/ui/ui_main.c:913get_upgrade_file_offset_afci←net/ems.c:6725
- 宏常量
AFCI_UPGRADE_ADDRESS(0xF8)也被alphaess_comm_usart.c:403与alphaess_afci_com_reply的判定逻辑使用(跨文件的协议耦合点);AFCI_UPGRADE_DATA_MAX_LEN由afci_upgrade.c单独使用。 - 不导出:
s_afci_upgrade_tab(定义于.c且未extern),以及 5 个static函数。
本文件调用的外部符号:仅 includes.h(:13),与 afci_comm.h 的做法一致,会连带引入 uCOS-II、GD32 固件库、标准库等全部基础依赖。
6. 依赖关系
依赖:includes.h(:13)。
被依赖(include afci_upgrade.h 的文件):
midware/afci/afci_upgrade.c:16midware/inverter/alphaess/alphaess_comm_usart.c:32net/ems.c:58(云端升级进度上报)hmi/ui/ui_main.c:33(主界面进度显示)
间接依赖链:alphaess_comm_usart.h:14 include 了 alphaess_upgrade.h,而 afci_upgrade.c:14 include 了 alphaess_comm_usart.h,因此 ALPHAESS_UPGRADE_DEBUG(alphaess_upgrade.h:16)在本模块内可见——这是一个跨文件、跨模块的隐式调试开关传递,仅靠头文件包含顺序成立。
7. 备注与风险点
- 两个命令字定义了但全工程无人使用:
EMS_AFCI_EXIT_OTA(0x04)(:21)与EMS_AFCI_RESTART_DEVICE(0x05)(:22)。失败恢复走的是常规寄存器软重启(afci_comm.c:623),OTA 通道上的重启/退出命令从未下发。这两条协议路径是否已在 AFCI 固件中实现、是否需要使用,代码层面无法确认,属未验证的协议面。 AFCI_UPGRADE_SATE_E枚举名拼写错误::40与其上注释写的是AFCI_UPGRADE_SATE_E(应为STATE)。该名称已被.c使用,改名需同步。- 状态机阈值未抽象为宏:帧级 4 次、轮级 3 次、5 s 等待、5000/500 ms 超时都是硬编码字面量散落在
afci_upgrade.c中。本头文件已是天然的常量归属地,把>3/>2/5000提取成宏可显著降低误改风险。 AFCI_ADDR_REG_VERSION_UPDATA_ENABLE拼写错误:UPDATA应为UPDATE;同一团队在afci_comm.h:66的AFCI_LOG_UPDATA_STATE_E沿用相同错拼,属习惯性笔误,不影响功能但影响检索。s_afci_upgrade_tab缺少声明:.c里定义了一个非static全局变量却没有extern声明,形成"外部不可见但占据全局符号"的中间态,建议改static(见afci_upgrade.c风险点 8)。#pragma pack(1)在此结构体上没有必要:AFCI_UPGRADE_TABLE_T的成员原本就能自然对齐(u8*+u16+u8+u32+u32),加pack(1)只是把u8后面的填充去掉,不会改变数据区布局语义;但它会让结构体尺寸与"自然对齐"版本不同(sizeof从 16 变 14),若将来有任何地方对它与别的类型做二进制转换/存储会比较危险。当前该结构体只在 RAM 中使用,风险低。TIME_ELAPSED的零值陷阱未在注释中提醒:宏名中的start若为 0(未初始化),立即返回 TRUE。头文件未给出使用说明,调用方需要自行保证"哨兵不为 0"(afci_upgrade.c:430做到了,但这是隐含约定)。- 宏命名前缀混用:
EMS_AFCI_*用于命令字,AFCI_UPGRADE_*/AFCI_ADDR_REG_*用于地址与长度。同一功能域三套前缀(还有afci_comm.h的AFCI_SLAVR_ADDRESS),缺少统一命名规范。