EMS 知识库
首页 / knowledge/代码解析/net

upd_download.c

源文件路径:net/upd_download.c 所属模块:net(网络应用层 — 通用设备固件升级) 代码行数:431

1. 文件职责

本文件是 EMS 整机固件升级的通用状态表与落盘服务层,被 EMS/BMS/逆变器/电表/充电桩/AFCI 等所有升级目标共用。

它维护 g_stEMSInfo.stUpt[] 这张升级描述表(STUpdateTab)的读写封装:文件掩码、软件版本、MD5、文件长度、下载偏移、强制更新标志、服务器已下发标志、特殊升级指令码等,全部通过本文件的 setter/getter 访问。

它提供两条固件二进制落盘通道:Save_Data_To_FW_Area_InSdram() 写 SDRAM 固件区(供需要先在内存中组包再转发的设备使用),save_data_to_fw_area_inFlash() 写外挂 SPI Flash(EMS 自身固件),两者都带 3 次重试。

它通过静态标志 s_u8_save_firm_in_app_flash_flag 决定落盘到主用分区(APP1)还是备份分区(APP2),实现双分区升级与回滚。

数据流位置上,它由 net/server.c(TCP 云端升级链路)和 midware/interface/modbus/modbus.c(Modbus 升级链路)驱动,是"收到数据包 → 落盘 → 推进偏移"链条中落盘与偏移维护的核心实现。

2. 关键数据结构与宏

本文件未定义结构体、枚举或联合体(其参数类型的定义位于 net/server.h)。使用的宏与外部类型如下。

名称类型说明
START_ADDR_SPI_FLASH_EMS_FW宏0x00003000,主用 EMS 固件下载区起始(upd_download.h:16)
END_ADDR_SPI_FLASH_EMS_FW宏0x00102FFF,该区结束(:17),本文件未使用
START_ADDR_SPI_FLASH_EMS_APP2_FW宏0x00106000,备份 EMS 固件下载区起始(:20)
END_ADDR_SPI_FLASH_EMS_APP2_FW宏0x00205FFF,该区结束(:21),本文件未使用
ENUpdateType枚举(外部 net/server.h:186-198)UPDT_EMS=0, UPDT_BMS, UPDT_INV1, UPDT_INV2, UPDT_INV3, UPDT_Meter, UPDT_EVC, UPDT_AFCI, UPDT_MAX,本文件所有 API 的第一参数;注意 UPDT_Meter 是拼写不一致的枚举名
ENInfoType枚举(外部 net/ems.h:247-272)上报信息类型位标志,calculate_dev_FW_Flash_MD5 用它作 set_info_status 的实参,取值为 BMS_FIRM_DOWNLOAD=0x04、INVC_FIRM_DOWNLOAD=0x08、METER_FIRM_DOWNLOAD=0x20、EVC_FIRM_DOWNLOAD=0x80、INV_ARM_DOWNLOAD=0x100、AFCI_FIRM_DOWNLOAD=0x200000,另传 INFO_MAX 表示"不置位"
STUpdateTab结构体(外部 net/server.h:216-236)单个升级目标的全部元信息,本文件全部 setter/getter 的操作对象
BOOLEAN类型(外部)uCOS-II 布尔类型,本文件用于 4 个标志位接口
SDRAM_1K宏(外部 hardware/sdram/sdram.h:22)1*1024
MAX_LEN_SOFT_VER宏(外部 net/server.h:146)16,软件版本字符串上限(含 '\0')
MAX_SIZE_Special_CMD_Update_Str宏(外部 net/server.h:80)16,特殊升级指令串长度上限
FLASH_OK枚举值(外部 hardware/flash/flash.h:107)Flash_Write 成功返回码
DEBUG_TCP_CLIENT_STATE宏(外部)调试打印总开关
s_u8_save_firm_in_app_flash_flag全局静态 u8分区选择标志,TRUE = 主用 APP1,FALSE = 备份 APP2,初值 TRUE(:27)
g_stEMSInfo全局变量(外部 net/ems.h:467,类型 STEMSTab)本文件读写 stUpt[updType] 全表项
g_t_upgrade_run_info全局变量(外部 net/ems.h:496,类型 UPGRADE_INFO_T)本文件置 u16_upgrade_error.BIT.md5_error(位域定义见 net/ems.h:409)

3. 函数清单

