goodwe_upgrade.h
RESTART_FLASH_MODE 与 UPGRADE_WRITE_SECTION_FAULT 的语义差别4.4 应答码的「值即协议」约定4.5 与 net 层枚举的绑定5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
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_SLAVE | g_u8_fw_sdram2 | UPDT_INV2 | UPGRADE_PACK_LEN = 64 | UPGRADE_HEADER_LEN = 32 | 单字节 0x06/0x15/0x01 | UPGRADE_RECV_TIMEOUT = 1500ms | 30 次 |
| 主机 DSP(旧) | GW_UPGRADE_MASTER | g_u8_fw_sdram1 | UPDT_INV1 | 64 | 32(代码里写成字面量 32) | 同上 | 1500ms | 30 次(boot 命令阶段同样 30 次) |
| 外部 ARC(新) | GW_UPGRADE_EXTERNAL | g_u8_fw_sdram1 | UPDT_INV3 | UPGRADE_PACK_LEN_NEW = 512 | 32 | 帧内 buf[10],比对 UPGRADE_REPLY_OK | 400ms(代码字面量) | 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 的语义差别
两个非成功应答码的处理方式完全不同,是升级流程容错的核心:
UPGRADE_WRITE_SECTION_FAULT(0x01):只回退一个扇区。若当前偏移大于UPGRADE_SECTION_LEN + UPGRADE_HEADER_LEN(4096+32),则offset = offset + 本包长 - 4096,即退回刚写完的那个 4096 字节扇区起点重发;否则从头开始(offset = 0)。这依赖逆变器 bootloader 报错时「只坏一个扇区、更早的扇区仍有效」的前提。RESTART_FLASH_MODE(0x15):整包流程重来。三个upgrade_*_handle()都在这个分支把offset、包长、失败计数清零并把状态设回自身(自赋值空迁移)。对主机流程而言这尤其危险——boot_code_flag仍为TRUE,重传时不会再发FlashDSP,若逆变器重启后已退出 bootloader,后续所有包都会失败。
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. 对外接口
本文件导出
- 8 个函数原型:
goodwe_upgrade_handle、goodwe_upgrade_external_handle、goodwe_read_upgrade_percent_master、goodwe_read_upgrade_flag_master、goodwe_read_upgrade_flag_slave、goodwe_read_upgrade_percent_slave、goodwe_read_upgrade_flag_external、goodwe_read_upgrade_percent_external。 - 1 个枚举:
GOODWE_UPGRADE_STATE_E(供阅读者理解状态语义,工程内实际无外部直接引用枚举成员)。 - 全部协议宏(
RESTART_FLASH_MODE、UPGRADE_REPLY_OK、UPGRADE_WRITE_SECTION_FAULT、UPGRADE_DSP_ENTER_BOOT、UPGRADE_SECTION_LEN、UPGRADE_PACK_LEN、UPGRADE_PACK_LEN_NEW、UPGRADE_HEADER_LEN、UPGRADE_HEADER_LEN_NEW、UPGRADE_RECV_TIMEOUT、UPGRADE_TYPE_DSP/ARM/ARC)。 - 不导出全局变量。
本文件引用的外部头文件
includes.h(goodwe_upgrade.h:13):项目总头文件,提供u8/u16/u32等基础类型定义,本头文件的原型签名(u8)依赖它。
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 获得。
被依赖:
midware/inverter/goodwe/goodwe_upgrade.c:13—— 唯一实现者。midware/inverter/goodwe/goodwe_comm.h:14—— 主动 include 本头文件,使goodwe_comm.c可以调用goodwe_upgrade_handle()/goodwe_upgrade_external_handle()/goodwe_read_upgrade_flag_master()/goodwe_read_upgrade_flag_slave()(goodwe_comm.c:2460-2479)。- 间接:
midware/inverter/inverter.c:21include 了goodwe_comm.h,因此也拿到本头文件的全部宏;inverter.c:3824/3859/3894/3929/3980/4007通过其中的goodwe_read_upgrade_*函数向服务器上报进度。
7. 备注与风险点
- 注释头完全空置(
goodwe_upgrade.h:3-6):@version、@date、@brief均为空,无法判断该模块的引入时间与版本兼容范围;相比goodwe_comm.h至少有版本号,本文件的历史信息更少。 UPGRADE_PACK_LEN_NEW重复定义(goodwe_upgrade.h:23与:30):两处均为(512),内容一致所以编译不报警(C 标准允许完全相同宏的重复定义),但这是明显的编辑残留,且第 30 行紧贴UPGRADE_HEADER_LEN_NEW,说明「新协议参数组」被追加时忘了删除旧的一行。- 三个宏定义了但从未使用:
UPGRADE_TYPE_DSP、UPGRADE_TYPE_ARM、UPGRADE_HEADER_LEN_NEW(已用全仓 grep 确认无引用)。它们的存在会误导读者以为新协议同时支持 DSP/ARM/ARC 三种目标,实际只有 ARC。 UPGRADE_DSP_ENTER_BOOT是字符串字面量宏而非数组:调用方用sizeof(UPGRADE_DSP_ENTER_BOOT) - 1取长度(goodwe_upgrade.c:410),因此只能用sizeof方式取长度,若误用strlen在无string.h声明的环境下会返回int隐式声明;同时sizeof依赖内容里没有隐式\0之外的转义,扩展字符串时需谨慎。UPGRADE_RECV_TIMEOUT只覆盖一部分协议:头文件把它定义为「旧协议应答超时」,但外部协议的 400ms 与 3 次失败阈值都写死在.c里,参数集中化的目标未达成。若逆变器固件调整应答时间,改动点分散在头与实现两处,容易漏改。- 应答码缺语义分类:
RESTART_FLASH_MODE/UPGRADE_REPLY_OK/UPGRADE_WRITE_SECTION_FAULT是三个裸数值宏,没有「导致重试」与「导致终止」的分组。goodwe_upgrade.c的四个状态机对同一批应答码各写一份if/else,新增应答码需要四处同改,缺少编译期检查。 - 状态枚举命名与流程顺序不符:
GW_UPGRADE_MIDDLE实际含义是「从机完成待切主机」,GW_UPGRADE_INFO_INIT实际含义是「收尾清云端标志」。两者名字都看不出真实语义,是本模块最容易误读的部分。 GW_UPGRADE_INFO_INIT语义重载:它既是「全部成功」的终点,也是「连续失败 30 次/3 次后放弃」的终点。由于两者都执行「清云端更新标志」,外部(服务器、HMI)无法区分升级成功与放弃,失败在上层不可见。goodwe_read_upgrade_flag_external的判据过宽(与头文件同名的语义承诺不符):其实现state != NONE && state <= EXTERNAL覆盖{MIDDLE, MASTER, SLAVE, EXTERNAL},包含主机/从机升级状态;而主机标志state <= MASTER、从机标志state >= SLAVE恰好互补。三个 flag 函数之间没有一致的区间划分规则,外部若不理解这一点会误报「外部升级中」。- 所有进度函数未做除零保护(见
goodwe_upgrade.c:239/261/283):头文件声明了这三个函数但未在注释中说明前置条件(文件长度必须非 0)。调用者inverter.c:3859/3929/4007也未做保护,若服务器下发更新标志而下载尚未完成,会出现除零。建议在头文件注释中明确「仅在对应的更新标志为真时才可调用」。 - 无版本兼容标注:新协议(
0xAA55头 + 512 字节包 + 索引)与旧协议(裸流 + 64 字节包)在同一个状态机里共存,但没有宏或注释说明「哪些逆变器固件版本走哪套协议」;选择完全由ENUpdateType(INV2 vs INV3)隐式决定,这在阅读时极难推断。