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_flag | static 变量 | 自定义信息复位请求标志(data/data.c:34) |
s_u8_energy_reset_flag | static 变量 | 能量数据清零请求标志(data/data.c:35) |
s_u8_factory_reset_flag | static 变量 | 恢复出厂设置请求标志(data/data.c:36) |
s_u8_ems_reset_flag | static 变量 | EMS 软复位请求标志(data/data.c:37) |
s_u8_ems_erase_run_data_flag | static 变量 | 擦除运行数据(续传数据)请求标志(data/data.c:38) |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
custom_info_reset_handle | static | 若 s_u8_custom_info_reset_flag 置位,则重建自定义配置并同步备份页(data/data.c:55) |
energy_reset_handle | static | 若 s_u8_energy_reset_flag 置位,延时 10s 后清零能量页并擦除续传运行数据(data/data.c:74) |
factory_reset_handle | static | 若 s_u8_factory_reset_flag 置位,逐页重初始化全部配置、清故障日志、延时 40s 后软复位(data/data.c:94) |
ems_reset_handle | static | 若 s_u8_ems_reset_flag 置位则立即 Sys_Soft_Reset()(data/data.c:154) |
erase_flash_handle | static | 若 s_u8_ems_erase_run_data_flag 置位,延时 10s 后调用 erase_run_data()(data/data.c:169) |
clear_eeprom_fault_temp | static | 对 32 个页逐个比较"软件读故障"与"软件写故障",读故障已清而写故障未置时把该页读故障标志清掉(data/data.c:187) |
eeprom_fault_handle | static | 汇总硬件读/写、软件读/写四组 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)结构为"三段式 + 无限循环":
- 初始化:
save_job_init()(创建保存任务队列SaveJob_Q与内存分区SaveJobMsgBuffer)、store_data_init()(续传存储初始化)、error_log_data_init()(故障日志初始化)。 - 循环体第一步
OSSemPend(g_t_power_down_sem, 500, &err)。由于OS_TICKS_PER_SEC = 1000u(UCOSII/uCOS-CONFIG/os_cfg.h:51),500 tick 即 500 ms。 - 按
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()。
- 无论哪个分支结束,最后都再调一次
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 保存上一轮值做边沿检测:
- 位图变化且结果为 0 →
ems_set_fault_bit(2, FALSE)(清EMS_FAULT_EEPROM_FAULT)。 - 位图变化且非 0 →
ems_set_fault_bit(2, TRUE)并set_info_status(EEPROM_FAULT)触发一次信息上报(net/ems.h:513)。 - 位图未变化时不做任何上报,避免 500 ms 周期刷新造成告警风暴。
随后调用 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 各复位请求的落盘与校验动作
custom_info_reset()→ 下一轮 500 ms 超时分支执行system_custom_info_init()+system_custom_backup_info_init()(data/data.c:55-64),两者之间插IWDG_Feed()喂狗。energy_reset()是双阶段的:调用点立即清inverter_energy_clear()、g_t_energy_info.u32_bat_charge/u32_bat_dischg、bms_clear_energy()(data/data.c:405-412),同时置标志,真正的energy_info_init()(重写ENERGY_INFO_PAGE)与erase_run_data()在任务里等OSTimeDlyHMSM(0,0,10,0)10 秒后才做(data/data.c:74-85),给其它任务留出清数据的时间。factory_reset()先energy_reset()再置标志,任务里按固定顺序重置约 20 个配置页:error_log_data_clear()→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()→meter_clear_run_data_flag(PV_METER)→evc_config_info_init()→evc_setting_info_init()→evc_ii_single_config_info1..4_init()→system_backup_info_init()→ 清Set_Setup_Config_Status(FALSE)/Set_Setup_Config_Extend1_Status(FALSE)→ 延时 40 s →Sys_Soft_Reset()。每一步之间都IWDG_Feed(),因为整段操作远超看门狗超时。meter_reset()不走标志,是同步的重置:清电表与 CT 的 clear flag、清g_t_energy_info的电网/光伏总量与 A/B/C 分相量、清g_t_meter的分相累计,最后投递两个保存任务(data/data.c:357-384)。因此它必须在允许写 Flash 的上下文调用——实测调用点是hmi/key.c:1517、net/server.c:1203、midware/meter/meter.c:4764/5005/5053、midware/interface/modbus/modbus.c:6759。
4.5 故障位接口与读写者
- 写:
ems_set_fault_bit(u8 index, u8 value),index为 0..31,实现为u32_all |= (u32)(1 << index)或按位与非(data/data.c:338-348)。校验要点:index是u8,但移位发生在int(32 位)上,index >= 32会触发未定义行为,调用点实测均在 0..28。 - 读:
ems_read_fault_code()返回整个 32 位字。主要读者有data/fault_warn_log/fault_warn_log.c:516(故障日志边沿检测)、hmi/led.c:213/245/628(告警灯)、logic/dispatch.c:825(调度侧是否降级)、net/ems.c:664(上报云端)、midware/interface/modbus/modbus.c:3061/3737(Modbus 故障寄存器高/低 16 位)。 system_info_restore()在 Flash 备份页损坏时置 bit13(data/system_config/system_config.c:2530),对应EMS_FAULT_FLASH_FAULT。
5. 对外接口
导出的函数(声明于 data/data.h:126-135):
| 名称 | 使用者 |
|---|---|
task_system_data | user/main.c:156(任务创建) |
ems_read_fault_code | data/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_reset | hmi/key.c:1517、net/server.c:1203、midware/meter/meter.c、midware/interface/modbus/modbus.c:6759 |
custom_info_reset | midware/interface/modbus/modbus.c:7340 |
energy_reset | hmi/key.c:1536、net/server.c:1196、midware/interface/modbus/modbus.c:6755 |
factory_reset | hmi/key.c:1417、net/server.c:1210、midware/interface/modbus/modbus.c:6763 |
set_ems_reset | net/server.c:2321 |
resume_flash_reset | data/resume_data/flash/flash_data_store.c:2816、net/server.c:1259 |
导出的全局变量:g_t_ems_info(data.h:123)、error_log(data.h:122)。
本文件调用的外部函数/变量:
user/main.c:g_t_power_down_sem(信号量句柄)。data/system_config:system_custom_info_init、system_custom_backup_info_init、energy_info_init、system_config_data_save、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_init、save_job_init、save_job_post、save_energy_data。data/fault_warn_log:error_log_data_init、error_log_data_handle、error_log_data_clear。data/resume_data/flash/flash_data_store:store_data_init、resume_data_handle、erase_run_data。midware/meter:meter_set_clear_flag、meter_clear_run_data_flag;midware/meter/ct.c:ct_energy_clear。midware/inverter:inverter_energy_clear;midware/bms:bms_clear_energy。midware/auxiliary/system_aux:g_t_energy_info。net/ems:Set_Setup_Config_Status、Set_Setup_Config_Extend1_Status、set_info_status。hardware/gpio:W5500_Power_ON、W5500_Power_OFF、wifi_power_on、wifi_power_off、get_skip_erase_on_powerdown_flag;hardware/iwdg:IWDG_Feed。system/sys/chip.h:Sys_Soft_Reset;hardware/exti:OSSemPost(g_t_power_down_sem)的中断侧生产者。
6. 依赖关系
- 依赖(include,
data/data.c:13-27):data.h、includes.h、main.h、fault_warn_log.h、system_config.h、flash_data_store.h、iwdg.h、meter.h、ct.h、inverter.h、system_aux.h、ems.h、server.h、ethernet.h、gpio.h。 - 被依赖:
user/main.c(创建任务)、data/system_config/system_config.c(includedata.h,回调ems_set_fault_bit)、data/fault_warn_log、hmi/led.c、hmi/key.c、logic/*、net/ems.c、net/server.c、midware/*。
7. 备注与风险点
- 掉电分支内长时间关中断:
data/data.c:273-284用OS_ENTER_CRITICAL()包住一个 while 循环,循环体内是 Flash 扇区擦写(system_config_data_save()),单次擦写为 ms 级,若反复进入会显著拉长中断关闭时间,可能影响串口/CAN 接收的实时性。 - 临界区退出条件依赖外部电平:while 条件
get_skip_erase_on_powerdown_flag() == SET读的是 PG10 实时电平,一旦该引脚上有抖动或长时间保持高,任务会一直卡在临界区不再喂狗/调度。 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。ems_set_fault_bit无 index 上界检查:(u32)(1 << index)在index >= 32时为未定义行为(data/data.c:342/346),建议加断言或钳位。meter_reset的同步写 Flash 风险:它被net/server.c(网络任务上下文)直接调用并投递保存任务,虽然真正的写动作在系统数据任务中完成,但调用点本身是跨任务共享数据无保护的读改写(g_t_energy_info直接赋 0),与 meter 任务存在竞态窗口。energy_reset_handle的"先写后擦"顺序:energy_info_init()(写ENERGY_INFO_PAGE)在erase_run_data()(擦续传运行区)之前执行,两页位置不同,功能上无冲突,但若后续把能量页挪进运行区就需要重新审视顺序。- 版本遗留:
data/data.h:2的文件头注释仍写着@file :com485do.h;ErrLog_t中u8 p_write已无任何引用;eeprom_fault_handle()中保留了#if 0的 printf 调试块(data/data.c:238-241)。 - 魔法数字:
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)的延时数值也是硬编码。