函数名作用域说明
set_response_force_upd_status(ENUpdateType updType, BOOLEAN set)全局写 stUpt[updType].bIsForceUpdtResponse,标记"已对服务器强制升级请求作出应答"(:41)
get_response_force_upd_status(ENUpdateType updType)全局读 bIsForceUpdtResponse(:57)
set_flag_force_update(ENUpdateType updType, BOOLEAN bSet)全局写 bIsUpdateForce,置位表示该设备进入强制/主动升级下载流程(:73)
get_flag_force_update(ENUpdateType updType)全局读 bIsUpdateForce(:89)
clear_curr_offset_file_request(ENUpdateType updType)全局把 u32OffsetFile 归零,开始一次新下载(:105)
get_curr_offset_file_request(ENUpdateType updType)全局读下载偏移 u32OffsetFile(:121)
Save_Data_To_FW_Area_InSdram(ENUpdateType updType, u8 *fw_Bin, u32 Len, u8 *fw_sdram_baseaddr)全局用当前偏移把一块固件写入指定 SDRAM 基址,最多重试 3 次,返回 0 成功 / 1 失败(:137)
get_save_firm_in_app_flash_flag(void)全局返回分区选择标志(:166)
set_save_firm_in_app_flash_flag(u8 flag)全局设置分区选择标志(:178)
save_data_to_fw_area_inFlash(ENUpdateType updType, u8 *fw_Bin, u32 Len)全局用当前偏移把一块固件写入外挂 SPI Flash 主/备固件区,最多重试 3 次,返回 0 成功 / 1 失败(:194)
set_status_update_file_info(ENUpdateType updType, u8 value)全局写 bIsGotUpdtFileInfo,标记"已从服务器取得升级文件信息"(:237)
set_updt_file_mask(ENUpdateType updType, char *fileMask)全局用 strncpy 把文件掩码串拷入 as8FileMask[8](:253)
set_ver_from_server(ENUpdateType updType, char *ver, u8 len)全局把服务器下发的软件版本字符串拷入 as8SoftVer[16],带长度截断与 '\0' 补齐(:269)
set_id_code_for_updt_cmd(ENUpdateType updType, u8 *buff, u32 len)全局把特殊升级指令码拷入 au8IDCMDUpdate[32],先清零(:304)
get_length_file_updating(ENUpdateType updType)全局读升级文件总长度 u32LenFile(:327)
get_curr_md5_code_fw_file(ENUpdateType updType)全局返回 as8MD5Code[32] 指针,服务器下发的 MD5 十六进制串(:343)
Set_Status_Is_Updated_From_Server(ENUpdateType updType, u8 bSet)全局写 bIsUpdatedFromServer,表示固件已就绪待升级(:359)
Get_Status_Is_Updated_From_Server(ENUpdateType updType)全局读 bIsUpdatedFromServer(:375)
calculate_dev_FW_Flash_MD5(ENUpdateType updType, u8 *g_u8_fw_sdram, ENInfoType type)全局对 SDRAM 中的整包固件做 MD5,与服务器 MD5 比对,成功则置位并上报设备升级状态,最后统一把偏移归零(:391)

4. 核心逻辑解析

4.1 升级状态表 STUpdateTab 的读写封装

本文件绝大多数函数是单行 getter/setter,操作对象统一为 g_stEMSInfo.stUpt[updType]。按字段归类:

字段类型写入函数读取函数语义
bIsForceUpdtResponseBOOLEANset_response_force_upd_status(:41)get_response_force_upd_status(:57)已应答服务器强制升级请求
bIsUpdateForceBOOLEANset_flag_force_update(:73)get_flag_force_update(:89)主动/强制升级标志(被 IsDownloadEMS 复用为"正在下载")
u32OffsetFileu32clear_curr_offset_file_request(:105)、save_data_to_fw_area_inFlash 间接不写、calculate_dev_FW_Flash_MD5(:429)get_curr_offset_file_request(:121)、Save_Data_To_FW_Area_InSdram(:143)、save_data_to_fw_area_inFlash(:202)已落盘字节偏移
u32LenFileu32无(由 net/server.c 解析响应时直写)get_length_file_updating(:327)固件总长度
as8MD5Code[32]char[]无(由 net/server.c / modbus.c 直写)get_curr_md5_code_fw_file(:343)服务器下发的 MD5 十六进制串
bIsGotUpdtFileInfoBOOLEANset_status_update_file_info(:237)无(由 net/server.c 直读)已取得文件信息
as8FileMask[8]char[]set_updt_file_mask(:253)无升级文件掩码
as8SoftVer[16]char[]set_ver_from_server(:269)无目标软件版本
au8IDCMDUpdate[32]u8[]set_id_code_for_updt_cmd(:304)无特殊升级指令码
bIsUpdatedFromServerBOOLEAN / u8Set_Status_Is_Updated_From_Server(:359)、calculate_dev_FW_Flash_MD5(:412)Get_Status_Is_Updated_From_Server(:375)固件已就绪待升级

