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

goodwe_upgrade.h

源文件路径:midware/inverter/goodwe/goodwe_upgrade.h 所属模块:midware/inverter/goodwe(古瑞瓦特 GoodWe 逆变器固件升级) 代码行数:54

1. 文件职责

本头文件是 GoodWe 逆变器固件升级模块的对外契约,定义升级状态机的状态枚举、三套升级协议用到的应答码与包长常量,以及 8 个导出函数原型。它把「bootloader 魔数命令 FlashDSP」「应答码 0x06/0x15/0x01」「包长 64/512」「扇区 4096」「超时 1500ms」这些散落在 goodwe_upgrade.c 里的魔法数字集中到一处,使协议参数可读、可改。文件被 goodwe_comm.h:14 包含,因此 goodwe_comm.c 能直接调用两个升级顶层状态机与 6 个进度/标志查询函数,而不需要额外 include。

2. 关键数据结构与宏

名称类型说明
GOODWE_UPGRADE_STATE_E枚举升级状态机状态(goodwe_upgrade.h:34)。GW_UPGRADE_NONE=0 空闲、GW_UPGRADE_MIDDLE=1 从机完成待转主机、GW_UPGRADE_MASTER=2 主机 DSP 升级中、GW_UPGRADE_SLAVE=3 从机升级中、GW_UPGRADE_EXTERNAL=4 外部 ARC 升级中、GW_UPGRADE_INFO_INIT=5 收尾(清云端更新标志)
GOODWE_INV_UPGRADE_DEBUG宏(0),升级调试总开关,控制 goodwe_upgrade.c 中带条件编译的 printf
RESTART_FLASH_MODE宏(0x15),旧协议应答:逆变器正在重启进入 FLASH 模式
UPGRADE_REPLY_OK宏(0x06),旧/新协议通用成功应答
UPGRADE_WRITE_SECTION_FAULT宏(0x01),旧协议应答:本 4096 字节扇区写入失败,需回退重传
UPGRADE_DSP_ENTER_BOOT宏字符串 "FlashDSP",让主机 DSP 进入 bootloader 的裸 ASCII 命令(无帧头、无长度、无校验)
UPGRADE_SECTION_LEN宏(4096),FLASH 扇区大小,写失败回退的粒度
UPGRADE_PACK_LEN宏(64),旧协议(主机/从机)每包固件字节数
UPGRADE_PACK_LEN_NEW宏(512),新协议(外部 ARC)每包固件字节数。在 goodwe_upgrade.h:23 与 :30 被重复定义两次,内容相同
UPGRADE_HEADER_LEN宏(32),固件镜像头部长度,首包固定只发 32 字节
UPGRADE_HEADER_LEN_NEW宏(32),新协议头部长度。定义了但从未使用
UPGRADE_RECV_TIMEOUT宏(1500),旧协议单包应答等待上限(毫秒)
UPGRADE_TYPE_DSP宏(0xFF01),新协议主机类型字。定义了但从未使用
UPGRADE_TYPE_ARM宏(0xFF02),新协议 ARM 类型字。定义了但从未使用
UPGRADE_TYPE_ARC宏(0xFF03),新协议 ARC 模块类型字,唯一实际使用的类型值

3. 函数清单

本文件为头文件,不含函数定义,共声明 8 个对外函数原型(goodwe_upgrade.h:45-52):

函数名作用域说明
goodwe_upgrade_handle全局(导出声明)内部升级(UPDT_INV1 + UPDT_INV2)顶层状态机,每轮调用推进一个包
goodwe_upgrade_external_handle全局(导出声明)外部升级(UPDT_INV3)顶层状态机,每轮调用推进一个包
goodwe_read_upgrade_percent_master全局(导出声明)返回主机 DSP 升级进度百分比(0~100)
goodwe_read_upgrade_flag_master全局(导出声明)返回主机 upgrade 进行中标志(u8,当布尔用)
goodwe_read_upgrade_flag_slave全局(导出声明)返回从机 upgrade 进行中标志
goodwe_read_upgrade_percent_slave全局(导出声明)返回从机升级进度百分比
goodwe_read_upgrade_flag_external全局(导出声明)返回外部 ARC upgrade 进行中标志
goodwe_read_upgrade_percent_external全局(导出声明)返回外部 ARC 升级进度百分比

本文件不导出任何全局变量;所有状态变量(s_u8_upgrade_state 与三个偏移量)在 goodwe_upgrade.c:21-24 中定义为 static,外部无法访问,只能通过上述 8 个访问函数读取。

