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

data.c

源文件路径:data/data.c 所属模块:data(数据管理 / EMS 故障位与复位调度中枢) 代码行数:449

1. 文件职责

本文件是 EMS 的"系统数据任务 + 全局复位请求分发"实现。它定义一个 uCOS-II 任务 task_system_data,由 user/main.c:156 以优先级 TASK_SYSTEM_DATA_PRIO(=7u,见 UCOSII/uCOS-CONFIG/app_cfg.h:58) 静态创建,任务是"低频管家":等待掉电信号量 g_t_power_down_sem,超时(500 个 tick)后执行周期性的掉电续传处理、各种复位请求处理、配置落盘与 EEPROM 故障标志汇总。

它同时持有 EMS 层故障位集合 g_t_ems_info(EMS_TAB_T,32 位按位定义的故障字),对外提供 ems_read_fault_code() / ems_set_fault_bit() 两个访问口,全工程约 30 个模块用它置/清 EMS 侧故障位,data/fault_warn_log/fault_warn_log.c:516 靠比较该字的变化来产生故障日志。

它还向上层提供"延迟执行的复位请求"接口:meter_reset() / custom_info_reset() / energy_reset() / factory_reset() / set_ems_reset() / resume_flash_reset() 都只置一个 static 标志位,真正耗时的 Flash 擦除与配置重初始化在系统数据任务里串行执行,避免在按键、网络或 Modbus 等上下文中做长耗时写 Flash 操作。

数据流位置:hmi/key.c、net/server.c、midware/interface/modbus/modbus.c 等发起复位请求 → 本文件置标志 → task_system_data 轮询标志 → 调用 data/system_config、data/fault_warn_log、data/resume_data 中的初始化/擦除函数 → 最终软复位或继续运行。

2. 关键数据结构与宏

名称类型说明
DEBUG_DATA宏取值 0(data/data.h:16)。为 1 时在任务循环内累加 g_t_task_run_status.u32_task_system_data_cnt 供看门狗监视打印
ErrLog_t结构体#pragma pack(1) 打包,仅含 u8 p_write; u8 cnt;(data/data.h:20-25)。原设计的 u8 LogBuf[10][16] 被注释掉。全局实例 error_log 实际只用到 cnt(历史故障日志条数,由 fault_warn_log 填),p_write 在本工程中未被任何代码引用
EMS_FAULT_E枚举EMS 故障码序号表(data/data.h:27-65),共 30 项 EMS_FAULT_*,值 0..29,与 EMS_FAULT_U 的位序号一一对应。注释标注"最多 32 个 bit"
EMS_FAULT_U联合体u32 u32_all / u8 byteData[4] / 位域 bit(30 个具名 1 位成员 + bit30/bit31 保留),见 data/data.h:67-114。位域按 BYTE0..BYTE3 每字节 8 位排列,小端 MCU 上位 0 即 u8_net_fault
EMS_TAB_T结构体仅包含一个 EMS_FAULT_U u_ems_fault(data/data.h:116-119),全局实例 g_t_ems_info
g_t_ems_info全局变量EMS 故障字,data/data.c:31 定义,声明于 data/data.h:123
error_log全局变量ErrLog_t 实例,data/data.c:32 定义,声明于 data/data.h:122
s_u8_custom_info_reset_flagstatic 变量自定义信息复位请求标志(data/data.c:34)
s_u8_energy_reset_flagstatic 变量能量数据清零请求标志(data/data.c:35)
s_u8_factory_reset_flagstatic 变量恢复出厂设置请求标志(data/data.c:36)
s_u8_ems_reset_flagstatic 变量EMS 软复位请求标志(data/data.c:37)
s_u8_ems_erase_run_data_flagstatic 变量擦除运行数据(续传数据)请求标志(data/data.c:38)

3. 函数清单