所有接口不做 updType 合法性检查,直接用作数组下标;越界调用会安静地破坏 g_stEMSInfo 之后的全局变量。

4.2 字符串拷贝接口的边界处理

set_ver_from_server()(:269-291) 是三个字符串接口中唯一做了完整边界保护的:

if (ver == NULL || len == 0) return;                       // :271-277  空指针/零长直接返回
if (len >= MAX_LEN_SOFT_VER) {                             // :278  len >= 16
    memcpy(as8SoftVer, ver, MAX_LEN_SOFT_VER - 1);         //   只拷 15 字节
    as8SoftVer[MAX_LEN_SOFT_VER - 1] = '\0';               //   [15] 置 0
} else {
    memcpy(as8SoftVer, ver, len);
    as8SoftVer[len] = '\0';                                //   [len] 置 0
}

注意调试打印(:289)里硬编码了 g_stEMSInfo.stUpt[UPDT_INV1].as8SoftVer 而不是 [updType],打印内容与参数无关,属调试遗留。

set_id_code_for_updt_cmd()(:304-314):

if (buff == NULL || len > MAX_SIZE_Special_CMD_Update_Str) return;   // len > 16 才拒绝
memset(au8IDCMDUpdate, 0x00, sizeof(...));                            // 先清 32 字节
memcpy(au8IDCMDUpdate, buff, len);

目标缓冲是 32 字节(net/server.h:229),但门限用的是 16(MAX_SIZE_Special_CMD_Update_Str,net/server.h:80)。安全性没问题,但 16 < len ≤ 32 的合法指令码会被静默丢弃,调用方(net/server.c:2358)无从感知。

set_updt_file_mask()(:253-256):

strncpy(g_stEMSInfo.stUpt[updType].as8FileMask, fileMask, sizeof(g_stEMSInfo.stUpt[updType].as8FileMask));

sizeof(as8FileMask) = 8,而所有 FILE_MASK_* 常量正好是 8 字符(net/server.h:83-91,如 "00000001")。strncpy 在源串长度 ≥ 目标长度时不写 '\0',因此 as8FileMask[8] 完全没有终止符。net/server.c:3227 的 strstr(uptInfo->as8FileMask, fileMask) 与 net/server.c:43/45 的 strlen(...as8FileMask) 都会越过 8 字节边界继续读取,直到碰巧遇到结构体中下一个 '\0'。

4.3 固件落盘通道

SDRAM 通道 Save_Data_To_FW_Area_InSdram()(:137-157)

l_u32AddrOffset = get_curr_offset_file_request(updType);            // :143
l_u32WrAddr     = (u32)(fw_sdram_baseaddr + l_u32AddrOffset);       // :144
for (i = 0; i < 3; i++) {
    l_u32Res = FMC_SDRAM_WriteBuffer_02(fw_Bin, l_u32WrAddr, Len);  // :151
    if (l_u32Res == Len) return 0;                                 // 写满 Len 字节视为成功
}
return 1;

SPI Flash 通道 save_data_to_fw_area_inFlash()(:194-224)

static u16 s_u16CntWrtieFlash;                                       // :198  调试计数器
s_u16CntWrtieFlash++;                                                // :201  永远递增,无上限
l_u32AddrOffset = get_curr_offset_file_request(updType);              // :202
if (get_save_firm_in_app_flash_flag() == TRUE)
    l_u32WrAddr = START_ADDR_SPI_FLASH_EMS_FW      + l_u32AddrOffset; // :205  主用区
else
    l_u32WrAddr = START_ADDR_SPI_FLASH_EMS_APP2_FW + l_u32AddrOffset; // :209  备份区
for (i = 0; i < 3; i++) {
    l_u8Res = Flash_Write(fw_Bin, l_u32WrAddr, Len);                  // :218
    if (l_u8Res == FLASH_OK) return 0;
}
return 1;