4. 核心逻辑解析

头文件本身无执行逻辑,本节说明它定义的三套协议约定与状态语义,这些约定直接决定 goodwe_upgrade.c 的行为。

4.1 状态枚举的数值区间设计

GOODWE_UPGRADE_STATE_E 的枚举顺序不是流程顺序,而是按「主机侧 / 从机侧 / 收尾」分成两段,并且这一划分被三个 flag 查询函数用区间比较直接依赖:

枚举值:  NONE(0)  MIDDLE(1)  MASTER(2)  |  SLAVE(3)  EXTERNAL(4)  INFO_INIT(5)
         └──────── 空闲 ────────┘         └──── 主机阶段 ────┘    └── 从机阶段 ──┘

goodwe_read_upgrade_flag_master :  state != NONE && state <= MASTER(2)  →  {MIDDLE, MASTER}
goodwe_read_upgrade_flag_slave  :  state != NONE && state >= SLAVE(3)   →  {SLAVE, EXTERNAL, INFO_INIT}
goodwe_read_upgrade_flag_external: state != NONE && state <= EXTERNAL(4)→  {MIDDLE, MASTER, SLAVE, EXTERNAL}

实际流程顺序与枚举顺序相反:goodwe_upgrade_handle() 的迁移是 NONE → SLAVE → MIDDLE → MASTER → INFO_INIT → NONE,即先升从机、再升主机。因此「MIDDLE 状态」的真实含义是「从机已升级完成,正在等待切换到主机流程」,名字上看不出这层含义。

GW_UPGRADE_INFO_INIT(值 5,最大)语义是收尾清理:进入后立即调用 Set_Status_Is_Updated_From_Server(UPDT_INV1/UPDT_INV2/UPDT_INV3, FALSE) 清掉云端更新标志,然后回到 GW_UPGRADE_NONE。它既被用作「全部完成」的终点,也被用作「连续失败太多、放弃」的终点。

4.2 三套协议参数的分工

协议状态固件源长度来源包长首包头应答码应答超时失败阈值
从机(旧)GW_UPGRADE_SLAVEg_u8_fw_sdram2UPDT_INV2UPGRADE_PACK_LEN = 64UPGRADE_HEADER_LEN = 32单字节 0x06/0x15/0x01UPGRADE_RECV_TIMEOUT = 1500ms30 次
主机 DSP(旧)GW_UPGRADE_MASTERg_u8_fw_sdram1UPDT_INV16432(代码里写成字面量 32)同上1500ms30 次(boot 命令阶段同样 30 次)
外部 ARC(新)GW_UPGRADE_EXTERNALg_u8_fw_sdram1UPDT_INV3UPGRADE_PACK_LEN_NEW = 51232帧内 buf[10],比对 UPGRADE_REPLY_OK400ms(代码字面量)3 次

可看出三套协议共用了 UPGRADE_HEADER_LEN、UPGRADE_REPLY_OK 两个宏,但应答超时与失败阈值没有抽象成宏:UPGRADE_RECV_TIMEOUT 只覆盖旧协议,外部协议的 400ms 与 3 次阈值都硬编码在 goodwe_upgrade.c:552/566、:599/612。这是本头文件「参数集中化」目标未彻底完成的地方。

4.3 RESTART_FLASH_MODE 与 UPGRADE_WRITE_SECTION_FAULT 的语义差别

两个非成功应答码的处理方式完全不同,是升级流程容错的核心:

4.4 应答码的「值即协议」约定

三个应答码都是裸 8 位值,没有独立的枚举类型,也没有「成功/重试/终止」的语义分类。调用方只能逐个 if/else if 比较(goodwe_upgrade.c:303、:350、:355、:369、:415、:444、:488、:493、:507、:554、:600)。要新增一个应答码(例如「校验失败,重发本包」)必须在四个状态机里各改一处,且没有编译期保护——漏改一处会落到 else 分支被当作普通超时计数,而不是被识别为新状态。

4.5 与 net 层枚举的绑定

头文件本身不定义更新类型,升级协议与云端下载的绑定通过 net/server.h:190-192 的 ENUpdateType 实现:UPDT_INV1(主机 DSP)、UPDT_INV2(从机)、UPDT_INV3(外部 ARC)。本头文件的三个状态、三个固件长度来源、三个进度百分比函数都是围绕这三个枚举值一一对应的,因此修改 ENUpdateType 的枚举顺序或插入新值会直接破坏升级模块——这类跨模块的隐式依赖在头文件里没有任何注释提示。

