ems_download.c
calculate_ems_flash_md5()(:120-209)4.2 分块读回 calculate_ems_flash_md5_read_data()(:93-107)4.3 升级配置落盘 save_updt_config_info_to_external_flash()(:63-91)4.4 状态查询与系统级聚合4.5 定时/调度时序与 uCOS-II 配合4.6 关键全局变量的读写与触发条件5. 对外接口本文件导出(声明于 net/ems_download.h:36-42)本文件导出但未在头文件声明的全局函数本文件调用的外部函数 / 变量6. 依赖关系依赖(本文件 include,:13-30)被依赖(会用到本文件导出的符号)7. 备注与风险点源文件路径:
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 | :172 | ems_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 | :180 | Set_Status_Is_Updated_From_Server(UPDT_EMS, TRUE) 置"下载完成待升级"标志 |
| c | :182 | g_stEMSInfo.stUpt[UPDT_EMS].u16_crc_code = GetCrcCode_Modbus((u8*)&g_stEMSInfo.stUpt[UPDT_EMS], sizeof(...) - 2),把表项自身做 Modbus CRC16,结果写进最后 2 字节 |
| d | :184 | save_updt_config_info_to_external_flash(...) 把整个 STUpdateTab 落盘到 SPI Flash CFG_INFO 区 |
| e | :191-192 | OSTimeDlyHMSM(0, 0, 0, 500) 延时 500ms,随后 system_hard_reset() 硬复位,由 Boot 读取配置并完成烧写 |
失败分支(:201-208):仅置 g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error = 1,不做任何重试或清理。
关键点:
- 长度来源是
g_stEMSInfo.stUpt[UPDT_EMS].u32LenFile(由服务器UpdateCheck响应解析得到),不是实际接收字节数。 remainder分支前会memset(g_au8FWBin, 0, sizeof(g_au8FWBin))(:146),确保不足 1024 字节的尾块前半部分是清零而非上一次的残留。IWDG_Feed()只在每个 1KB 块的读取之后调用(:143、:154);MD5Update和Flash_Read都在喂狗间隙内完成,1KB 粒度对看门狗友好。- 读回失败(
Flash_Read三次都非FLASH_OK)时直接return(:139、:150),既不复位也不置错误位,上层无法感知,只是升级流程静默停止。
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;
- 分区选择依赖
upd_download.c里的静态标志s_u8_save_firm_in_app_flash_flag(经get_save_firm_in_app_flash_flag()读取):TRUE读主区(APP1),FALSE读备份区(APP2)。该标志由 Modbus 升级对象UPGRADE_OBJECT_APP2_E或net/ems.c:4660设置。 - 返回值为原始
Flash_Read返回码,不是TRUE/FALSE;调用方用!= FLASH_OK判断(:138、:149)。 size_to_read为u16,本文件最多传 1024,安全;但函数本身不校验size_to_read <= sizeof(g_au8FWBin),若被其它调用方传入超过 1024 的值将越界写g_au8FWBin。
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;
- 长度上限用
FLASH_SECTOR_SIZE(4KB)做保护,与两个 CFG_INFO 区各 4KB 的划分一致(0x0000-0x0FFF、0x103000-0x103FFF)。 - 与固件区一样,落盘目标跟随同一个
s_u8_save_firm_in_app_flash_flag,保证"配置区"和"固件区"始终成对(主区配置 ↔ 主区固件)。 - 只写不擦:
Flash_Write(hardware/flash/flash.h:140)内部是否做扇区擦除由底层实现决定,本文件不额外调用擦除接口。 - 返回
TRUE/FALSE,与save_data_to_fw_area_inFlash()的0/1约定相反,跨文件调用时容易混淆。
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 配合
- 本文件不创建任务、不使用信号量或消息邮箱,全部为同步调用。
- 驱动源有两处:TCP 收包完成时
net/server.c:3785→calculate_ems_flash_md5();Modbus 收包完成时midware/interface/modbus/modbus.c:9336→calculate_ems_flash_md5()。 - 唯一的时序动作是成功路径上的
OSTimeDlyHMSM(0, 0, 0, 500)(:191)——延时 500ms 让网络应答/日志先发出去,然后system_hard_reset()(:192,声明于net/server.h:285)。 - 看门狗在
calculate_ems_flash_md5()内以 1KB 块为粒度喂(:143、:154);但save_updt_config_info_to_external_flash()内一次Flash_Write最多 4KB 且循环最多 3 次,其间不喂狗。 - 断点续传/升级闸门变量(
IsSystemDownload/IsSystemUpgrade)由其它任务异步读取,本文件对它们的写入没有任何临界区保护。
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_md5 | net/server.c:3785(TCP 升级收包完成)、midware/interface/modbus/modbus.c:9336(Modbus 升级收包完成) |
save_ems_server_update_info | net/server.c:3234(check_server_fw_is_updating_for_dev() 中,type == UPDT_EMS 时调用);当前为空实现 |
IsDownloadEMS | hmi/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() |
GetPercentDownloadEMS | hmi/ui/ui_main.c:788(LCD 画进度数字) |
IsSystemDownload | hmi/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 |
IsSystemUpgrade | hmi/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_Server | net/upd_download.c |
ems_upgrade_boot_check | net/ems_boot_handle.c:215 |
IsDownloadBMS | net/bms_download.h:17 |
IsDownloadINV / IsDownloadCHG / IsDownloadInv3 | net/inv_download.h:36-38 |
IsDownloadMeter | net/meter_download.h:19 |
is_download_evc | net/evc_download.h:27(实现于 evc_download.c / evc_upgrade.c) |
IsDownloadAFCI | net/afci_download.h:17 |
Flash_Write / Flash_Read / FLASH_OK | hardware/flash/flash.h |
MD5Init / MD5Update / MD5Final / MD5_CTX | common/md5.h |
GetCrcCode_Modbus | common/crc.h |
IWDG_Feed | hardware/iwdg/iwdg.h |
set_info_status | net/ems.c:2722 |
system_hard_reset | net/server.h:285 |
OSTimeDlyHMSM | uCOS-II |
g_stEMSInfo | net/ems.h:467 |
g_t_upgrade_run_info | net/ems.h:496 |
g_au8FWBin | net/server.h:257 |
START_ADDR_SPI_FLASH_* / APP_MIN_SIZE / APP_MAX_SIZE / FLASH_SECTOR_SIZE | net/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。
被依赖(会用到本文件导出的符号)
net/server.c—calculate_ems_flash_md5(:3785)、save_ems_server_update_info(:3234)、IsSystemDownload/IsSystemUpgrademidware/interface/modbus/modbus.c—calculate_ems_flash_md5(:9336)hmi/ui/ui_main.c—IsDownloadEMS(:782)、GetPercentDownloadEMS(:788)hmi/led.c—IsSystemDownload(:180、:191、:193)、IsSystemUpgrade(:180、:191)midware/bms/bms.c—IsSystemDownload/IsSystemUpgrade(:1658、:1728、:2414、:2415、:2493、:2583、:2676、:2677)midware/ethernt/ethernet.c—IsSystemDownload(:1468)、IsSystemUpgrade(:1518)midware/ethernt/wireless/wireless_driver.c—IsSystemDownload(:243)net/ems.c—IsSystemDownload(:4604、:4620、:4636、:5160)、IsSystemUpgrade(:2861)data/fault_warn_log/fault_warn_log.c—IsDownloadEMS(:1540)data/resume_data/flash/flash_data_store.c—IsDownloadEMS(:2806)
7. 备注与风险点
- 栈越界写(真实缺陷):
char file_Md5[32](:122)配合for (k = 0; k < 16; k++) sprintf(&file_Md5[k * 2], "%02X", md5.digest[k])(:159-162)。k = 15时sprintf在file_Md5[30]、[31]写入 2 个十六进制字符后,还会在file_Md5[32]写入'\0',越界 1 字节。实际是否致命取决于栈布局,属未定义行为,net/upd_download.c:404的calculate_dev_FW_Flash_MD5()存在完全相同的写法。 - 除零风险:
GetPercentDownloadEMS()(:230)分母get_length_file_updating(UPDT_EMS)未做零判断。hmi/ui/ui_main.c:782-788的判断顺序是IsDownloadEMS()为真就画进度,而bIsUpdateForce在文件长度尚未解析时也可能为真,此时除零 → Cortex-M4 除零默认不产生异常但结果不确定(/0返回 0 或触发 UsageFault,取决于SCB->CCR.DIV_0_TRP)。 - 空桩函数:
save_ems_server_update_info()(:47-50)与GetPercentUpgradeEMS()(:239-243)均为空/常量返回,是历史遗留;前者仍在net/server.c:3234被调用,后者无调用方。 - 未声明的全局函数:
calculate_ems_flash_md5_read_data(:93)与GetPercentUpgradeEMS(:239)未进ems_download.h,破坏"头文件即接口契约"的约定。 - 静默失败:读回失败时
calculate_ems_flash_md5()直接return(:139、:150),既不置md5_error也不置download_error,上层与云端均无法区分"MD5 不匹配"和"读 Flash 失败",只能观察到升级流程停在原地。 - 返回值约定不统一:
save_updt_config_info_to_external_flash返回TRUE/FALSE,而同工程的save_data_to_fw_area_inFlash、Save_Data_To_FW_Area_InSdram(net/upd_download.c:137、:194)返回0=成功 / 1=失败,跨文件调用时极易判反。 - 文件头注释完全错误:文件头写的是
@file :BMSUpdate.c、@date :2017/05/18(:2-5),与实际内容(EMS 固件下载)和文件实际日期不符,是历史拷改遗留。net/ems_download.h:2同样写着BMSUpdate.h,作者字段还出现了错误的标识符Alpha g_stEMSInfo Team。 - 魔数字面量:
1024(分块大小,多处)、16(MD5 digest 长度)、32(MD5 十六进制串长度)、352*1024(在ems_boot_handle.c)、500(复位前延时毫秒)均为裸数字。 - CRC 覆盖范围依赖结构体无填充:
GetCrcCode_Modbus((u8 *)&g_stEMSInfo.stUpt[UPDT_EMS], sizeof(...) - 2)(:182)假设STUpdateTab无尾部填充且 CRC 字段正好是最后 2 字节(net/server.h:235)。该结构体未加#pragma pack,当前字段排布恰好满足,但新增字段时极易破坏。 - 无锁:
IsSystemDownload()/IsSystemUpgrade()读的bIsUpdateForce/bIsUpdatedFromServer由其它任务写,本文件不做临界区保护;u8单字节读在 Cortex-M4 上是原子的,风险可控,但语义上的"读-判断-动作"竞态(例如 BMS 加热逻辑读到 0 后又立刻进入升级)仍存在。 - 两处 CFG_INFO 区不一致:主用配置区在
0x00000000(紧邻 Flash 起始),备份配置区在0x00103000;而固件主区在0x00003000、备份区在0x00106000,四个区域之间留有0x1000~0x3000的空洞,属于地址规划的显式余量,改动时需保持对称。