4.4 分区选择标志 s_u8_save_firm_in_app_flash_flag

值含义落盘地址
TRUE(初值,:27)保存到 APP1(主用)START_ADDR_SPI_FLASH_EMS_FW = 0x3000
FALSE保存到 APP2(备份)START_ADDR_SPI_FLASH_EMS_APP2_FW = 0x106000

写入点:

读取点:net/ems_download.c:74(配置信息落盘)、net/ems_download.c:97(固件读取起点)、本文件 :203(固件落盘)。该标志同时决定"固件区"和"配置信息区",保证两者始终配对。

4.5 MD5 校核与升级结果发布 calculate_dev_FW_Flash_MD5()(:391-430)

IWDG_Feed();                                                          // :396
MD5Init(&md5);
MD5Update(&md5, (char *)g_u8_fw_sdram, get_length_file_updating(updType));  // :398  一次性整包
MD5Final(&md5);
IWDG_Feed();                                                          // :400
for (k = 0; k < 16; k++) sprintf(&file_Md5[k*2], "%02X", md5.digest[k]);     // :402-405
if (memcmp(file_Md5, get_curr_md5_code_fw_file(updType), 32) == 0) {         // :407
    Set_Status_Is_Updated_From_Server(updType, TRUE);                        // :412
    if (updType != UPDT_INV1)
        set_info_status(type);                                               // :414
    if (updType == UPDT_Meter)
        g_stEMSInfo.stUpt[UPDT_Meter].u32OffsetFile = 0;                     // :417
} else {
    g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error = 1;                // :422
}
g_stEMSInfo.stUpt[updType].u32OffsetFile = 0;                                // :429  无条件归零

要点:

调用点updTypeg_u8_fw_sdramtype
net/server.c:3789UPDT_INV1g_u8_fw_sdram1INFO_MAX(故意无效,跳过 set_info_status)
net/server.c:3793UPDT_INV2g_u8_fw_sdram2INVC_FIRM_DOWNLOAD
net/server.c:3797UPDT_INV3g_u8_fw_sdram1INV_ARM_DOWNLOAD
net/server.c:3801UPDT_BMSg_u8_fw_sdram1BMS_FIRM_DOWNLOAD
net/server.c:3805UPDT_Meterg_u8_fw_sdram1METER_FIRM_DOWNLOAD
net/server.c:3809UPDT_EVCg_u8_fw_sdram1EVC_FIRM_DOWNLOAD
net/server.c:3813UPDT_AFCIg_u8_fw_sdram1AFCI_FIRM_DOWNLOAD
midware/interface/modbus/modbus.c:9340UPDT_BMSg_u8_fw_sdram1BMS_FIRM_DOWNLOAD
midware/interface/modbus/modbus.c:9344UPDT_INV1g_u8_fw_sdram1INFO_MAX
midware/interface/modbus/modbus.c:9348UPDT_INV2g_u8_fw_sdram2INVC_FIRM_DOWNLOAD
midware/interface/modbus/modbus.c:9352UPDT_INV3g_u8_fw_sdram1INV_ARM_DOWNLOAD

UPDT_INV1 用 INFO_MAX 作哨兵值来跳过 set_info_status,原因见风险点:set_info_status(INFO_MAX) 会置一个网侧无法识别的信息位且不被清除。这个约定完全靠调用方自觉,函数签名里没有任何说明。

4.6 定时/调度时序与 uCOS-II 配合

4.7 关键全局变量的读写与触发条件

变量读写触发条件
g_stEMSInfo.stUpt[updType].u32OffsetFile✔(:143、:202)✔(:107、:417、:429)开始新下载(clear_curr_offset_file_request)或 MD5 校核结束
g_stEMSInfo.stUpt[updType].u32LenFile✔(:329、:398)—服务器 UpdateCheck 响应解析时写入
g_stEMSInfo.stUpt[updType].as8MD5Code✔(:345、:407)—同上
g_stEMSInfo.stUpt[updType].bIsUpdateForce✔(:91)✔(:75)服务器下发强制升级指令或 Modbus 升级流程
g_stEMSInfo.stUpt[updType].bIsForceUpdtResponse✔(:59)✔(:43)net/server.c:2370/2382 置位,net/server.c:151 消费后清零
g_stEMSInfo.stUpt[updType].bIsGotUpdtFileInfo—✔(:239)服务器 UpgradeCheck 响应校验通过
g_stEMSInfo.stUpt[updType].bIsUpdatedFromServer✔(:377)✔(:361、:412)MD5 校核通过
g_stEMSInfo.stUpt[updType].as8FileMask—✔(:255)服务器下发文件掩码
g_stEMSInfo.stUpt[updType].as8SoftVer—✔(:280/:285)服务器下发版本串
g_stEMSInfo.stUpt[updType].au8IDCMDUpdate—✔(:312-313)服务器下发特殊升级指令码
g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error—✔(:422)MD5 不匹配
s_u8_save_firm_in_app_flash_flag✔(:168、:203)✔(:180)Modbus APP2 对象 / net/ems.c:4660

