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

system_config.c

源文件路径:data/system_config/system_config.c 所属模块:data/system_config(系统配置持久化:出厂值初始化、上电恢复、异步保存调度) 代码行数:3379

1. 文件职责

本文件是 EMS 全部配置数据的"落盘与还原中枢",负责三件事:

一是出厂值初始化。24 个 *_info_init() 函数把每一类配置(系统基础信息、逻辑配置及三个时间控制扩展、电表、电池及电池扩展、逆变器、CT 配置与 CT 电量、能量统计、干接点、网络、Modbus、EVC 一代/二代、自定义配置与其备份、系统备份)填成出厂默认值、算好 Modbus CRC16 后整块写进内部 Flash 的 EEPROM 模拟区(页 4071~4095,每页 4 KB)。

二是上电恢复。system_info_restore()(system_config.c:2371)在 system/board_init/board_init.c:250 被调用,逐页读取并校验 CRC,成功则灌进对应的 RAM 全局变量,失败则回退调用同页的 *_init() 出厂值;同时处理跨页的兼容修补(老版本字段迁移)和"Flash 备份页 4095 与 4071/4077/4075 互救"逻辑。

三是异步保存调度。它创建 uCOS-II 消息队列 SaveJob_Q 与内存分区 SaveJobMsgBuffer(save_job_init,:2602),对外提供 save_job_post(page) 投递"某页需要保存"的任务(带去重位图),由 data/data.c 的 task_system_data 任务调用 system_config_data_save()(:2894)从队列取任务并真正写 Flash,写前重算 CRC,写后清业务侧的"配置已改"标志并更新 EEPROM 读/写故障标志。

