inv_download.c
Save_Data_To_Inv3_FW_Area() —— 唯一的写路径4.3 Set_Inv2_Ver_From_Server() —— 带截断保护的字符串复制4.4 两个“完成”判据的语义差异4.5 状态迁移(外部状态机,本文件提供读点)4.6 通信协议帧格式与重试机制4.7 与 uCOS-II 的配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
net/inv_download.c所属模块:net(网络应用层 / 设备固件升级下载) 代码行数:195
1. 文件职责
本文件是逆变器(INV)与其配套设备云端固件下载流程的适配层,覆盖三个升级目标:UPDT_INV1(主机 / INVC(M))、UPDT_INV2(从机 / INVC(S))、UPDT_INV3(ARM 协处理器 / INVC(ARM))。它提供三类能力:查询“云端是否强制下载”(IsDownloadINV / IsDownloadCHG / IsDownloadInv3)、查询下载百分比(GetPercentDownloadINV / GetPercentDownloadCHG / GetPercentDownloadInv3)、查询“整机固件是否已从服务器更新完成”(is_inverter_firm_download_finished、is_bwinv_firm_download_finished)。
它还包含一个唯一的数据写入函数 Save_Data_To_Inv3_FW_Area(),把服务器下发的 INV3 固件块直接写进 SDRAM 外挂的固件缓冲区 g_u8_fw_sdram1(带 3 次重试),以及 Set_Inv2_Ver_From_Server() 把服务器版本串写进 stUpt[UPDT_INV2].as8SoftVer(带长度截断保护)。
数据流位置:云端特殊命令 → net/server.c:2346 handle_special_cmd_for_dev() 置位 → 本文件的状态查询被 HMI(hmi/ui/ui_main.c:791-813)与升级流程轮询 → net/server.c:3675 handle_fw_file_from_server() 把包数据分发到 save_fw_to_sdram()(INV1/INV3 用 g_u8_fw_sdram1,INV2 用 g_u8_fw_sdram2,见 net/server.c:3648-3657)→ net/upd_download.c:391 calculate_dev_FW_Flash_MD5() 校验 MD5 → 完成后由各厂商升级模块(midware/inverter/*/xxx_upgrade.c)读取本文件的完成标志决定何时开始刷写逆变器。
驱动方有三类:云端(触发下载)、HMI(显示进度)、逆变器通信/升级任务(消费完成标志)。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
STUpdateTab | 结构体 | 定义于 net/server.h:216;本文件读写其中的 bIsForceUpdtResponse(已应答强制升级)、as8SoftVer[16](服务器版本串)、bIsUpdateForce、bIsUpdatedFromServer、u32LenFile、u32OffsetFile |
ENUpdateType | 枚举 | 定义于 net/server.h:186;本文件使用 UPDT_INV1(2)、UPDT_INV2(3)、UPDT_INV3(4) 三个下标 |
ST_EMS_INFO(g_stEMSInfo) | 全局结构体实例 | 定义于 net/ems.h;stUpt[UPDT_MAX] 是升级表(net/ems.h:346) |
g_u8_fw_sdram1 | 全局数组 | u8 g_u8_fw_sdram1[SDRAM_3M] __attribute__((at(SDRAM_FIRM1_START_ADDR))),声明于 net/server.h:258;INV1/INV3/BMS/Meter/EVC/AFCI 共用的 3 MB SDRAM 固件暂存区 |
MAX_LEN_SOFT_VER | 宏 | net/server.h:146,值 (16);软版本串最大长度,对应 as8SoftVer[16];本文件用它做 Set_Inv2_Ver_From_Server() 的截断判据 |
BOOLEAN | 类型 | uCOS/uC-LIB 定义;Get_Response_For_Inv3_Force_Update() 的返回类型 |
BOARD_EMS3_6_3_E | 枚举值 | system/board_init/board_init.h:21 的板型之一;is_bwinv_firm_download_finished() 用它判断是否为单核 CPU 逆变器(贝瓦机型)板 |
DEBUG_TCP_CLIENT_STATE | 宏 | 调试开关,控制 Save_Data_To_Inv3_FW_Area() 与 Set_Inv2_Ver_From_Server() 中的 printf |
SIZE_KB_500 | 宏 | 定义于 inv_download.h:21,值 (1024*500);全工程无任何引用(死宏),且未在本文件使用 |
本文件不定义任何结构体、枚举、联合体、位域。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
Get_Response_For_Inv3_Force_Update | 全局 | inv_download.c:39。返回 stUpt[UPDT_INV3].bIsForceUpdtResponse,表示 EMS 是否已对云端 INV3 强制升级命令作出应答 |
Save_Data_To_Inv3_FW_Area | 全局 | inv_download.c:55。把一包 INV3 固件写入 g_u8_fw_sdram1 + u32OffsetFile,最多重试 3 次,成功返回 0、失败返回 1 |
Set_Inv2_Ver_From_Server | 全局 | inv_download.c:88。把服务器版本串写入 stUpt[UPDT_INV2].as8SoftVer,带 NULL/长度 0 提前返回与 MAX_LEN_SOFT_VER 截断保护 |
IsDownloadINV | 全局 | inv_download.c:116。返回 get_flag_force_update(UPDT_INV1),主机强制下载标志 |
GetPercentDownloadINV | 全局 | inv_download.c:127。UPDT_INV1 的下载百分比 |
IsDownloadCHG | 全局 | inv_download.c:138。返回 get_flag_force_update(UPDT_INV2),从机(充电机 CHG)强制下载标志 |
GetPercentDownloadCHG | 全局 | inv_download.c:149。UPDT_INV2 的下载百分比 |
IsDownloadInv3 | 全局 | inv_download.c:160。返回 get_flag_force_update(UPDT_INV3),ARM 侧强制下载标志 |
GetPercentDownloadInv3 | 全局 | inv_download.c:171。UPDT_INV3 的下载百分比 |
is_inverter_firm_download_finished | 全局 | inv_download.c:182。返回 Get_Status_Is_Updated_From_Server(UPDT_INV1) && Get_Status_Is_Updated_From_Server(UPDT_INV2),即主/从机固件都已就绪 |
is_bwinv_firm_download_finished | 全局 | inv_download.c:192。返回 get_board_type() == BOARD_EMS3_6_3_E,单核 CPU 逆变器板的特判 |
共 11 个函数,全部为全局作用域,无 static 函数、无函数指针、无回调。其中 Get_Response_For_Inv3_Force_Update、Save_Data_To_Inv3_FW_Area、Set_Inv2_Ver_From_Server 三个全工程无调用点(详见第 7 节备注 1)。
4. 核心逻辑解析
4.1 三种逆变器升级目标的编号与显示映射
ENUpdateType 中的三个逆变器槽位与 HMI 显示文案、SDRAM 缓冲区、机型串来源的对应关系如下(数据来源:net/server.h:186-198、net/server.c:3642-3662、hmi/ui/ui_main.c:791-813):
| 枚举 | 值 | HMI 文案 | 文件掩码 | SDRAM 缓冲区 | 固件类型串来源 |
|---|---|---|---|---|---|
UPDT_INV1 | 2 | INVC(M) | FILE_MASK_INV1 = "00000004" | g_u8_fw_sdram1 | inverter_read_inv_type() |
UPDT_INV2 | 3 | INVC(S) | FILE_MASK_INV2 = "00000008" | g_u8_fw_sdram2 | inverter_read_inv_type() |
UPDT_INV3 | 4 | INVC(ARM) | FILE_MASK_INV3 = "00000400" | g_u8_fw_sdram1 | inverter_read_inv_type() |
注意 INV2 是唯一使用 g_u8_fw_sdram2 的类型,因此 INV1/INV3 的下载不能与其它共用 g_u8_fw_sdram1 的设备(BMS/Meter/EVC/AFCI)并发,这是云端调度必须遵守的隐含约束。
4.2 Save_Data_To_Inv3_FW_Area() —— 唯一的写路径
char Save_Data_To_Inv3_FW_Area(u8 *fw_Bin, u32 Len) // inv_download.c:55
{
l_u32AddrOffset = get_curr_offset_file_request(UPDT_INV3);
l_u32WrAddr = (u32)(g_u8_fw_sdram1 + l_u32AddrOffset);
...
for(u8 i = 0;i < 3;i ++)
{
l_u8Res = FMC_SDRAM_WriteBuffer_02(fw_Bin, l_u32WrAddr, Len);
if(l_u8Res == Len)
return 0;
}
return 1;
}
要点:
- 写入地址 = SDRAM 固件区基址 +
stUpt[UPDT_INV3].u32OffsetFile,偏移由net/server.c:3568increase_current_offset_file_updt()在每包收妥后推进。 - 重试策略:最多 3 次,判据是写函数的返回值等于字节数
Len(FMC_SDRAM_WriteBuffer_02返回实际写入长度,定义于hardware/sdram/sdram.c:242)。3 次都失败才返回 1。 - 返回值语义与常规 C 约定相反(0 表示成功),与
net/upd_download.c:137的通用版Save_Data_To_FW_Area_InSdram()一致——后者是本函数把g_u8_fw_sdram1硬编码后得到的等价实现,两者代码结构完全相同。 - 无
IWDG_Feed():单包写 SDRAM 耗时短,调用方net/server.c的收包流程负责喂狗。 - 无边界检查:函数不校验
offset + Len是否超过SDRAM_3M(3 MB)。上限保护依赖上传侧的net/server.c:3326set_file_updating_info_for_dev()对u32LenFile的区间校验((sizeMin, SDRAM_3M),其中SDRAM_3M定义于hardware/sdram/sdram.h:35)。
4.3 Set_Inv2_Ver_From_Server() —— 带截断保护的字符串复制
void Set_Inv2_Ver_From_Server(char * ver,u8 len) // inv_download.c:88
{
if(ver == NULL || len == 0 ) { ... return ; } // 参数保护
if( len >= MAX_LEN_SOFT_VER) // len >= 16
{
memcpy(..., ver, (MAX_LEN_SOFT_VER-1)); // 拷 15 字节
...as8SoftVer[MAX_LEN_SOFT_VER-1] = '\0'; // 强补结束符
}
else
{
memcpy(..., ver, len); // 拷 len 字节
...as8SoftVer[len] = '\0'; // 补结束符(len<=15,不越界)
}
}
契约是「最多保留 15 个可见字符 + 1 个 '\0'」,对应 as8SoftVer[16](net/server.h:226)。len 参数由调用方传入;本文件没有调用点,功能上已被 net/upd_download.c:269 的通用版 set_ver_from_server(ENUpdateType, char*, u8) 完全覆盖(net/server.c:2422 调用的是通用版)。
4.4 两个“完成”判据的语义差异
| 接口 | 判据 | 语义 |
|---|---|---|
is_inverter_firm_download_finished() | UPDT_INV1 && UPDT_INV2 的 bIsUpdatedFromServer | 主机与从机固件都已收妥并通过 MD5 校验 |
is_bwinv_firm_download_finished() | get_board_type() == BOARD_EMS3_6_3_E | 不查任何升级状态,纯板型判据 |
is_bwinv_firm_download_finished() 的实现与命名严重不符(注释称“获取单核 cpu 逆变器升级标记”):它只是问“本机是不是 3.6.3_E 板”。唯一调用点 midware/inverter/alphaess/alphaess_upgrade.c:410 的写法是:
else if(is_bwinv_firm_download_finished() && get_board_type() != BOARD_EMS3_6_4_E)
即 get_board_type()==BOARD_EMS3_6_3_E && get_board_type()!=BOARD_EMS3_6_4_E,后半段恒为真——这实际上就是在筛选 3.6.3_E 板。可以认为该函数是“板型判据”的包装,而不是“升级状态”查询。
is_inverter_firm_download_finished() 的调用点很多(至少 14 处),典型用法是“固件已就绪 → 逆变器通信层只传固件不传常规数据帧”:
midware/inverter/alphaess/alphaess_comm_usart.c:5434、kelong_comm.c:1365、goodwe_comm.c:2448、ginlong_comm_usart.c:4863、inovance_comm_usart.c:2575midware/inverter/alphaess/alphaess_comm_usart.c:5337与is_afci_firm_download_finished()一起用作“逆变器或 AFCI 正在升级”的联合判据。
4.5 状态迁移(外部状态机,本文件提供读点)
[云端特殊命令] server.c:2346 handle_special_cmd_for_dev
├─ 强制: server.c:2408 set_flag_force_update(UPDT_INV1/2/3, TRUE)
├─ 非强制: server.c:2410-2412 INV3 被显式清零(与 BMS/EVC 同组)
└─ server.c:2418 set_updt_file_mask(...)
↓ IsDownloadINV()/IsDownloadCHG()/IsDownloadInv3() == 1
↓ HMI ui_main.c:791-813 进入 “Download INV/CHG FW” 界面,显示百分比
↓ server.c:3453-3464 set_file_updating_info_for_dev(UPDT_INV1/2/3, 5K, SDRAM_3M)
↓ server.c:3748-3758 save_fw_to_spi_flash → save_fw_to_sdram(INV1/INV3→sdram1, INV2→sdram2)
↓ server.c:3787-3798 calculate_dev_FW_Flash_MD5(UPDT_INV1/2/3, ...)
├─ MD5 一致 → upd_download.c:412 Set_Status_Is_Updated_From_Server(..., TRUE)
│ ↓ is_inverter_firm_download_finished() == 1
│ ↓ 各厂商 upgrade 模块开始刷写逆变器
└─ MD5 不一致 → upd_download.c:422 u16_upgrade_error.BIT.md5_error = 1
另有强制升级应答链:net/server.c:2370/:2382 置 bIsForceUpdtResponse,Get_Response_For_Inv3_Force_Update() 是它的读点(当前无人调用);net/ems.c:2882 与 net/server.c:151 会把它清回 FALSE。
4.6 通信协议帧格式与重试机制
本文件不构造/解析任何报文,也没有超时参数。相关外部契约:
- 上行:
net/ems.c:2945request_update_file_info()组装checkUpdate请求,INV 分支输出Hard_Version = inverter_read_hw_ver()、File_Type = inverter_read_inv_type()(ems.c:2960-2971、:3005);请求由net/ems.c:3042fill_common_data(..., REQUEST_COMM, CMD_CHECK_UPDATE)加 7 字节头与 2 字节校验。 - 下行包级校验(
net/server.c:3692起):FileMask(8 字符) →FileOffset(必须等于本地已下载偏移,server.c:3699)→FileType(必须与inverter_read_inv_type()匹配,server.c:3707)→FileLength(≤LEN_REQ_FILE,server.c:3717)→FileData(hex,server.c:3743hex_ascii_to_bin还原)。任一步失败立即return丢包,等云端超时重发。 - 整卷校验:MD5(
net/upd_download.c:391-427),失败只置md5_error位,不置完成位。 - SDRAM 写入重试:本文件 3 次(
inv_download.c:67-72),与net/upd_download.c:149-154一致。
4.7 与 uCOS-II 的配合
本文件不涉及任何 uCOS-II API:无任务创建/挂起/延时,无信号量,无消息邮箱,无事件标志组。所有函数都是同步、非阻塞、O(1)(Save_Data_To_Inv3_FW_Area 的 3 次重试是忙等循环,但每次只是一次 SDRAM 写,不调用 OSTimeDly)。
g_stEMSInfo.stUpt[] 由 EMS 网络任务(net/server.c、net/ems.c)写,被 HMI 任务与多个逆变器通信任务读,全程无临界区。is_inverter_firm_download_finished() 连续读两个 u8 字段(inv_download.c:184)而未做原子快照,理论上可能出现“读到 INV1 已置位、INV2 尚未置位”的中间态;由于该函数只用于放宽数据帧发送限制,中间态只会导致延迟一轮生效,无功能风险。
5. 对外接口
本文件导出(供谁调用):
| 导出名 | 调用方 |
|---|---|
IsDownloadINV | hmi/ui/ui_main.c:791、:795;net/ems_download.c:255 |
IsDownloadCHG | hmi/ui/ui_main.c:791、:802;net/ems_download.c:256 |
IsDownloadInv3 | hmi/ui/ui_main.c:791;net/ems_download.c:257 |
GetPercentDownloadINV | hmi/ui/ui_main.c:799 |
GetPercentDownloadCHG | hmi/ui/ui_main.c:806 |
GetPercentDownloadInv3 | hmi/ui/ui_main.c:813 |
is_inverter_firm_download_finished | midware/inverter/alphaess/alphaess_comm_can.c:1189、alphaess_comm_usart.c:5337/:5434、ginlong/ginlong_comm_can.c:301、ginlong_comm_usart.c:4863、goodwe/goodwe_comm.c:2448、inovance/inovance_comm_can.c:599、inovance_comm_usart.c:2575、inovance_upgrade.c:414、kelong/kelong_comm.c:1365、set/set_comm_can.c:685、set_upgrade_s5.c:164;net/ems.c:6681 |
is_bwinv_firm_download_finished | midware/inverter/alphaess/alphaess_upgrade.c:410 |
Get_Response_For_Inv3_Force_Update | 无调用点 |
Save_Data_To_Inv3_FW_Area | 无调用点 |
Set_Inv2_Ver_From_Server | 无调用点 |
本文件不导出全局变量。
本文件调用的外部符号:
| 名称 | 来源 |
|---|---|
g_stEMSInfo | net/ems.h |
get_flag_force_update / Get_Status_Is_Updated_From_Server / get_curr_offset_file_request / get_length_file_updating | net/upd_download.c(声明于 upd_download.h:26/28/37/40) |
FMC_SDRAM_WriteBuffer_02 | hardware/sdram/sdram.c:242(声明于 sdram.h:107) |
g_u8_fw_sdram1 | net/server.h:258 |
get_board_type | system/board_init/board_init.h:30 / board_init.c |
MAX_LEN_SOFT_VER、DEBUG_TCP_CLIENT_STATE | net/server.h:146 / 调试开关 |
memcpy、printf、__FUNCTION__、__LINE__ | C 标准库(string.h / stdio.h) |
6. 依赖关系
依赖(第 13-23 行 include):
<stdlib.h>、<string.h>、<stdio.h>(显式标准库,同族其它 download 文件没有写)ems.h、server.h、upd_download.h、inv_download.h、md5.h、sdram.h、iwdg.h、board_init.hiwdg.h在本文件内无任何引用(文件中没有IWDG_Feed()调用);stdlib.h也无使用(无atoi/malloc);md5.h在本文件内无引用(MD5 计算在upd_download.c)。- 书写细节:
#include "md5.h"用的是小写文件名,而同族其它文件写"MD5.h"。在 Windows/Keil 下不敏感,但若移植到区分大小写的工具链(如 Linux 上的 GCC)会直接编译失败。
被依赖(include inv_download.h 的文件,共 18 处):
net/inv_download.c:19、net/ems.c:20、net/server.c:22、net/ems_download.c:19hmi/ui/ui_main.c:24midware/debug/ate.c:18(未使用其中任何符号,纯冗余)midware/interface/modbus/modbus.c:34midware/inverter/parallel.c:22midware/inverter/alphaess/alphaess_comm_can.c:18、alphaess_comm_usart.c:26、alphaess_upgrade.c:17midware/inverter/ginlong/ginlong_comm_can.c:20、ginlong_comm_usart.c:23、ginlong_upgrade.c:15midware/inverter/goodwe/goodwe_comm.c:24、goodwe_upgrade.c:15midware/inverter/inovance/inovance_comm_can.c:10、inovance_comm_usart.c:20、inovance_upgrade.c:15midware/inverter/kelong/kelong_comm.c:22、kelong_upgrade.c:15midware/inverter/set/set_comm_can.c:20、set_comm_usart_s5.c:20、set_upgrade_s5.c:16
7. 备注与风险点
- 3 个函数定义后无任何调用点(死代码):
Get_Response_For_Inv3_Force_Update(:39)、Save_Data_To_Inv3_FW_Area(:55)、Set_Inv2_Ver_From_Server(:88)在全工程.c/.h中除本文件外无第二次出现(已用grep -rn确认)。功能分别被get_response_force_upd_status(UPDT_INV3)、Save_Data_To_FW_Area_InSdram(UPDT_INV3, ..., g_u8_fw_sdram1)、set_ver_from_server(UPDT_INV2, ...)取代。保留的三份重复实现会随版本演化与通用版产生行为漂移。 Save_Data_To_Inv3_FW_Area返回值语义反直觉且未做边界检查:return 0表示成功、return 1表示失败(与net/upd_download.c:137一致),且不校验offset + Len ≤ SDRAM_3M。越界防护完全依赖云端上报的u32LenFile落在(5 KB, 3 MB)区间内(net/server.c:3463)。若云端上报的包长与偏移不一致(例如重复包导致u32OffsetFile累加超限),会写穿g_u8_fw_sdram1的 3 MB 上界,覆盖到SDRAM_FIRM2_START_ADDR(INV2 固件区)。is_bwinv_firm_download_finished()名实不符:函数名与注释都是“固件下载完成标记”,实现却是get_board_type() == BOARD_EMS3_6_3_E(inv_download.c:194),完全不查升级状态。唯一调用点alphaess_upgrade.c:410又叠加了&& get_board_type() != BOARD_EMS3_6_4_E,构成冗余条件。这是明确的命名/语义风险,改动板型定义时极易误伤。u8截断与除零:三个百分比函数(:129、:151、:173)的除数是u32LenFile,在文件信息到达前为 0;HMI 是「先判IsDownload*()再算百分比」,存在除零窗口(ARM 平台的 32 位无符号除法除零返回 0、不触发硬件异常,故不死机)。返回类型u8与u32中间值之间没有上限保护。is_inverter_firm_download_finished()的两次读非原子:inv_download.c:184在一行内连续读两个u8字段并做&&,中间可能被网络任务打断,读到不一致快照。影响仅为延迟一轮生效,无功能风险,但属于典型竞态写法。SIZE_KB_500是死宏:定义于inv_download.h:21,全工程无引用(grep -rn仅命中定义行)。INV 固件长度上限实际由SDRAM_3M(net/server.c:3455-3463)决定,该宏已过期。inv_download.h声明的 5 个函数无定义:set/get_flag_inv1_force_update、set/get_flag_inv3_force_update、Clear_Inv2_Update_Info在本文件及全工程都没有实现;同时本文件定义的Get_Response_For_Inv3_Force_Update又没有在头文件中声明(缺原型,C99 下会有告警)。声明集合与实现集合双向不匹配。#include "md5.h"大小写与其它文件不一致:同族文件用"MD5.h"。Windows/Keil 不敏感,跨平台工具链会失败(见 6. 依赖)。- 注释与实现不一致:
inv_download.c:186-190的函数头注释写u8 is_inverter_firm_download_finished(void),但下面定义的是is_bwinv_firm_download_finished()(注释标题重复);:44-53的注释签名写char Save_Data_To_Inv3_FW_Area(const u8 *fw_Bin, u32 Len),实现是u8 *fw_Bin(去掉了const)。 - 冗余 include:
iwdg.h、stdlib.h、md5.h在本文件无使用,midware/debug/ate.c引入本头文件后也未使用任何符号。