函数名作用域说明
custom_info_reset_handlestatic若 s_u8_custom_info_reset_flag 置位,则重建自定义配置并同步备份页(data/data.c:55)
energy_reset_handlestatic若 s_u8_energy_reset_flag 置位,延时 10s 后清零能量页并擦除续传运行数据(data/data.c:74)
factory_reset_handlestatic若 s_u8_factory_reset_flag 置位,逐页重初始化全部配置、清故障日志、延时 40s 后软复位(data/data.c:94)
ems_reset_handlestatic若 s_u8_ems_reset_flag 置位则立即 Sys_Soft_Reset()(data/data.c:154)
erase_flash_handlestatic若 s_u8_ems_erase_run_data_flag 置位,延时 10s 后调用 erase_run_data()(data/data.c:169)
clear_eeprom_fault_tempstatic对 32 个页逐个比较"软件读故障"与"软件写故障",读故障已清而写故障未置时把该页读故障标志清掉(data/data.c:187)
eeprom_fault_handlestatic汇总硬件读/写、软件读/写四组 EEPROM 故障位,位图变化时更新 EMS_FAULT_EEPROM_FAULT 位(bit2)并上报 set_info_status(EEPROM_FAULT)(data/data.c:211)
task_system_data全局系统数据任务主体,等待掉电信号量并在超时/掉电两种分支下完成保存与复位处理(data/data.c:251)
ems_read_fault_code全局返回 g_t_ems_info.u_ems_fault.u32_all(data/data.c:326)
ems_set_fault_bit全局按位序号 index(0..31) 置位/清位 EMS 故障字(data/data.c:338)
meter_reset全局清电表与 CT 的累计电量、分相电量,并投递 METER_CONFIG_PAGE/ENERGY_INFO_PAGE 保存任务(data/data.c:357)
custom_info_reset全局置自定义信息复位标志(data/data.c:393)
energy_reset全局置能量复位标志,并立即清逆变器能量、电池充放电量与 BMS 能量(data/data.c:405)
factory_reset全局先调用 energy_reset(),再置恢复出厂标志(data/data.c:421)
set_ems_reset全局置 EMS 软复位标志(data/data.c:434)
resume_flash_reset全局置"擦除续传运行数据"标志(data/data.c:446)

4. 核心逻辑解析

4.1 任务调度骨架与掉电续传状态迁移

任务入口(data/data.c:251-317)结构为"三段式 + 无限循环":

  1. 初始化:save_job_init()(创建保存任务队列 SaveJob_Q 与内存分区 SaveJobMsgBuffer)、store_data_init()(续传存储初始化)、error_log_data_init()(故障日志初始化)。
  2. 循环体第一步 OSSemPend(g_t_power_down_sem, 500, &err)。由于 OS_TICKS_PER_SEC = 1000u(UCOSII/uCOS-CONFIG/os_cfg.h:51),500 tick 即 500 ms。
  3. 按 err 分两条路径:
    • err == OS_ERR_NONE(中断里收到掉电信号,hardware/exti/exti.c:95 在 EXTI_10 中断中检测到 GPIOG GPIO_PIN_10 为高后 OSSemPost):进入掉电保护分支。若 get_skip_erase_on_powerdown_flag() == SET(该函数直接返回 PG10 电平,hardware/gpio/gpio.c:1463),先 save_energy_data() 并投递 NET_PARA_PAGE;随后进入 OS_ENTER_CRITICAL() 临界区,循环执行 system_config_data_save() + ems_reset_handle(),只要 PG10 仍为高就持续保存,一旦电平恢复(市电回来)就退出临界区并调用 W5500_Power_ON() / wifi_power_on() 恢复网口与无线模块供电。
    • err != OS_ERR_NONE(500 ms 超时,即正常运行态):进入周期维护分支,顺序执行 custom_info_reset_handle() → energy_reset_handle() → factory_reset_handle() → error_log_data_handle() → resume_data_handle() → erase_flash_handle() →(若复位标志已置则 save_energy_data() + save_job_post(NET_PARA_PAGE))→ system_config_data_save() → eeprom_fault_handle()。
  4. 无论哪个分支结束,最后都再调一次 ems_reset_handle():因此 set_ems_reset() 的效果实际在 500 ms 内生效(net/server.c:2321 是唯一调用者)。

命令字/帧格式:本文件不涉及串口或网络协议帧,不存在起始码、长度、CRC 等字段。

4.2 掉电保存的时序与临界区

system_config_data_save() 每轮最多处理 7 个保存任务(data/system_config/system_config.c:3373),每写完一页后如果 PG10 仍为高就立刻 W5500_Power_OFF() + wifi_power_off()(system_config.c:3367-3371)——这是"边写边断电"的省电策略:外部电源已失去,靠母线电容余量把关键页写完。本文件在掉电分支里用 OS_ENTER_CRITICAL() 包住这个 while 循环,目的是禁止任务切换与中断,防止其它任务在掉电窗口内再发起 Flash 写导致擦写冲突。代价中断被长时间关闭(每页擦写是 4 KB 扇区级操作),这是后面"风险点"提到的主要隐患。

4.3 EEPROM 故障位汇总(eeprom_fault_handle)

