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

ems_download.c

源文件路径:net/ems_download.c 所属模块:net(网络应用层 — EMS 固件下载与升级) 代码行数:289

1. 文件职责

本文件是 EMS 自身应用固件的"下载结果确认 + 固化重启"环节:当云端或 Modbus 把整包 EMS 固件写入外挂 SPI Flash 之后,本文件负责从头到尾重新读回、计算 MD5、与服务器下发的 MD5 比对,比对通过才写升级配置并复位进入 Boot。

它同时扮演 EMS 升级状态的"查询门面":IsDownloadEMS() / GetPercentDownloadEMS() 供 HMI 显示升级进度条,IsSystemDownload() / IsSystemUpgrade() 供 LED、BMS 加热、断点续传、故障日志、以太网等模块判断"整机是否处于升级中"从而暂停业务。

数据流位置上,它处于 net/server.c(TCP 固件接收,:3785 调用 calculate_ems_flash_md5())和 midware/interface/modbus/modbus.c(Modbus 固件接收,:9336 调用)的下游终点,也是 system_hard_reset() 的触发点。

本文件不驱动任何任务、不创建信号量,全部函数由 net 任务或 Modbus 任务在收完最后一包固件时同步调用。

2. 关键数据结构与宏

本文件未定义结构体、枚举或联合体;使用的宏与外部类型如下。

名称类型说明
START_ADDR_SPI_FLASH_EMS_FW_CFG_INFO宏0x00000000,主用 EMS 升级配置信息区起始(ems_download.h:17)
END_ADDR_SPI_FLASH_EMS_FW_CFG_INFO宏0x00000FFF,该区结束(4KB,1 个扇区)
START_ADDR_SPI_FLASH_EMS_FW宏0x00003000,主用 EMS 固件下载区起始(ems_download.h:21)
END_ADDR_SPI_FLASH_EMS_FW宏0x00102FFF,该区结束(约 1MB)
START_ADDR_SPI_FLASH_EMS_APP2_FW_CFG_INFO宏0x00103000,备份 EMS 升级配置信息区起始(ems_download.h:25)
END_ADDR_SPI_FLASH_EMS_APP2_FW_CFG_INFO宏0x00103FFF,该区结束(4KB)
START_ADDR_SPI_FLASH_EMS_APP2_FW宏0x00106000,备份 EMS 固件下载区起始(ems_download.h:29)
END_ADDR_SPI_FLASH_EMS_APP2_FW宏0x00205FFF,该区结束(约 1MB)
APP_MIN_SIZE宏100 * 1024,APP 固件最小合法长度(ems_download.h:32)
APP_MAX_SIZE宏(CHIP_FLASH_SIZE - 32) * 1024,APP 固件最大合法长度(ems_download.h:33)
FLASH_SECTOR_SIZE宏(外部 hardware/flash/flash.h:27)4 * 1024,SPI Flash 扇区大小,用于长度上限校验
FLASH_OK枚举值(外部 hardware/flash/flash.h:107)Flash_Write / Flash_Read 成功返回码
STUpdateTab结构体(外部 net/server.h:216-236)单个升级目标(EMS/BMS/INV/Meter/EVC/AFCI)的升级描述表,含 u32LenFile、u32OffsetFile、as8MD5Code[32]、bIsUpdatedFromServer、u16_crc_code 等
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
ENInfoType枚举(外部 net/ems.h:247-272)上报信息类型位标志,本文件使用 SYSTEM_RESTART = 0x01
SYSTEM_RESTART枚举值(外部 net/ems.h:249)0x01,置位后 net/ems.c:2449 会上报 "System restart" 消息
DEBUG_TCP_CLIENT_STATE宏(外部)调试打印总开关,为 1 时启用本文件所有 printf
g_stEMSInfo全局变量(外部 net/ems.h:467,类型 STEMSTab)本文件读 stUpt[UPDT_EMS].u32LenFile / as8MD5Code,写 bIsUpdatedFromServer、u16_crc_code
g_au8FWBin全局变量(外部 net/server.h:257)u8[SDRAM_1K],定位在 SDRAM_NET_FW_BIN_ADDR,本文件的分块读回缓冲
g_t_upgrade_run_info全局变量(外部 net/ems.h:496,类型 UPGRADE_INFO_T)本文件置其中 u16_upgrade_error.BIT.md5_error / .BIT.boot_error(位域定义见 net/ems.h:405-424)