被谁驱动:初始化/恢复由 board_init 与 data.c 的复位流程驱动;保存由全工程 131 处 save_job_post 调用点(hmi/key.c、net/server.c、logic/logic.c、midware/* 等)驱动,最终由 task_system_data 每 500 ms 拉一次队列执行。

2. 关键数据结构与宏

名称类型(结构体/枚举/宏/联合体)说明
SaveJob_Q全局变量 OS_EVENT *保存任务消息队列,容量 MESSAGES_SIZE(=25),save_job_init 中 OSQCreate,事件名 "SaveJob_Q"(:38、:2606)
SaveJobMsgGrp全局数组 void *[MESSAGES_SIZE]OSQCreate 的队列控制块存储(:39、:2606)
SaveJobMsgBuffer全局变量 OS_MEM *保存任务报文的内存分区,块大小 sizeof(SAVE_JOB_T)(=11 字节),块数 25(:40、:2618)
SaveJobMsgMem全局数组 SAVE_JOB_T[MESSAGES_SIZE]内存分区的实际存储(:41、:2618)
g_t_system_base_info全局变量 SYSTEM_BASE_PARA_T4071 页 RAM 镜像:SN、语言、用户密码、OEM 标志、功能位 1/2、E-Link 密码、安装日期、上报间隔(:43)
g_system_custom_info全局变量 STSTEM_CUSTOM_PARA_T4088 页 RAM 镜像:显示/登录/socket 服务器配置(:44)
g_system_function_info全局变量 SYSTEM_FUNCTION_PARA_T运行期功能能力表,本文件只声明与比较,填充在 midware/auxiliary/system_aux.c(:45)
g_system_msg_page_id全局变量 SYSTEM_MESSAGES_PAGE_ID_U保存任务去重位图,初值 {0}(:46)
s_system_backup_infostatic 变量 SYSTEM_BACKUP_TABLE_T4095 页的 RAM 镜像(SN/MAC/逆变器型号/服务器点/IP 配置)(:48)
s_system_custom_backup_infostatic 变量 STSTEM_CUSTOM_PARA_T4090 页的 RAM 镜像,g_system_custom_info 的备份(:49)
SAVE_JOB_T结构体队列报文:page_id(位号)、page(页号)、tim_s(投递时刻)、tim(耗时 tick),见 system_config.h:66-72
MESSAGES_SIZE宏25(system_config.h:49),队列长度 / 内存块数 / save_job_post 位号上界
SYSTEM_STARTE_PAGE宏4071(system_config.h:22),页号→位号换算基准
CONFIG_INFO_HEAD宏0xAA55(system_config.h:20),配置块头标识
DEBUG_SYSTEM_CONFIG宏0(system_config.h:18),打开后各函数打印 CRC 与 ShowData() 十六进制转储

3. 函数清单

函数名作用域(static/全局)说明(一句话)
system_sn_initstatic生成默认 SN、用户密码、英文语言并写入 4071 页(:89)
net_info_init全局(原型误写成 static,见风险点 1)网络信息出厂初始化,保留 MAC 与服务器点后写入 4077 页(:130)
ct_config_info_initstaticCT 配置出厂初始化:A/B/C 电压 2 点、电网/光伏功率各 5 点校准系数置 10000,写 4080 页(:176)
ui_info_init全局把 LCD 里的密码与语言写回 4071 页(:256)
logic_config_info_init全局逻辑配置主表出厂值,写 4072 页(:291)
logic_config_extend1_info_init全局时间控制扩展 1(14 段充放电)出厂值,写 4091 页(:345)
logic_config_extend2_info_init全局时间控制扩展 2 出厂值,写 4092 页(:382)
logic_config_extend3_info_init全局时间控制扩展 3 出厂值,写 4093 页(:419)
meter_config_info_init全局电表配置出厂值(BB 优先级 1/2/3),写 4073 页(:451)
bat_config_info_init全局电池配置出厂值,写 4074 页(:494)
bat_extend_config_info_init全局电池扩展配置出厂值,写 4087 页(:526)
inverter_config_info_init全局逆变器配置出厂值,保留逆变器型号、写硬件版本、安全法规 AS4777_2,写 4075 页(:557)
ct_energy_info_init全局CT 累计电量清零并写 4081 页(:595)
energy_info_init全局能量统计清零并写 4076 页(:631)
dry_node_info_init全局干接点配置出厂值,写 4078 页(:662)
net_ip_method_init全局网络地址方式复位为 DHCP 并写 4077 页(:698)
modbus_config_info_init全局Modbus 配置出厂值(地址、9600、Sunspec 使能、版本 1),写 4079 页并调 Sunspec 数据初始化(:729)
evc_config_info_init全局EVC 一代充电桩配置出厂值,写 4082 页(:772)
evc_setting_info_init全局EVC 充电参数出厂值(优先级 1、三相、160 A),写 4083 页(:812)
evc_ii_single_config_info1_init全局EVC 二代第 1 路出厂值,写 4084 页(:851)
evc_ii_single_config_info2_init全局EVC 二代第 2 路出厂值,写 4085 页(:890)
evc_ii_single_config_info3_init全局EVC 二代第 3 路出厂值,写 4086 页(:929)
evc_ii_single_config_info4_init全局EVC 二代第 4 路出厂值,写 4089 页(:968)
system_custom_info_init全局自定义配置出厂值,写 4088 页(:1007)
system_custom_backup_info_init全局由 g_system_custom_info 生成备份并写 4090 页(:1040)
system_backup_info_init全局以 EEPROM 现状为准生成 4095 页备份数据(SN/MAC/型号/服务器点/IP)(:1073)
system_sn_restorestatic读取并校验 4071 页,安装商密码降级为用户密码,恢复 g_t_system_base_info(:1128)
logic_config_info_restorestatic读取并校验 4072 页,恢复逻辑配置并同步 g_t_time_control_run.weekday_charge_discahrge_en,做 3 项字段兜底(:1185)
logic_config_extend1_info_restorestatic读取并校验 4091 页,恢复并把 14 段时间控制拷进 g_t_time_control_run(:1247)
logic_config_extend2_info_restorestatic读取并校验 4092 页,同上(:1297)
logic_config_extend3_info_restorestatic读取并校验 4093 页,恢复逻辑扩展 3(:1346)
meter_config_info_restorestatic读取并校验 4073 页,恢复电表配置及四项分相累计电量(:1392)
bat_config_info_restorestatic读取并校验 4074 页,恢复电池配置并同步到 t_run_info(含老版本字段清理)(:1443)
bat_extend_config_info_restorestatic读取并校验 4087 页,恢复 12 节电池单体的软件版本/平台/SN(:1509)
inverter_config_info_restorestatic读取并校验 4075 页,恢复逆变器配置(:1562)
ct_energy_info_restorestatic读取并校验 4081 页,恢复 CT 累计电量(:1607)
energy_info_restorestatic读取并校验 4076 页,恢复能量统计(:1670)
net_info_restorestatic读取并校验 4077 页,恢复 IP_Address(:1727)
dry_node_info_restorestatic读取并校验 4078 页,恢复干接点配置(:1773)
modbus_config_info_restorestatic读取并校验 4079 页,恢复 Modbus 配置(Sunspec 版本为 0 时强制走重初始化)(:1820)
ct_config_info_restorestatic读取并校验 4080 页,恢复 CT 校准表(老版本缺 B/C 相校准时补齐)(:1873)
evc_config_info_restorestatic读取并校验 4082 页,恢复 EVC 一代配置(:1954)
evc_setting_info_restorestatic读取并校验 4083 页,恢复 EVC 充电参数(:2001)
evc_ii_single_config_info1_restorestatic读取并校验 4084 页,恢复 EVC 二代第 1 路(:2047)
evc_ii_single_config_info2_restorestatic读取并校验 4085 页,恢复 EVC 二代第 2 路(:2094)
evc_ii_single_config_info3_restorestatic读取并校验 4086 页,恢复 EVC 二代第 3 路(:2141)
evc_ii_single_config_info4_restorestatic读取并校验 4089 页,恢复 EVC 二代第 4 路(:2187)
system_custom_info_restorestatic读取并校验 4088 页,失败时直接重建 4088 页(:2234)
system_custom_backup_info_restorestatic读取并校验 4090 页,失败时重建备份页(:2280)
system_backup_info_restorestatic读取并校验 4095 页,失败时以当前 EEPROM 内容重建(:2326)
system_info_restore全局上电恢复总调度:逐页恢复/回退出厂值 + 跨页备份修复(:2371)
save_job_init全局创建保存队列与内存分区,并设置事件名(:2602)
atomic_check_and_set_bitstatic inline用 __LDREXW/__STREXW 原子实现"测试并置位",已置位返回 1(:2637)
atomic_clear_bitstatic inline用 __LDREXW/__STREXW 原子清位(:2666)
save_job_post全局去重、取内存块、投递队列;失败时回退位图(:2688)
save_job_pendstatic队列取报文、累加耗时、清位图、归还内存块(:2740)
get_backup_data_refresh_flagstatic比较 4095 页镜像与当前 SN/MAC/型号/服务器点/IP 是否一致,不一致返回 TRUE(:2780)
save_energy_data全局一次性投递能量、CT 电量、电表三页保存任务(:2880)
system_config_data_save全局保存主循环:定时能量保存、备份页刷新检测、按页号分派写 Flash 与善后(:2894)

合计 59 个函数(其中 static inline 2 个,static 28 个,全局 29 个)。

4. 核心逻辑解析

4.1 保存任务机制(队列 + 内存分区 + 原子去重位图)

同步对象:save_job_init()(:2602)用 OSQCreate(SaveJobMsgGrp, MESSAGES_SIZE) 建队列、OSMemCreate(SaveJobMsgMem, MESSAGES_SIZE, sizeof(SAVE_JOB_T), &err) 建内存分区,两者都以失败仅打印不返回错误的宽松方式处理;随后 OSEventNameSet / OSMemNameSet 设置调试名。该函数由 data/data.c:257 在任务启动时调用一次。

投递路径 save_job_post(u16 msg)(:2688):

  1. g_u8_msg_id = msg - SYSTEM_STARTE_PAGE,把页号换算成 0..24 的位号。
  2. 越界检查 + 去重:(g_u8_msg_id > MESSAGES_SIZE) || (atomic_check_and_set_bit(...) != 0) 即返回 0。位已被置说明该页已有排队中的任务,直接丢弃这次请求——这是"同一页最多一个待保存任务"的合并策略。
  3. OSMemGet 取报文块,失败则 atomic_clear_bit 回退位图并返回 0。
  4. 填 page_id / page / tim_s = OSTimeGet(),OSQPost 入队。队列满(OS_ERR_Q_FULL)时回退位图;成功则返回 1。

重试次数限制:atomic_check_and_set_bit 内部 l_u16_retry = 1000,__STREXW 失败最多重试 1000 次,返回 2 表示"重试用尽"。三个返回码(0 成功、1 已置位、2 重试失败)在 save_job_post 里被统一当成非零即失败处理(:2695)。

取用路径 save_job_pend(void *msg)(:2740):OSQPend(SaveJob_Q, 1, &err) 超时仅 1 个 tick(OS_TICKS_PER_SEC=1000,即 1 ms),因此是"准非阻塞"轮询;成功时把 page 写到调用者的 u16 变量、算出 tim = OSTimeGet() - tim_s 作为耗时统计、原子清位图、OSMemPut 归还内存块。

主循环 system_config_data_save()(:2894):

4.2 上电恢复状态机(system_info_restore)

system_info_restore()(:2371)是一段顺序执行的"逐页恢复 → 失败回退"链表,无显式状态变量,但用 4 个标志位记录"哪些页是从坏数据回退的",供后续备份修复使用:l_u8_sn_read_eeprom_flag、l_u8_net_read_eeprom_flag、l_u8_inv_read_eeprom_flag、l_u8_custom_read_eeprom_flag(初值均为 TRUE)。

迁移条件(按页号升序):

页恢复函数失败回退
4071system_sn_restoresystem_sn_init,并置 l_u8_sn_read_eeprom_flag = FALSE
4072logic_config_info_restorelogic_config_info_init
4073meter_config_info_restoremeter_config_info_init
4074bat_config_info_restorebat_config_info_init
4087bat_extend_config_info_restorebat_extend_config_info_init
4075inverter_config_info_restoreinverter_config_info_init,置 l_u8_inv_read_eeprom_flag = FALSE
4081ct_energy_info_restorect_energy_info_init
4076energy_info_restoreenergy_info_init
4077net_info_restorenet_info_init,置 l_u8_net_read_eeprom_flag = FALSE
4078dry_node_info_restoredry_node_info_init
4079modbus_config_info_restoremodbus_config_info_init
4080ct_config_info_restorect_config_info_init
4082/4083evc_config_info_restore / evc_setting_info_restore对应 *_init
4091/4092/4093logic_config_extend1/2/3_info_restore对应 *_init
4084/4085/4086/4089evc_ii_single_config_info1..4_restore对应 *_init
4088 / 4090system_custom_info_restore / system_custom_backup_info_restore见下
4095system_backup_info_restoresystem_backup_info_init,并 ems_set_fault_bit(13, TRUE)(EMS_FAULT_FLASH_FAULT,:2530)

三个跨页修复规则是本函数的"精华":

  1. 自定义配置互救(:2509-2526):若 4088 页读失败(l_u8_custom_read_eeprom_flag == FALSE)但 4090 页备份读成功,就用备份内容重建 g_system_custom_info 并写回 4088 页。
  2. 系统备份反哺三页(:2528-2591):只有 4095 页读成功时才生效——SN 读失败则用备份里的 SN 补进 4071 页并写回(用 feeprom_write,因为 4071 页里还有当前 RAM 的其它字段,不能整页覆盖);逆变器型号丢失则 set_inverter_type(备份型号) 后重写 4075 页;网络页读失败则把 MAC/IP 方法/服务器点/IP/掩码/网关全量补进 IP_Address 并写回 4077 页。
  3. Flash 故障上报:4095 页也坏了就置 bit13,让上层出 EMS_FAULT_FLASH_FAULT 告警。

单个 *_restore 的统一实现骨架(以 system_sn_restore(:1128)为例):

for (i = 0; i < 3; i++) {
    l_u8_res = feeprom_read(&l_t_buf, PAGE, size);
    if (l_u8_res == TRUE) {
        l_u16_crc = GetCrcCode_Modbus(&l_t_buf, size - 2);
        if (l_u16_crc == l_t_buf.u16_crc_code) {
            memcpy(目标全局变量, &l_t_buf, size);
            ...(兼容修补)...
            set_eeprom_fault_flag(读故障标志, PAGE, FALSE);
            return TRUE;
        }
    }
}
set_eeprom_fault_flag(读故障标志, PAGE, TRUE);
return FALSE;

关键点:最多试 3 次(应对 feeprom_read 内部偶发读失败),每次都是独立读同一地址——因为 feeprom_read 只在读取失败时才可能触发一次"槽位回卷"的擦写,所以重试有实际意义。

4.3 恢复期的字段兼容修补(老版本数据迁移)

恢复函数不只是拷贝,还内嵌了大量"历史版本补丁":

位置条件动作
system_sn_restore:1160u16_user_password == INSTALLER_PASSWORD降级为 USER_PASSWORD(防止安装商密码被固化)
logic_config_info_restore:1209GCRatePercent == 0置 80(默认放电倍率百分比)
logic_config_info_restore:1214storage_grid_breaker_specs == 0置 32(电网侧空开规格)
logic_config_info_restore:1219t_g100_info.u8_system_state > 4 或 < 1钳到 1(英国 G100 系统状态合法域 1..4)
bat_config_info_restore:1468u8_reserved_flag == FALSE(老版本无此标志)清空 u8_dod、u16_cell_rate_volt、u16_cell_rate_capacity、u8_series_para_state、s_type_splice、u8_reserved1,再置 TRUE
ct_config_info_restore:1894u8_reserved_flag == FALSE把 B/C 相电压(2 点)与电网/光伏 B/C 相功率(各 5 点)的校准系数重置为 point=0、coef=10000、offset=0,再置 TRUE
modbus_config_info_restore:1842u16_sunspec_addr == 0置 ADDR_SUNSPEC_MODBUS
modbus_config_info_restore:1846u16_sunspec_Ver == 0break 跳出循环 → 返回 FALSE → 上层走 modbus_config_info_init() 全量重初始化("版本 0 即不可用"的强判据)

这些修补保证"老设备升级固件后配置不炸",但也意味着恢复函数的返回值语义被复用:modbus 的 break 是为了强制重初始化,而不是真的读失败。

4.4 出厂值初始化的写入策略与故障记账

所有 *_init() 的骨架一致:

  1. memset(&目标结构体, 0, sizeof)(个别函数如 system_custom_backup_info_init/system_backup_info_init 走赋值而非清零)。
  2. 填 u16_head = CONFIG_INFO_HEAD。
  3. 填默认业务值(例:logic_config_info_init 设 u8_system_mode = DC_SYSTEM_MODE、u8_max_feedin_percent = 100、Charger_Cut_SOC = 100、UPS_Reserve_SOC = 10、GCRatePercent = 80、storage_grid_breaker_specs = 32、u8_ongrid_low_soc = 25、u8_offgrid_low_soc = 50、s32_grid_power_limit = 69000、馈网百分比 100/60/30/0,见 :303-318;PV_Capacity_Storage 取逆变器额定放电功率 inverter_read_rated_dc_discharge_power)。
  4. u16_crc_code = GetCrcCode_Modbus(..., size - 2)。
  5. 最多 3 次 feeprom_init_write()(擦扇区 + 写,覆盖语义),成功即 break。
  6. set_eeprom_fault_flag(get_eeprom_software_write_fault_flag(), PAGE, !l_u8_res) 记账。

注意 net_ip_method_init(:698)的记账写法与其它函数不同:它写的是 if (l_u8_res == FALSE) set_eeprom_fault_flag(..., TRUE);,即失败才置位,成功时不清位——与其它页的 !l_u8_res 语义不对称(成功时清位)。

另外 *_init() 里对 l_u8_res 的判据有两套:大部分用 == TRUE,而 system_custom_info_init、system_custom_backup_info_init、system_backup_info_init 三处用 == FLASH_OK(:1028、:1061、:1116)。已核对定义:FLASH_ERR = 0, FLASH_OK = 1, FLASH_BSY = 2(hardware/flash/flash.h:105-110),TRUE = !FALSE = 1(system/sys/chip.h:64),两者数值相同,因此当前行为一致;但混用写法在枚举顺序被改动时会静默失效。

4.5 特殊初始化函数的额外副作用

4.6 备份刷新检测(get_backup_data_refresh_flag)

get_backup_data_refresh_flag()(:2780)逐项比较 4095 页镜像与当前运行值,任何一项不同即返回 TRUE 并触发一次备份页写入:

  1. s8_sn[16] 与 g_t_system_base_info.s8_sn;
  2. u8_mac[6] 与 IP_Address.u8_mac_addr;
  3. u8_inv_type 与 get_inverter_type();
  4. u8_server_point 与 IP_Address.u8_server_point;
  5. u8_ip_method 与 IP_Address.IPmethod;
  6. 且仅当 IP_Address.IPmethod == IP_METHOD_MANUAL 时才比较 IP/网关/掩码 12 个 u16(DHCP 模式下这些字段无效,跳过比较以避免无意义的反复备份写)。

这一函数与 system_config_data_save() 中的备份写动作构成"变更即备份"闭环:任何 SN/MAC/IP/型号变化都会在 500 ms 内被投递保存一次。

4.7 与 uCOS-II 的配合汇总

同步对象创建/使用位置语义
SaveJob_Q(消息队列)save_job_init:2606 创建;save_job_post:2712 投递;save_job_pend:2746 接收生产者是任意任务/中断外的调用者,消费者是 task_system_data
SaveJobMsgBuffer(内存分区)save_job_init:2618 创建;OSMemGet:2698 / OSMemPut:2756避免在队列里传大结构,只有 11 字节报文
g_system_msg_page_id(原子位图,非 OS 对象)__LDREXW/__STREXW 独占访问跨任务去重;不用关中断也能保证一致性
OSTimeGet()save_job_post:2710、save_job_pend:2750、system_config_data_save:2905记录投递时刻、计算单页保存耗时、720 s 定时
IWDG_Feed()本文件未调用喂狗在调用方(data/data.c 的复位流程)中穿插

5. 对外接口

导出的函数(system_config.h:808-836 声明,除 net_info_init 原型写在 .c 内部):

名称使用者
system_info_restoresystem/board_init/board_init.c:250
save_job_initdata/data.c:257
save_job_post全工程 *.c 中 131 处匹配(其中 1 处是 system_config.c:2688 的定义,其余约 130 处为调用),分布在 18 个文件:data/data.c、data/resume_data/flash/flash_data_store.c、hmi/key.c、logic/logic.c、net/server.c、midware/auxiliary/system_aux.c、midware/bms/bms.c、midware/interface/interface.c、midware/inverter/*(alphaess/ginlong/goodwe/inovance/kelong/set)、midware/meter/meter.c、midware/meter/ct.c
save_energy_datadata/data.c:269/302
system_config_data_savedata/data.c:277/306
ui_info_init、logic_config_info_init、logic_config_extend1/2/3_info_init、meter_config_info_init、bat_config_info_init、bat_extend_config_info_init、inverter_config_info_init、dry_node_info_init、net_ip_method_init、modbus_config_info_init、evc_config_info_init、evc_setting_info_init、evc_ii_single_config_info1..4_init、system_backup_info_initdata/data.c 的 factory_reset_handle(:94-144,实测调用行 :100-138)
system_custom_info_init、system_custom_backup_info_initdata/data.c 的 custom_info_reset_handle(:55-65,实测 :60、:62)
energy_info_initdata/data.c:80(energy_reset_handle)
ct_energy_info_init全工程无外部调用者,仅在本文件 system_info_restore:2421 的 4081 页回退中使用(原型仍导出于 system_config.h:817)
net_info_init仅本文件内部 system_info_restore:2433 使用(非 static 却无外部调用者)

导出的全局变量:SaveJob_Q、SaveJobMsgGrp、SaveJobMsgBuffer、SaveJobMsgMem(未在头文件声明,仅本文件使用)、g_t_system_base_info、g_system_custom_info、g_system_function_info、g_system_msg_page_id。

本文件调用的外部函数/变量(按 include 分组,:13-34):

6. 依赖关系

7. 备注与风险点

  1. net_info_init 的 static 声明与非 static 定义冲突::54 声明 static void net_info_init(void);,而 :130 定义 void net_info_init(void),同时 system_config.h:831 又给出非 static 的原型。这是一个真实的语言层面矛盾(严格编译器会报错),说明该函数原本设计为私有、后来被提升为公开接口,但旧声明忘了删。修改时需同时动两处。
  2. save_job_post 的越界判断是 > 而不是 >=::2695 写 (g_u8_msg_id > MESSAGES_SIZE)。合法位号是 0..24,MESSAGES_SIZE=25,因此 g_u8_msg_id == 25 会通过检查并去置位位 25——虽然位 25 仍在 32 位联合体内(不会越界),但它是一个没有对应页面的保留位。若调用者传入 SYSTEM_STARTE_PAGE + 25,就会产生永远无法被 save_job_pend 清掉的悬挂位,最终导致该位后续再也不会被设置。建议改为 >=。
  3. l_u16_save_cnt 未在每轮调用时复位:它在 system_config_data_save() 内定义为局部变量并初值 0(:2901),每次调用重新初始化,行为正确;但其累加判断 >= 6 位于循环末尾,实际最多处理 7 页,与注释"每页 10 ms"一起暗示了单次调用耗时上限约 70 ms,属于隐式时间预算。
  4. 掉电时在页间关闭网络电源::3367 依据 PG10 电平关闭 W5500/WiFi 电源,但 system_config_data_save() 也在正常运行态(task_system_data 超时分支)被调用。若因某种原因 PG10 长期为高而系统并未真正掉电,W5500/WiFi 会被反复关断,只能靠 data/data.c:281 的 W5500_Power_ON()/wifi_power_on() 在同轮末恢复——依赖"每轮必然执行到恢复语句",若中途 Sys_Soft_Reset()(ems_reset_handle)触发则不复电。
  5. l_u8_res 可能未初始化使用:SYSTEM_BASE_PARA_PAGE、LOGIC_CONFIG_PAGE 等 case 中,l_u8_res 每次循环都被 feeprom_write 赋值,无问题;但 system_custom_info_init(:1025-1030)、system_custom_backup_info_init(:1058-1063)、system_backup_info_init(:1113-1118)三处循环结束后 未使用 l_u8_res 记账,即这三页的写失败不会被记录到 EEPROM 故障标志,与其它页不一致。
  6. TRUE 与 FLASH_OK 混用(本次核对后确认数值等价,属风格问题):system_custom_info_init:1028、system_custom_backup_info_init:1061、system_backup_info_init:1116 以及 system_info_restore:2250/2296/2342 用 == FLASH_OK 判写成功,其余全部用 == TRUE。已核对定义:hardware/flash/flash.h:107-110 的枚举是 FLASH_ERR = 0, FLASH_OK, FLASH_BSY,即 FLASH_OK == 1;system/sys/chip.h:64 的 TRUE = !FALSE = 1。因此当前行为一致,不构成缺陷,但仍建议统一写法,避免以后有人改枚举顺序时静默失效。
  7. net_ip_method_init 的故障标志语义不对称::718-719 只在失败时置位,成功时不清位;而其它页是 set_eeprom_fault_flag(..., !l_u8_res) 双向记账。结果是网络页写成功后,之前的软件写故障位不会被清除,EEPROM 故障告警可能"粘住"。
  8. clear_eeprom_fault_temp 与 set_eeprom_fault_flag 的范围不一致:data/data.c:192-201 循环 i < 32 而合法页范围只到 4095(位 24),后 7 次迭代被 set_eeprom_fault_flag 的 addr > SYSTEM_DATA_BACKUP_PAGE 保护静默丢弃(本文件未参与,但同属该机制)。
  9. atomic_check_and_set_bit 的 return 2 未被区分::2657 返回 2 表示 STREX 重试 1000 次仍失败(说明有极高并发的独占竞争),但 save_job_post 把 2 与 1 一样当作"已有任务"静默丢弃,可能造成该页长期不被保存而无人知晓(无日志、无故障标志)。
  10. system_info_restore 依赖 IP_Address 已具备部分初值:net_info_init 在清零前先备份 IP_Address.u8_mac_addr 与 u8_server_point(:141-149),要求调用时该结构体里已经有 MAC(通常来自硬件/其它页)。若上电流程顺序变化导致 IP_Address 全 0,会写出一张全 0 MAC 的网卡配置,且因为 CRC 正确、head 正确,后续不会再被修复。
  11. STSTEM_CUSTOM_CONFIG_PAGE 的 case 未做写失败记账::3254-3266 只 break,没有 set_eeprom_fault_flag;同属 default 之外却缺少统一善后,属于不一致。
  12. sys_info_restore 中 l_u8_custom_read_eeprom_flag 的双重语义:它先被赋成 system_custom_info_restore() 的返回值(TRUE 表示读成功,:2509),而 :2512 又用 == FALSE 表示"读失败需要从备份恢复",与前三页的 read_eeprom_flag(TRUE 表示读成功,FALSE 后用来触发修复)语义相反,容易误读。
  13. DEBUG_SYSTEM_CONFIG 关闭时 ShowData、printf 全部被条件编译剔除,但 system_sn_restore 中 l_u16_crc 变量在关闭调试时只在 CRC 比较处使用(:1155),不会产生未使用告警;而 dry_node_info_init:677 的调试打印引用了 g_t_evc_config_table.u16_crc_code(EVC 结构),属于复制粘贴留下的错误调试信息。
  14. evc_ii_single_config_info1_init 的调试打印索引写错::869-870 打印与 ShowData 用的是 g_t_evc_ii_single_config[1],而该函数初始化的是 [0](:861-866)。仅在 DEBUG_SYSTEM_CONFIG == 1 时可见,但会误导调试。
  15. 魔法数字密集:ems_set_fault_bit(13, TRUE)(:2530)、720 * 1000(:2905)、l_u16_save_cnt >= 6(:3373)、32/80/10000/31 等默认值均无具名常量;memcpy(..., 31)(:1487)与 s_type_splice 数组长度的耦合也不明显。