goodwe_upgrade.c
goodwe_upgrade_handle(内部升级 INV1+INV2)4.3 upgrade_slave_handle 的包粒度状态机4.4 upgrade_master_handle 的两段式状态机4.5 upgrade_external_handle 的新协议状态机4.6 顶层状态机 goodwe_upgrade_external_handle4.7 与 uCOS-II 任务和主通信的配合4.8 状态标志函数的位区间设计5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/inverter/goodwe/goodwe_upgrade.c所属模块:midware/inverter/goodwe(古瑞瓦特 GoodWe 逆变器固件升级) 代码行数:725
1. 文件职责
本文件实现 EMS 把云端下载好的 GoodWe 逆变器固件分包烧录到逆变器内部 FLASH 的完整流程,包含三套互相独立的升级协议:主机 DSP 升级(UPDT_INV1,先发 FlashDSP 命令让 DSP 进入 bootloader)、从机/模块升级(UPDT_INV2,64 字节小包 + 单字节应答)、外部 ARC/AFCI 模块升级(UPDT_INV3,512 字节包 + 0xAA 0x55 头 + 索引 + 累加和)。它由 goodwe_comm.c 的 goodwe_inverter_comm()(goodwe_comm.c:2460-2479)在检测到云端更新标志后每轮调用,是 EMS 数据流的末端环节——把 SDRAM 里的固件镜像逐包推给逆变器,并把进度百分比回传给上层显示。整个模块与主通信共用 UART4,升级期间主通信的常规数据查询全部暂停。
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 收尾(清云端标志) |
s_u8_upgrade_state | static 变量 | GOODWE_UPGRADE_STATE_E 唯一实例,goodwe_upgrade.c:21,整个模块的状态中枢 |
s_u32_slave_file_offset | static 变量 | 从机升级已发送字节偏移,goodwe_upgrade.c:22 |
s_u32_master_file_offset | static 变量 | 主机 DSP 升级已发送字节偏移,goodwe_upgrade.c:23 |
s_u32_external_file_offset | static 变量 | 外部 ARC 升级已发送字节偏移,goodwe_upgrade.c:24 |
GOODWE_INV_UPGRADE_DEBUG | 宏 | (0),升级调试开关(goodwe_upgrade.h:14) |
RESTART_FLASH_MODE | 宏 | 0x15,逆变器应答「正在重启进入 FLASH 模式」 |
UPGRADE_REPLY_OK | 宏 | 0x06,逆变器应答「收到/成功」 |
UPGRADE_WRITE_SECTION_FAULT | 宏 | 0x01,逆变器应答「本扇区写入失败」,需回退一个扇区重传 |
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_RECV_TIMEOUT | 宏 | 1500(ms),旧协议单包应答超时 |
UPGRADE_TYPE_DSP | 宏 | 0xFF01,新协议里的主机类型(未使用) |
UPGRADE_TYPE_ARM | 宏 | 0xFF02,新协议里的 ARM 类型(未使用) |
UPGRADE_TYPE_ARC | 宏 | 0xFF03,新协议里的 ARC 模块类型,实际唯一使用的类型字 |
UPGRADE_HEADER_LEN_NEW | 宏 | 32,新协议头部长度(未使用) |
g_u8_fw_sdram1 / g_u8_fw_sdram2 | 外部数组 | 由 net/server.h:258-259 定义、链接到 SDRAM 固定地址 SDRAM_FIRM1_START_ADDR / SDRAM_FIRM2_START_ADDR,长度 SDRAM_3M;本文件直接从这两个数组取固件镜像,不关心下载过程 |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
check_sum | 全局(仅本文件调用) | 逐字节累加的 16 位校验和,新旧两套协议共用 |
inv_com_send | 全局(仅本文件调用) | 旧协议发送:清 UART4 收缓冲后裸发,无节流、无重试 |
inv_com_reply | 全局(仅本文件调用) | 旧协议收包:轮询等 1 个字节,超时返回 FALSE,成功返回该字节值 |
inv_com_send_new | 全局(仅本文件调用) | 新协议组帧并发送:6 字节头 + type + idx + len + 数据 + 2 字节累加和 |
inv_com_reply_new | 全局(仅本文件调用) | 新协议收包:校验头、校验和、类型字,返回应答帧第 11 字节 |
goodwe_read_upgrade_flag_master | 全局(导出) | 主机升级中标志:状态为 MIDDLE 或 MASTER |
goodwe_read_upgrade_percent_master | 全局(导出) | 主机升级进度百分比 = master_file_offset×100 / UPDT_INV1 文件长度 |
goodwe_read_upgrade_flag_slave | 全局(导出) | 从机升级中标志:状态为 SLAVE/EXTERNAL/INFO_INIT |
goodwe_read_upgrade_percent_slave | 全局(导出) | 从机升级进度百分比 = slave_file_offset×100 / UPDT_INV2 文件长度 |
goodwe_read_upgrade_flag_external | 全局(导出) | 外部升级中标志:状态 ≤ EXTERNAL 且非 NONE |
goodwe_read_upgrade_percent_external | 全局(导出) | 外部升级进度百分比 = external_file_offset×100 / UPDT_INV3 文件长度 |
upgrade_slave_handle | 全局(仅本文件调用) | 从机(UPDT_INV2)分包发送状态机:首包 32 字节头,之后 64 字节/包 |
upgrade_master_handle | 全局(仅本文件调用) | 主机 DSP(UPDT_INV1)升级状态机:先 FlashDSP 进 boot,再首包 32 字节,之后 64 字节/包 |
upgrade_external_handle | 全局(仅本文件调用) | 外部 ARC(UPDT_INV3)新协议分包状态机:32 字节头 + 512 字节/包 + 递增索引 |
goodwe_upgrade_external_handle | 全局(导出) | 外部升级顶层状态机:NONE → EXTERNAL → INFO_INIT → NONE |
goodwe_upgrade_handle | 全局(导出) | 内部升级顶层状态机:NONE → SLAVE → MIDDLE → MASTER → INFO_INIT → NONE |
4. 核心逻辑解析
4.1 两套帧格式
旧协议(主机 DSP + 从机)——裸数据流,无帧结构
这是 GoodWe bootloader 的最小协议:
- 发送:直接
uart4_send_data2(buf, len, timeout),帧内容就是固件字节本身,没有起始码、长度字段、命令字或 CRC。 - 接收:
inv_com_reply()(goodwe_upgrade.c:83)以 10ms 为步长轮询uart4_is_recv_async_data2(),超时(UPGRADE_RECV_TIMEOUT= 1500ms)返回FALSE,收到则返回单个字节:0x06(UPGRADE_REPLY_OK):本包成功,偏移前进;0x15(RESTART_FLASH_MODE):逆变器正在重启,状态机复位重来;0x01(UPGRADE_WRITE_SECTION_FAULT):扇区写失败,偏移回退一个 4096 字节扇区重传;- 其它值:按失败处理,计数
s_u8_file_send_cnt。
主机 DSP 的 bootloader 入口不是协议命令,而是一段纯 ASCII 字符串 "FlashDSP"(UPGRADE_DSP_ENTER_BOOT),sizeof(...) - 1 去掉结尾 \0 后发送,等 0x06 应答表示已进入 boot。
新协议(外部 ARC,UPDT_INV3)——带帧头的流式协议
inv_com_send_new()(goodwe_upgrade.c:118)组装的帧结构:
[0..5] 固定头 0xAA 0x55 0x80 0x7F 0x02 0x29
[6..7] 类型 type(大端),本模块只用 0xFF03 (UPGRADE_TYPE_ARC)
[8..9] 包索引 idx(大端),从 0 起每包 +1
[10..11] 数据长度 len(大端)
[12..] 数据(len 字节)
[末尾2] 累加和(大端:高字节在前)
校验和由 check_sum()(goodwe_upgrade.c:32)计算:对帧头 + 类型 + 索引 + 长度 + 数据(即 CRC 之前的全部字节)做 16 位逐字节累加,结果按大端放在帧尾。这与 goodwe_comm.c 里 Modbus CRC 的小端排布不同,是两套独立协议。
响应帧头与请求帧头不同:inv_com_reply_new()(goodwe_upgrade.c:155)期望 0xAA 0x55 0x7F 0x80 0x02 0xA9——第 4、5 字节互换,第 6 字节 0x29 变 0xA9,即方向位翻转。校验与匹配流程为:长度 ≥ 2 → 读全部字节 → 取末两字节作为期望校验和(buf[len-2]<<8 | buf[len-1])→ 对前 len-2 字节重算 check_sum → 相等则比对 6 字节帧头 → 再比对 [6..7] 的类型字与调用方传入的 type → 全部通过才返回应答帧第 11 字节(buf[10]),否则返回 0。
4.2 顶层状态机 goodwe_upgrade_handle(内部升级 INV1+INV2)
goodwe_upgrade_handle()(goodwe_upgrade.c:674)由 goodwe_inverter_comm() 在 is_inverter_firm_download_finished()(= UPDT_INV1 && UPDT_INV2)为真时每轮调用:
| 当前状态 | 迁移条件 | 动作 | 下一状态 |
|---|---|---|---|
GW_UPGRADE_NONE | UPDT_INV1 && UPDT_INV2 均为真且 set_inv_wait_mode() == TRUE | set_inv_feedin(ON, 0) 关闭馈电 | GW_UPGRADE_SLAVE |
GW_UPGRADE_NONE | 上述条件不满足 | 无 | 保持 GW_UPGRADE_NONE |
GW_UPGRADE_SLAVE | 由 upgrade_slave_handle() 内部决定 | 执行从机分包发送 | 见 4.3 |
GW_UPGRADE_MIDDLE | set_inv_wait_mode() == TRUE | set_inv_feedin(ON, 0) | GW_UPGRADE_MASTER |
GW_UPGRADE_MASTER | 由 upgrade_master_handle() 内部决定 | 执行主机分包发送 | 见 4.4 |
GW_UPGRADE_INFO_INIT | 无条件 | Set_Status_Is_Updated_From_Server(UPDT_INV1, FALSE) + (UPDT_INV2, FALSE) | GW_UPGRADE_NONE |
关键点:从机先升级、主机后升级。状态命名与 goodwe_comm.c 中的版本字段存在语义交叉——goodwe_comm.c:557 把 ARC 模块版本写进 u8_slave_ver,即本模块的「slave」在版本语义上对应 ARC 模块;而 UPGRADE_TYPE_ARC 只在新协议的 external 分支使用。因此可理解为:INV1 = 主机 DSP,INV2 = 从机(旧协议),INV3 = 外部 ARC 模块(新协议),三者在 s_u8_upgrade_state 中通过 SLAVE / MASTER / EXTERNAL 三个状态区分。
4.3 upgrade_slave_handle 的包粒度状态机
upgrade_slave_handle()(goodwe_upgrade.c:292)用 s_u32_slave_file_offset == 0 区分首包与后续包,数据源固定为 g_u8_fw_sdram2,总长度取自 get_length_file_updating(UPDT_INV2):
首包(offset == 0):发 UPGRADE_HEADER_LEN(32) 字节头,300ms 发送超时,等 1500ms 应答。
应答 0x06 (OK) → file_send_cnt=0;offset += 32
应答 0x15 (RESTART) → state 自赋值为 GW_UPGRADE_SLAVE;len=0;cnt=0;offset=0(重来一遍)
其它/超时 → file_send_cnt++;若 >30 → state = GW_UPGRADE_MASTER;len=0;cnt=0;offset=0
后续包(offset > 0):
若 UPDT_INV2 长度 ≤ 0 或 offset >= 总长度
→ state = GW_UPGRADE_MIDDLE;清零;OSTimeDlyHMSM(0,2,0,0) 延时 2 分钟等 ARM 重启(直接 return)
包长 = min(64, 总长度 - offset)
发送 g_u8_fw_sdram2 + offset,len 字节
应答 0x06 (OK) → cnt=0;offset += 本包长
应答 0x01 (扇区写失败) → 若 offset > 4096+32:cnt=0;offset = offset + 本包长 - 4096(回退一整个扇区重传)
否则:cnt=0;len=0;offset=0(从头开始)
应答 0x15 (RESTART) → state 自赋值为 GW_UPGRADE_SLAVE;清零偏移重来
其它/超时 → cnt++;若 >30 → state = GW_UPGRADE_MASTER;清零
「30 次连续失败后跳到主机升级」是一个降级容错设计:从机升级反复不成功时不再死等,直接推进到主机 DSP 流程,避免整个升级流程卡死。但这也意味着从机可能被留在未升级状态而流程仍算走完。
4.4 upgrade_master_handle 的两段式状态机
upgrade_master_handle()(goodwe_upgrade.c:399)比从机多一个前置阶段,由文件内 static s_u8_boot_code_flag / s_u8_boot_code_cnt 控制:
阶段一:进入 bootloader(boot_code_flag == FALSE)
inv_com_send("FlashDSP", 8, 300) → inv_com_reply(1500)
应答 0x06 → boot_code_cnt=0;boot_code_flag=TRUE(进入阶段二)
其它 → boot_code_flag 保持 FALSE;boot_code_cnt++
若 ≥30 → boot_code_cnt=0;state = GW_UPGRADE_INFO_INIT(放弃升级、收尾清标志)
阶段二:分包发送(boot_code_flag == TRUE),数据源 g_u8_fw_sdram1,总长度 get_length_file_updating(UPDT_INV1),逻辑与从机几乎一致,差异有三处:
- 首包固定 32 字节(字面量 32,未用
UPGRADE_HEADER_LEN宏,goodwe_upgrade.c:441); - 首包失败只做
cnt++,超过 30 才回GW_UPGRADE_INFO_INIT并彻底复位(含boot_code_flag = FALSE、boot_code_cnt = 0); - 完成判定分支(
goodwe_upgrade.c:465)同样OSTimeDlyHMSM(0,2,0,0)延时 2 分钟等 ARM 重启; - 收到
RESTART_FLASH_MODE时状态设回GW_UPGRADE_MASTER自身(goodwe_upgrade.c:509)——这是一个空迁移,实际效果是把偏移与计数清零后原地重来,等价于「重新发 FlashDSP」需要boot_code_flag仍为 TRUE,所以实际是从头重传固件而不再发 boot 命令。
4.5 upgrade_external_handle 的新协议状态机
upgrade_external_handle()(goodwe_upgrade.c:540)数据源 g_u8_fw_sdram1,长度 get_length_file_updating(UPDT_INV3),引入 s_u16_index 作为包序号:
首包(offset == 0):
inv_com_send_new(g_u8_fw_sdram1, UPGRADE_TYPE_ARC, 32, index=0, 1000ms)
OSTimeDlyHMSM(0, 0, 3, 0) ← 阻塞 3 秒,注释说"等待 ARM 响应升级"
inv_com_reply_new(UPGRADE_TYPE_ARC, 400)
应答 0x06 → cnt=0;index++;offset += 32
其它 → state = GW_UPGRADE_EXTERNAL(自赋值,无实际迁移);cnt++
若 cnt > 3 → state = GW_UPGRADE_INFO_INIT;index=0;len=0;cnt=0;offset=0
后续包:包长 = min(512, 总长度 - offset),发送后同样等 400ms 应答;0x06 则 index++、offset += 包长;其它则 cnt++,cnt > 3 时整体复位到 GW_UPGRADE_INFO_INIT。完成判定时额外调用 set_inv_wait_mode() 并延时 2 分钟「等 ARM 传输至 AFCI 模块」。
与旧协议的关键差异:外部升级的重试阈值是 3(而非 30),应答窗口是 400ms(而非 1500ms),包长 512(而非 64),并维护递增包索引。3 次失败即整体放弃,容错明显更紧。
4.6 顶层状态机 goodwe_upgrade_external_handle
goodwe_upgrade_external_handle()(goodwe_upgrade.c:633)由 goodwe_inverter_comm() 在 Get_Status_Is_Updated_From_Server(UPDT_INV3) 为真时每轮调用:
| 当前状态 | 迁移条件 | 动作 | 下一状态 |
|---|---|---|---|
GW_UPGRADE_NONE | UPDT_INV3 为真且 set_inv_wait_mode() == TRUE | set_inv_feedin(ON, 0) | GW_UPGRADE_EXTERNAL |
GW_UPGRADE_NONE | 不满足 | 无 | 保持 |
GW_UPGRADE_EXTERNAL | 由 upgrade_external_handle() 决定 | 分包发送 | 见 4.5 |
GW_UPGRADE_INFO_INIT | 无条件 | Set_Status_Is_Updated_From_Server(UPDT_INV3, FALSE) | GW_UPGRADE_NONE |
4.7 与 uCOS-II 任务和主通信的配合
- 调用者:只有
goodwe_comm.c的goodwe_inverter_comm()会调用这两个顶层状态机(goodwe_comm.c:2465和:2479),而goodwe_inverter_comm()又只由task_inverter(midware/inverter/inverter.c:1125)驱动。因此本模块全部运行在task_inverter上下文中,无独立任务、无信号量、无消息邮箱,状态机的所有状态都靠文件内static变量保存。 - 与主通信的总线互斥:升级期间
goodwe_inverter_comm()走的是独立分支,不会调用任何query_*/set_*(除goodwe_comm_init()与写u8_run_mode),所以 UART4 不会被两套协议同时抢占。u32_ems_inv_comm_tick = OSTimeGet()被无条件刷新,是为了让 30 秒通信丢失判定不误触发。 - 阻塞式延时:
inv_com_reply()用 10ms 粒度的OSTimeDlyHMSM轮询;upgrade_*_handle()在完成/放弃时用OSTimeDlyHMSM(0, 2, 0, 0)阻塞 2 分钟,upgrade_external_handle首包还额外阻塞 3 秒。这些阻塞期间task_inverter完全停摆,是必须知晓的时序特性。 - 进度上报:三个百分比函数由
inverter.c转发给服务器(inverter.c:3859/3929/4007),通过inverter_read_upgrade_percent_master/slave/external()之类的包装暴露;goodwe_comm.c:2460-2463则通过goodwe_read_upgrade_flag_master/slave()把t_run_info.u8_run_mode置为cUpdateMaster/cUpdateSlave/cUpdateArm供 HMI 显示。
4.8 状态标志函数的位区间设计
三个 flag 函数用区间判断代替精确枚举比较:
| 函数 | 表达式 | 真值状态集合 |
|---|---|---|
goodwe_read_upgrade_flag_master | state != NONE && state <= MASTER(2) | {MIDDLE(1), MASTER(2)} |
goodwe_read_upgrade_flag_slave | state != NONE && state >= SLAVE(3) | {SLAVE(3), EXTERNAL(4), INFO_INIT(5)} |
goodwe_read_upgrade_flag_external | state != NONE && state <= EXTERNAL(4) | {MIDDLE(1), MASTER(2), SLAVE(3), EXTERNAL(4)} |
区间设计使 master 与 slave 恰好互补且互斥(goodwe_comm.c:2460-2463 的 if/else if 才能正确工作),但 external 的判据包含了 master/slave 状态——inverter.c:3980 用它给服务器报「外部升级中」,在主机/从机升级期间会被误报为外部升级。
5. 对外接口
本文件导出(goodwe_upgrade.h 声明)
| 符号 | 说明 | 调用者 |
|---|---|---|
goodwe_upgrade_handle | 内部升级(INV1+INV2)顶层状态机 | goodwe_comm.c:2465 |
goodwe_upgrade_external_handle | 外部升级(INV3)顶层状态机 | goodwe_comm.c:2479 |
goodwe_read_upgrade_percent_master | 主机进度百分比 | inverter.c:3859 |
goodwe_read_upgrade_flag_master | 主机升级中标志 | inverter.c:3824、goodwe_comm.c:2460 |
goodwe_read_upgrade_flag_slave | 从机升级中标志 | inverter.c:3894、goodwe_comm.c:2462 |
goodwe_read_upgrade_percent_slave | 从机进度百分比 | inverter.c:3929 |
goodwe_read_upgrade_flag_external | 外部升级中标志 | inverter.c:3980 |
goodwe_read_upgrade_percent_external | 外部进度百分比 | inverter.c:4007 |
本文件内部符号(未加 static 但工程内无其它引用)
- 函数:
check_sum、inv_com_send、inv_com_reply、inv_com_send_new、inv_com_reply_new、upgrade_slave_handle、upgrade_master_handle、upgrade_external_handle。 - 变量:
s_u8_upgrade_state、s_u32_slave_file_offset、s_u32_master_file_offset、s_u32_external_file_offset(均为static,仅本文件可见)。
本文件调用的外部函数/变量
- 硬件:
uart4_clear_buffer、uart4_send_data2、uart4_is_recv_async_data2、uart4_read_data_async、uart4_get_data_len(hardware/usart4)。 - 网络/下载:
Get_Status_Is_Updated_From_Server、Set_Status_Is_Updated_From_Server、get_length_file_updating(net/upd_download.h:37-40)。 - 跨模块数据:
g_u8_fw_sdram1/g_u8_fw_sdram2(net/server.h:258-259)。 - 同模块通信:
set_inv_wait_mode()、set_inv_feedin()(goodwe_comm.c)。 - 系统:
OSTimeGet、OSTimeDlyHMSM(uCOS-II)。 - 枚举:
ENUpdateType的UPDT_INV1/UPDT_INV2/UPDT_INV3(net/server.h:190-192)。
6. 依赖关系
依赖(include 列表见 goodwe_upgrade.c:13-19):goodwe_upgrade.h、upd_download.h、inv_download.h、server.h、sdram.h、usart4.h、goodwe_comm.h;其中 server.h 提供 SDRAM 固件镜像数组与 ENUpdateType 枚举,upd_download.h 提供更新标志与文件长度查询。
被依赖:
midware/inverter/goodwe/goodwe_comm.c:14include 本模块头,并在goodwe_inverter_comm()(goodwe_comm.c:2460-2479)中调用两个顶层状态机与两个 flag 查询函数。midware/inverter/inverter.c:3824/3859/3894/3929/3980/4007通过本模块的 6 个read_upgrade_*函数向服务器与 HMI 提供进度与状态(inverter.c本身不直接 includegoodwe_upgrade.h,它通过自身的封装函数转发;goodwe_comm.h:14已把goodwe_upgrade.h传递进来)。- 无其它模块直接引用本文件内部符号。
7. 备注与风险点
inv_com_send_new存在固定栈缓冲越界(goodwe_upgrade.c:120):u8 l_u8_buf[520],但最大包长UPGRADE_PACK_LEN_NEW = 512,写入12 + 512 + 2 = 526字节,越界 6 字节。当前len参数始终 ≤ 512,因此实际会写l_u8_buf[512]与[513](校验和),刚好越界 6 字节但仍在函数栈帧附近的填充区内,不会立刻崩溃——属于「侥幸可用」的缺陷,一旦编译器改变栈布局或包长被上调即会踩坏返回地址。inv_com_reply_new固定 20 字节收缓冲(goodwe_upgrade.c:162):u8 l_u8_recv_buf[20],但搬入长度是uart4_get_data_len()的任意值,未做上界判断。应答帧若超过 20 字节(例如逆变器把请求回显)会直接越界写栈。这是本文件最高风险的缺陷。inv_com_reply_new的首次调用不做延时(goodwe_upgrade.c:164-170):static u32 recv_start_tick = 0,条件是abs(recv_start_tick - OSTimeGet()) < timeout。上电初期OSTimeGet()通常已远大于 400,首次调用不会进入 400ms 延时分支。这段延时的本意是「等上一包的大响应被收完」,首次调用恰好跳过反而是正确的,但代码意图与实现不一致。inv_com_reply_new中无条件printf(goodwe_upgrade.c:204):printf("inv return %d \r\n", ...)没有被GOODWE_INV_UPGRADE_DEBUG包住,即使调试宏为 0 也会在每包应答时输出串口日志,在高波特率升级中会拖慢时序。UPGRADE_PACK_LEN_NEW在头文件中重复定义(goodwe_upgrade.h:23与:30):两处内容完全相同((512)),属冗余;若将来只改一处会造成「哪个生效取决于预处理顺序」的隐患。UPGRADE_TYPE_DSP、UPGRADE_TYPE_ARM、UPGRADE_HEADER_LEN_NEW定义了但从未使用(已用全仓 grep 确认):新协议的类型字机制只为 ARC 实施,DSP/ARM 走的是旧协议,两者并存说明这是协议过渡期的遗留。GW_UPGRADE_INFO_INIT的语义是「清云端标志」而非「信息初始化」:命名(INFO_INIT)与实际动作(Set_Status_Is_Updated_From_Server(..., FALSE))不符,容易误读为状态机准备阶段。RESTART_FLASH_MODE分支存在自赋值空迁移:upgrade_slave_handle(goodwe_upgrade.c:371)、upgrade_master_handle(:509)、upgrade_external_handle(:562、:608)都在收到0x15时把状态设回自身再清偏移,形成「原地重传」。这段逻辑反复出现且没有注释说明意图,是拷贝粘贴的痕迹。且对upgrade_master_handle而言,boot_code_flag仍为 TRUE,重传时不会再发FlashDSP,若逆变器重启后已退出 bootloader,后续包会全部失败。- 「连续失败 30 次即跳到下一阶段」会掩盖失败(
goodwe_upgrade.c:318-324、:379-385、:452-460、:517-525):从机失败 30 次后直接进入主机升级,主机失败 30 次后直接进入INFO_INIT收尾。两个阶段都可能完全没写成功,但云端标志被清除、流程算作结束,服务器侧看不到失败。缺少「上报失败」的通道。 upgrade_external_handle首包失败分支的逻辑矛盾(goodwe_upgrade.c:561-573):else分支先state = GW_UPGRADE_EXTERNAL(自赋值),再cnt++;随后判cnt > 3。由于首次失败不会重置offset(仍为 0),下一次仍走首包分支,逻辑上勉强自洽,但状态自赋值是明显的笔误(原意可能是GW_UPGRADE_NONE之类)。s_u16_index溢出:UPGRADE_TYPE_ARC帧的包索引是 16 位。以 512 字节/包、SDRAM 固件区SDRAM_3M计,最大约 6144 包,远小于 65536,暂不会溢出;但若固件区被扩展需重新评估。- 完成后的 2 分钟阻塞(
goodwe_upgrade.c:335、:473、:585):OSTimeDlyHMSM(0, 2, 0, 0)期间task_inverter不响应任何逻辑,包括服务器下发的停机/复位命令。这是把「等 ARM 重启」用最粗暴的阻塞实现,若逆变器实际恢复更快会白白损失 2 分钟可用性,反之若更慢则后续查询会拿到错误数据。 get_length_file_updating()被反复调用且未判 0:三个percent_*函数(goodwe_upgrade.c:239、:261、:283)直接做offset * 100 / get_length_file_updating(...),若文件长度尚未就绪返回 0,会发生除零。调用者inverter.c:3859等没有做保护。check_sum的累加和很弱:16 位逐字节累加,无法检出字节换位或成对的互补错误。作为 bootloader 协议属可接受,但若 CRC 可用建议替换;同时注意新旧协议中校验和的字节序不同(旧协议靠单字节应答无校验、新协议大端),跨协议复用代码时极易搞反。- 无
static声明导致符号外泄:check_sum、inv_com_send、inv_com_reply、inv_com_send_new、inv_com_reply_new都是外部链接且名字非常通用,与工程里其它厂商升级模块(如midware/afci/afci_upgrade.c、midware/bms/alpha/battery_upgrade.c、midware/evc/evc_upgrade.c)的同类函数存在潜在重名风险,目前靠实际命名差异侥幸不冲突。 - 注释中的函数名与实际不符:
goodwe_upgrade.c:27、:47、:113、:150的@Name都写成inv_com_send,与实际函数名(check_sum、inv_com_send、inv_com_send_new、inv_com_reply_new)不一致;:394写goodwe_master_handle而函数名是upgrade_master_handle;:535写upgrade_external_handle与函数名一致但紧邻的:628注释写goodwe_upgrade_external_handle。文档与代码脱节。