3. 函数清单

函数名作用域说明
save_ems_server_update_info(void)全局空实现(函数体只有一对空花括号),EMS 升级信息保存的历史桩函数(:47)
save_updt_config_info_to_external_flash(u8 *pBuff, u32 Len)全局把升级配置表写入外挂 SPI Flash 的 CFG_INFO 区,最多重试 3 次,返回 TRUE/FALSE(:63)
calculate_ems_flash_md5_read_data(u32 address_to_read, u16 size_to_read)全局从 EMS 固件分区按偏移读一块到 g_au8FWBin,最多重试 3 次(:93)
calculate_ems_flash_md5(void)全局EMS 固件整包 MD5 校核主流程,通过则落盘配置并硬复位,失败则置错误位(:120)
IsDownloadEMS(void)全局返回 EMS 强制更新标志 g_stEMSInfo.stUpt[UPDT_EMS].bIsUpdateForce(:217)
GetPercentDownloadEMS(void)全局返回 EMS 下载百分比 = offset * 100 / len(:228)
GetPercentUpgradeEMS(void)全局恒返回 0 的空桩(:239)
IsSystemDownload(void)全局聚合 8 类设备的"正在下载"标志,任一为真返回 1(:252)
IsSystemUpgrade(void)全局聚合 8 类设备的"下载完成待升级"标志,任一为真返回 1(:274)

注意:calculate_ems_flash_md5_read_data(:93)与 GetPercentUpgradeEMS(:239)是全局函数,但未在 net/ems_download.h 中声明,只能在同文件内或通过隐式声明调用。

4. 核心逻辑解析

4.1 EMS 固件 MD5 校核主流程 calculate_ems_flash_md5()(:120-209)

状态机形态为"分块读回 → 流式 MD5 → 与服务器 MD5 比对 → 分支处理",无循环状态,一次调用走到底。

1) loop_cnt  = get_length_file_updating(UPDT_EMS) / 1024;      // :129
   remainder = get_length_file_updating(UPDT_EMS) % 1024;      // :130
2) MD5Init(&md5);
3) for (i = 0; i < loop_cnt; i++) {
       calculate_ems_flash_md5_read_data(i*1024, 1024);         // 失败直接 return
       MD5Update(&md5, g_au8FWBin, 1024);
       IWDG_Feed();                                             // :143
   }
4) memset(g_au8FWBin, 0, sizeof(g_au8FWBin));                    // :146
   if (remainder != 0) {
       calculate_ems_flash_md5_read_data(loop_cnt*1024, remainder);
       MD5Update(&md5, g_au8FWBin, remainder);
       IWDG_Feed();
   }
5) MD5Final(&md5);
6) for (k = 0; k < 16; k++) sprintf(&file_Md5[k*2], "%02X", md5.digest[k]);   // :159-162
7) if (memcmp(file_Md5, get_curr_md5_code_fw_file(UPDT_EMS), 32) == 0)  → 通过分支
   else                                                               → 失败分支

通过分支(:164-200)

步骤行号动作
a:172ems_upgrade_boot_check(get_length_file_updating(UPDT_EMS)),返回 FALSE 时:set_info_status(SYSTEM_RESTART)(:174)、memset(&g_stEMSInfo.stUpt[UPDT_EMS], 0, sizeof(STUpdateTab))(:175)、g_t_upgrade_run_info.u16_upgrade_error.BIT.boot_error = 1(:176),然后 return,不写配置、不复位
b:180Set_Status_Is_Updated_From_Server(UPDT_EMS, TRUE) 置"下载完成待升级"标志
c:182g_stEMSInfo.stUpt[UPDT_EMS].u16_crc_code = GetCrcCode_Modbus((u8*)&g_stEMSInfo.stUpt[UPDT_EMS], sizeof(...) - 2),把表项自身做 Modbus CRC16,结果写进最后 2 字节
d:184save_updt_config_info_to_external_flash(...) 把整个 STUpdateTab 落盘到 SPI Flash CFG_INFO 区
e:191-192OSTimeDlyHMSM(0, 0, 0, 500) 延时 500ms,随后 system_hard_reset() 硬复位,由 Boot 读取配置并完成烧写