算法:把四组标志(get_eeprom_hardware_read_fault_flag()、get_eeprom_hardware_write_fault_flag()、get_eeprom_software_read_fault_flag()、get_eeprom_software_write_fault_flag())按位或成一个 l_u32_eeprom_fault_flag,用 static 变量 s_u32_eeprom_fault_flag 保存上一轮值做边沿检测:

随后调用 clear_eeprom_fault_temp():对 i = 0..31 检查"软件读故障位已置但软件写故障位未置"的页,调用 set_eeprom_fault_flag(get_eeprom_software_read_fault_flag(), i + SYSTEM_STARTE_PAGE, FALSE) 清读故障。注意 set_eeprom_fault_flag 内部有范围保护 addr < 4071 || addr > 4095 直接返回(data/fault_warn_log/fault_warn_log.c:2077),所以 i >= 25 的迭代是空操作。

4.4 各复位请求的落盘与校验动作

4.5 故障位接口与读写者

5. 对外接口

导出的函数(声明于 data/data.h:126-135):

名称使用者
task_system_datauser/main.c:156(任务创建)
ems_read_fault_codedata/fault_warn_log、hmi/led.c、logic/dispatch.c、net/ems.c、midware/inverter/*、midware/interface/modbus/modbus.c
ems_set_fault_bit约 30 处,含 midware/afci/afci_comm.c、midware/bms/bms.c、midware/inverter/inverter.c、midware/inverter/parallel.c、midware/meter/ct.c、system_config.c:2530
meter_resethmi/key.c:1517、net/server.c:1203、midware/meter/meter.c、midware/interface/modbus/modbus.c:6759
custom_info_resetmidware/interface/modbus/modbus.c:7340
energy_resethmi/key.c:1536、net/server.c:1196、midware/interface/modbus/modbus.c:6755
factory_resethmi/key.c:1417、net/server.c:1210、midware/interface/modbus/modbus.c:6763
set_ems_resetnet/server.c:2321
resume_flash_resetdata/resume_data/flash/flash_data_store.c:2816、net/server.c:1259

导出的全局变量:g_t_ems_info(data.h:123)、error_log(data.h:122)。

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

6. 依赖关系

7. 备注与风险点

  1. 掉电分支内长时间关中断:data/data.c:273-284 用 OS_ENTER_CRITICAL() 包住一个 while 循环,循环体内是 Flash 扇区擦写(system_config_data_save()),单次擦写为 ms 级,若反复进入会显著拉长中断关闭时间,可能影响串口/CAN 接收的实时性。
  2. 临界区退出条件依赖外部电平:while 条件 get_skip_erase_on_powerdown_flag() == SET 读的是 PG10 实时电平,一旦该引脚上有抖动或长时间保持高,任务会一直卡在临界区不再喂狗/调度。
  3. clear_eeprom_fault_temp 的循环上界高于有效页范围:i 取 0..31(data/data.c:192),而 i + SYSTEM_STARTE_PAGE 超过 4095 时被 set_eeprom_fault_flag 内部静默忽略,属于无效迭代;同时它对 i 使用 u8,i + 4071 会整型提升为 int,暂无溢出风险,但语义上应循环到 SYSTEM_DATA_BACKUP_PAGE - SYSTEM_STARTE_PAGE。
  4. ems_set_fault_bit 无 index 上界检查:(u32)(1 << index) 在 index >= 32 时为未定义行为(data/data.c:342/346),建议加断言或钳位。
  5. meter_reset 的同步写 Flash 风险:它被 net/server.c(网络任务上下文)直接调用并投递保存任务,虽然真正的写动作在系统数据任务中完成,但调用点本身是跨任务共享数据无保护的读改写(g_t_energy_info 直接赋 0),与 meter 任务存在竞态窗口。
  6. energy_reset_handle 的"先写后擦"顺序:energy_info_init()(写 ENERGY_INFO_PAGE)在 erase_run_data()(擦续传运行区)之前执行,两页位置不同,功能上无冲突,但若后续把能量页挪进运行区就需要重新审视顺序。
  7. 版本遗留:data/data.h:2 的文件头注释仍写着 @file :com485do.h;ErrLog_t 中 u8 p_write 已无任何引用;eeprom_fault_handle() 中保留了 #if 0 的 printf 调试块(data/data.c:238-241)。
  8. 魔法数字:ems_set_fault_bit(2,...)、ems_set_fault_bit(13,...) 直接写字面量位号,未使用 EMS_FAULT_EEPROM_FAULT / EMS_FAULT_FLASH_FAULT 枚举名;OSTimeDlyHMSM(0,0,10,0) / (0,0,40,0) 的延时数值也是硬编码。