5. 对外接口

本文件导出

本文件引用的外部头文件

6. 依赖关系

依赖:includes.h(基础类型)。include guard 为 __GOODWE_UPGRADE_H__(goodwe_upgrade.h:9-10)。注意本头文件不包含 server.h 或 upd_download.h,因此不引入 ENUpdateType;该枚举由 goodwe_upgrade.c:16 自行 include server.h 获得。

被依赖:

7. 备注与风险点

  1. 注释头完全空置(goodwe_upgrade.h:3-6):@version、@date、@brief 均为空,无法判断该模块的引入时间与版本兼容范围;相比 goodwe_comm.h 至少有版本号,本文件的历史信息更少。
  2. UPGRADE_PACK_LEN_NEW 重复定义(goodwe_upgrade.h:23 与 :30):两处均为 (512),内容一致所以编译不报警(C 标准允许完全相同宏的重复定义),但这是明显的编辑残留,且第 30 行紧贴 UPGRADE_HEADER_LEN_NEW,说明「新协议参数组」被追加时忘了删除旧的一行。
  3. 三个宏定义了但从未使用:UPGRADE_TYPE_DSP、UPGRADE_TYPE_ARM、UPGRADE_HEADER_LEN_NEW(已用全仓 grep 确认无引用)。它们的存在会误导读者以为新协议同时支持 DSP/ARM/ARC 三种目标,实际只有 ARC。
  4. UPGRADE_DSP_ENTER_BOOT 是字符串字面量宏而非数组:调用方用 sizeof(UPGRADE_DSP_ENTER_BOOT) - 1 取长度(goodwe_upgrade.c:410),因此只能用 sizeof 方式取长度,若误用 strlen 在无 string.h 声明的环境下会返回 int 隐式声明;同时 sizeof 依赖内容里没有隐式 \0 之外的转义,扩展字符串时需谨慎。
  5. UPGRADE_RECV_TIMEOUT 只覆盖一部分协议:头文件把它定义为「旧协议应答超时」,但外部协议的 400ms 与 3 次失败阈值都写死在 .c 里,参数集中化的目标未达成。若逆变器固件调整应答时间,改动点分散在头与实现两处,容易漏改。
  6. 应答码缺语义分类:RESTART_FLASH_MODE/UPGRADE_REPLY_OK/UPGRADE_WRITE_SECTION_FAULT 是三个裸数值宏,没有「导致重试」与「导致终止」的分组。goodwe_upgrade.c 的四个状态机对同一批应答码各写一份 if/else,新增应答码需要四处同改,缺少编译期检查。
  7. 状态枚举命名与流程顺序不符:GW_UPGRADE_MIDDLE 实际含义是「从机完成待切主机」,GW_UPGRADE_INFO_INIT 实际含义是「收尾清云端标志」。两者名字都看不出真实语义,是本模块最容易误读的部分。
  8. GW_UPGRADE_INFO_INIT 语义重载:它既是「全部成功」的终点,也是「连续失败 30 次/3 次后放弃」的终点。由于两者都执行「清云端更新标志」,外部(服务器、HMI)无法区分升级成功与放弃,失败在上层不可见。
  9. goodwe_read_upgrade_flag_external 的判据过宽(与头文件同名的语义承诺不符):其实现 state != NONE && state <= EXTERNAL 覆盖 {MIDDLE, MASTER, SLAVE, EXTERNAL},包含主机/从机升级状态;而主机标志 state <= MASTER、从机标志 state >= SLAVE 恰好互补。三个 flag 函数之间没有一致的区间划分规则,外部若不理解这一点会误报「外部升级中」。
  10. 所有进度函数未做除零保护(见 goodwe_upgrade.c:239/261/283):头文件声明了这三个函数但未在注释中说明前置条件(文件长度必须非 0)。调用者 inverter.c:3859/3929/4007 也未做保护,若服务器下发更新标志而下载尚未完成,会出现除零。建议在头文件注释中明确「仅在对应的更新标志为真时才可调用」。
  11. 无版本兼容标注:新协议(0xAA55 头 + 512 字节包 + 索引)与旧协议(裸流 + 64 字节包)在同一个状态机里共存,但没有宏或注释说明「哪些逆变器固件版本走哪套协议」;选择完全由 ENUpdateType(INV2 vs INV3)隐式决定,这在阅读时极难推断。