失败分支(:201-208):仅置 g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error = 1,不做任何重试或清理。

关键点:

4.2 分块读回 calculate_ems_flash_md5_read_data()(:93-107)

startAddr = (get_save_firm_in_app_flash_flag() == TRUE) ? START_ADDR_SPI_FLASH_EMS_FW
                                                        : START_ADDR_SPI_FLASH_EMS_APP2_FW;
for (j = 0; j < 3; j++) {
    l_u8Res = Flash_Read(g_au8FWBin, startAddr + address_to_read, size_to_read);
    if (l_u8Res == FLASH_OK) break;
}
return l_u8Res;

4.3 升级配置落盘 save_updt_config_info_to_external_flash()(:63-91)

if (pBuff == NULL || Len > FLASH_SECTOR_SIZE || Len == 0) return FALSE;   // :67-68
for (i = 0; i < 3; i++) {
    l_u8Res = get_save_firm_in_app_flash_flag() == TRUE
              ? Flash_Write(pBuff, START_ADDR_SPI_FLASH_EMS_FW_CFG_INFO, Len)
              : Flash_Write(pBuff, START_ADDR_SPI_FLASH_EMS_APP2_FW_CFG_INFO, Len);
    if (l_u8Res == FLASH_OK) return TRUE;
}
return FALSE;

4.4 状态查询与系统级聚合

IsDownloadEMS()(:217-220):直接返回 get_flag_force_update(UPDT_EMS),即 g_stEMSInfo.stUpt[UPDT_EMS].bIsUpdateForce。语义上是"EMS 强制更新标志",但被 HMI 当作"正在下载 EMS 固件"使用(hmi/ui/ui_main.c:782)。

GetPercentDownloadEMS()(:228-231):

return get_curr_offset_file_request(UPDT_EMS) * 100 / get_length_file_updating(UPDT_EMS);

下载进度 = 已写偏移 × 100 ÷ 文件总长。分母未做零检查。

GetPercentUpgradeEMS()(:239-243):u8 res = 0; return res; — 恒 0 的空桩,导出在头文件(net/ems_download.h:41 对应 IsSystemUpgrade 之外),但没有任何调用方,属于版本遗留。

IsSystemDownload()(:252-266):|| 短路聚合 8 个设备下载标志:

IsDownloadBMS() || IsDownloadINV() || IsDownloadCHG() || IsDownloadInv3() ||
IsDownloadEMS() || IsDownloadMeter() || is_download_evc() || IsDownloadAFCI()

IsSystemUpgrade()(:274-288):聚合 8 个设备的"服务器已下发固件"标志:

Get_Status_Is_Updated_From_Server(UPDT_EMS / UPDT_INV1 / UPDT_INV2 / UPDT_INV3 /
                                  UPDT_BMS / UPDT_Meter / UPDT_EVC / UPDT_AFCI)

两个函数是整机"升级闸门"的单一事实来源,被 BMS 加热逻辑(midware/bms/bms.c:1658、:1728、:2493、:2583)、LED(hmi/led.c:180、:191、:193)、以太网(midware/ethernt/ethernet.c:1468、:1518)、无线驱动(midware/ethernt/wireless/wireless_driver.c:243)、云端周期发送(net/ems.c:4604、:4620、:4636、:5160)、断点续传(data/resume_data/flash/flash_data_store.c:2806)、故障日志(data/fault_warn_log/fault_warn_log.c:1540)等大量模块读取。

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

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