5. 对外接口

本文件导出(全部声明于 net/upd_download.h:23-41,共 19 个,与实现一一对应)

符号主要使用者
set_response_force_upd_status / get_response_force_upd_statusnet/ems.c:2882、net/server.c:151、:2370、:2382
set_flag_force_update / get_flag_force_updatemidware/afci/afci_upgrade.c:406、:452、midware/inverter/alphaess/alphaess_upgrade.c:538-539、:587-588、net/ems_download.c:219、net/server.c:2412
clear_curr_offset_file_requestnet/server.c:3236
get_curr_offset_file_requestnet/ems_download.c:230、net/server.c:3778
Save_Data_To_FW_Area_InSdramnet/server.c:3589、:3598(经 save_fw_to_sdram)
save_data_to_fw_area_inFlashnet/server.c:3613、:3622(经 save_fw_to_flash)
get_save_firm_in_app_flash_flag / set_save_firm_in_app_flash_flagnet/ems_download.c:74、:97;midware/interface/modbus/modbus.c:8992、:9002;net/ems.c:4660
set_status_update_file_infonet/server.c:2405、:3232
set_updt_file_masknet/server.c:2418、:2430、:2439
set_ver_from_servernet/server.c:2422
set_id_code_for_updt_cmdnet/server.c:2358、:2359、:2361
get_length_file_updatingnet/ems_download.c:129、:130、:172、net/ems_boot_handle.c:222、net/server.c:3786、midware/afci/afci_upgrade.c:263-271、本文件 :398
get_curr_md5_code_fw_filenet/ems_download.c:164、:167、:206、midware/inverter/alphaess/alphaess_upgrade.c:105、:107、本文件 :407、:410、:425
Set_Status_Is_Updated_From_Server / Get_Status_Is_Updated_From_Servernet/ems_download.c:180、:276-283、midware/afci/afci_upgrade.c:405、:451、:517、midware/bms/alpha/battery_upgrade.c:751、midware/bms/bms.c:2751、:2758、midware/evc/evc_upgrade.c:677、midware/interface/interface.c:318、midware/debug/ate.c:132-134、:166
calculate_dev_FW_Flash_MD5net/server.c:3789-3813、midware/interface/modbus/modbus.c:9340-9352

本文件导出但未在头文件声明

无(19 个实现与 19 条声明完全对应,接口契约完整)。

本文件调用的外部函数 / 变量

外部符号来源模块
FMC_SDRAM_WriteBuffer_02hardware/sdram/sdram.h:107
Flash_Writehardware/flash/flash.h:140
MD5Init / MD5Update / MD5Final / MD5_CTXcommon/md5.h
IWDG_Feedhardware/iwdg/iwdg.h
set_info_statusnet/ems.c:2722
g_stEMSInfonet/ems.h:467
g_t_upgrade_run_infonet/ems.h:496
g_u8_fw_sdram1 / g_u8_fw_sdram2(仅由调用方传入地址)net/server.h:258-259
START_ADDR_SPI_FLASH_EMS_FW / START_ADDR_SPI_FLASH_EMS_APP2_FWnet/upd_download.h
FLASH_OKhardware/flash/flash.h:107
strncpy / memcpy / memset / sprintf / memcmp / printf标准库(经 includes.h)

6. 依赖关系

依赖(本文件 include,:13-22)

ems.h、server.h、board_init.h、md5.h、flash.h、iwdg.h、crc.h、inverter.h、meter.h、upd_download.h。

其中 board_init.h、crc.h、inverter.h、meter.h 在本文件中并未实际使用,属冗余 include。

被依赖(会用到本文件导出的符号)

7. 备注与风险点