变量读写触发条件
g_stEMSInfo.stUpt[UPDT_EMS].u32LenFile✔(:129、:130、:172)—服务器 UpdateCheck 响应解析时写入
g_stEMSInfo.stUpt[UPDT_EMS].as8MD5Code✔(:164)—上述同一时机写入
g_stEMSInfo.stUpt[UPDT_EMS].u32OffsetFile✔(:230)—每收一包固件累加
g_stEMSInfo.stUpt[UPDT_EMS].bIsUpdateForce✔(:219)—服务器 UpdateCheck 响应或 Modbus 升级流程置位
g_stEMSInfo.stUpt[UPDT_EMS].bIsUpdatedFromServer—✔(:180)MD5 校验通过时置 TRUE
g_stEMSInfo.stUpt[UPDT_EMS].u16_crc_code—✔(:182)同上,CRC 覆盖表项前 sizeof-2 字节
g_stEMSInfo.stUpt[UPDT_EMS] 整表—✔(:175)仅当 Boot 准入失败时 memset 清零
g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error—✔(:203)MD5 不匹配时置 1
g_t_upgrade_run_info.u16_upgrade_error.BIT.boot_error—✔(:176)Boot 准入失败时置 1
g_au8FWBin✔(MD5Update)✔(Flash_Read 写入)每 1KB 块
s_u8_save_firm_in_app_flash_flag(upd_download.c)✔(:74、:97)—Modbus APP2 升级对象或 net/ems.c:4660 写入

5. 对外接口

本文件导出(声明于 net/ems_download.h:36-42)

符号使用者
save_updt_config_info_to_external_flash仅本文件内部(:184);头文件导出但无外部调用方
calculate_ems_flash_md5net/server.c:3785(TCP 升级收包完成)、midware/interface/modbus/modbus.c:9336(Modbus 升级收包完成)
save_ems_server_update_infonet/server.c:3234(check_server_fw_is_updating_for_dev() 中,type == UPDT_EMS 时调用);当前为空实现
IsDownloadEMShmi/ui/ui_main.c:782(LCD 显示升级页)、data/fault_warn_log/fault_warn_log.c:1540、data/resume_data/flash/flash_data_store.c:2806、本文件 IsSystemDownload()
GetPercentDownloadEMShmi/ui/ui_main.c:788(LCD 画进度数字)
IsSystemDownloadhmi/led.c:180/191/193、midware/bms/bms.c:1658/1728/2414/2493/2583/2676、midware/ethernt/ethernet.c:1468、midware/ethernt/wireless/wireless_driver.c:243、net/ems.c:4604/4620/4636/5160
IsSystemUpgradehmi/led.c:180/191、midware/bms/bms.c:1658/1728/2415/2493/2583/2677、midware/ethernt/ethernet.c:1518、net/ems.c:2861

本文件导出但未在头文件声明的全局函数

符号说明
calculate_ems_flash_md5_read_data全局可见,仅本文件内部调用(:138、:149)
GetPercentUpgradeEMS全局可见,恒返回 0,无调用方

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

外部符号来源模块
get_length_file_updating / get_curr_offset_file_request / get_curr_md5_code_fw_file / get_flag_force_update / get_save_firm_in_app_flash_flag / Set_Status_Is_Updated_From_Server / Get_Status_Is_Updated_From_Servernet/upd_download.c
ems_upgrade_boot_checknet/ems_boot_handle.c:215
IsDownloadBMSnet/bms_download.h:17
IsDownloadINV / IsDownloadCHG / IsDownloadInv3net/inv_download.h:36-38
IsDownloadMeternet/meter_download.h:19
is_download_evcnet/evc_download.h:27(实现于 evc_download.c / evc_upgrade.c)
IsDownloadAFCInet/afci_download.h:17
Flash_Write / Flash_Read / FLASH_OKhardware/flash/flash.h
MD5Init / MD5Update / MD5Final / MD5_CTXcommon/md5.h
GetCrcCode_Modbuscommon/crc.h
IWDG_Feedhardware/iwdg/iwdg.h
set_info_statusnet/ems.c:2722
system_hard_resetnet/server.h:285
OSTimeDlyHMSMuCOS-II
g_stEMSInfonet/ems.h:467
g_t_upgrade_run_infonet/ems.h:496
g_au8FWBinnet/server.h:257
START_ADDR_SPI_FLASH_* / APP_MIN_SIZE / APP_MAX_SIZE / FLASH_SECTOR_SIZEnet/ems_download.h、hardware/flash/flash.h
inverter_read_inv_type 等(经 inverter.h、meter.h 间接引入)midware/inverter、midware/meter

6. 依赖关系

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

ems.h、server.h、upd_download.h、ems_download.h、ems_boot_handle.h、bms_download.h、inv_download.h、Meter_download.h、evc_download.h、evc_upgrade.h、board_init.h、md5.h、flash.h、iwdg.h、crc.h、inverter.h、meter.h、afci_download.h。

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

7. 备注与风险点