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

system_aux.c

源文件路径:midware/auxiliary/system_aux.c 所属模块:midware/auxiliary(系统辅助任务:能量统计 + RTC 刷新 + 设备功能位初始化) 代码行数:2602

1. 文件职责

system_aux.c 实现 EMS 的「系统辅助任务」task_system_aux,是整个固件里唯一一个把瞬时功率积分成电量的地方。它每个周期(400ms)读取电网电表有功功率、逆变器交流有功功率、BMS 总功率和油机功率,按实际经过的毫秒数做 W·ms 累加,满 1 分钟折算成 Wh 并入 g_t_energy_info;再由 data/system_config/system_config.c:3063 在保存作业中调用 enenrgy_count_handle() 把这些累计值刷到 Flash 的 ENERGY_INFO_PAGE。同一任务还承担三件辅助工作:刷新 RTC(rtc_refresh,并据此置位/清位 RTC 故障)、维护电网电表的 1 分钟滑动平均功率、以及按机型(逆变器厂商 + 子型号 + 板型)初始化两张功能位表 g_system_function_info。它处于数据流的「采样 → 统计 → 持久化/上云」中间环节,上游是 meter/inverter/bms 中间件,下游是 Flash、Modbus、云端和 HMI。

2. 关键数据结构与宏

名称类型说明
ENERGY_COUNT_INFO_T结构体(定义于 system_aux.h:18)本文件的核心数据体,128 字节紧凑结构,g_t_energy_info 的类型
g_t_energy_info全局变量(定义于 :35)能量统计持久化实体,本文件的主要写对象,被 data/data.c、data/system_config/system_config.c 读写
SYSTEM_DATETIME全局变量(定义于 :36)struct RTC_DateTime,6 字段当前时间,全工程广泛读取
g_t_sys_datetime全局变量(定义于 :37)_PCF8563_DateTime_Typedef,含星期的原始 RTC 结构
s_time_rtc_tick静态全局 time_t(:38)RTC 换算出的 Unix 秒级时间戳,经 get_rtc_tick() 导出
s_u32_energy_grid_charge静态 u32(:39)电网充电池电量累加器,单位 10Wh(累加时以 /10 进位,余数留存)
s_u32_energy_generator_output静态 u32(:40)油机发电量累加器(分钟级,Wh 直接累加)
s_u32_energy_generator_charge静态 u32(:41)油机给电池充电量累加器。当前无任何代码写入,恒为 0
s_u32_energy_bat_charge / s_u32_energy_bat_dischg静态 u32(:42、:43)电池充电/放电能量累加器,单位 10Wh
s_u32_bat_dischg_to_grid_energy_temp静态 u32(:44)电池强制馈网电量累加器,单位 10Wh
s_s32_average_power_of_gridmeter_a/b/c/t静态 s32(:46~:49)电网电表 1 分钟滑动平均有功功率(分相 + 总),单位 W
DEBUG_SYSTEM_AUX宏(system_aux.h:11,值为 0)调试总开关,控制 printf_debug_info() 与任务运行计数
TIME_BASE_YEAR / NOW_BASE_YEAR宏(system_aux.h:13、:14)1900 / 2000,用于 tm_year 换算(:367)
SYSTEM_FUNCTION_PARA_T结构体(data/system_config/system_config.h:722)g_system_function_info 的类型,含 u_system_function1(u64_all)、u_system_function2、t_dev_fun、t_paradev_fun
DEVICE_FUNCTION_U联合体结构(system_config.h:578 结束)t_dev_fun 类型,按设备分组:ems_fun_u/ems_fun_2_u/ems_fun_3_u/bms_fun_u/inv_fun_u/meter_fun_u/evc_fun_u/grid_regulation_u,每组一个 u32 位域 + 第 4 字节存表编号
PARALLELISMDEVICE_FUNCTION_U结构体(system_config.h:717)t_paradev_fun 类型,含 para_ems_fun_u(并机强制馈网、取电限制)
SYSTEM_BASE_PARA_T结构体(system_config.h:748)g_t_system_base_info 类型,u_system_function1.u64_all 是落 Flash 的功能位镜像
PHASE_A/B/C/T枚举(midware/meter/meter.h:172)相位 0/1/2/3(3 = 电表总值),本文件所有分相接口的参数
GRID_METER / PV_METER枚举(meter.h:165、:166)电表站点号 1 / 2
PARA_COM_TYPE_USART3枚举(midware/inverter/parallel.h:37)并机通信方式为 USART3 时的取值,用于选择并机态功能位表
INVERTER_TYPE 及 KELONG_TYPE/SET_TYPE/GINLONG_TYPE/ALPHAESS_TYPE/GOODWE_TYPE/INOVANCE_TYPE/INVERTER_MAX宏(midware/inverter/inverter.h:164~:172)编译期选定的逆变器厂商,device_function_init() 靠它把机型分支整段编译掉
INVERTER_SUB_E / get_inverter_sub()枚举 + 函数(inverter.h:289、:1942)子型号(SUB_S5、SUB_S5_II、SUB_M10、SUB_S3_6、SUB_B3_6、SUB_S6、SUB_S8、SUB_S10、SUB_B10 等),用于同厂商内的型号细分
BOARD_EMS3_6_1_E~BOARD_EMS3_6_4_E枚举(system/board_init/board_init.h:19~)单板型号,区分 EMS3.6 各硬件版本
EMS_FAULT_U联合体(data/data.h:69)g_t_ems_info.u_ems_fault,本文件用 bit1 = u8_rtc_fault
u8_non_fixed_domain_name_point / u8_ems_support_18031位域(system_config.h:358、:392)最后无条件置 1 的两个功能位

3. 函数清单

函数名作用域说明
grid_chargr_energy_statistics(void)static统计「电网给电池充电」与「电池强制馈网」两条能量流,1 分钟结算一次
bat_total_energy_statistics(void)static用 BMS 总功率统计电池充/放电量,1 分钟结算一次
generator_output_energy_statistics(void)static用油机(离网 GC)功率统计油机发电量
generator_charge_energy_statistics(void)static(被 #if 0 屏蔽,:283~:333)油机给电池充电量统计,当前未编译
rtc_refresh(void)static读 PCF8563 并刷新时间结构、time_t 时间戳和 RTC 故障位
calculation_average_power_of_gridmeter(void)static电网电表 1S 采样、1 分钟滑动平均功率(分相 + 总)
device_function_init(void)static按逆变器厂商/子型号/单板型号初始化 g_system_function_info.t_dev_fun 全部设备功能位
para_device_function_init(void)static初始化并机相关功能位 t_paradev_fun
device_function_init_parallel_change(void)static并机通信方式变化时重置功能位(只保留远程升级/加密/并机等必需位)
ststem_function1_init(void)static初始化旧版 64 位功能位表 u_system_function1.u64_all(函数名拼写为 ststem)
system_function_release_processing_handle(void)static比较固化的功能位与 EEPROM 镜像,不一致时投递 SYSTEM_BASE_PARA_PAGE 保存作业
task_system_aux(void *p_arg)全局辅助任务主循环入口,初始化功能位后每 400ms 跑一遍全部统计与维护动作
enenrgy_count_handle(void)全局把 RAM 分钟级累计刷新进 g_t_energy_info(含 BMS 同步、油机、电表读数)
energy_read_storage_pv_input(void)全局返回 g_t_energy_info.u32_storage_pv_input
energy_read_storage_inv_input(void)全局返回 g_t_energy_info.u32_storage_inv_input
energy_read_storage_inv_output(void)全局返回 g_t_energy_info.u32_storage_inv_output
energy_read_storage_bat_charge(void)全局返回 g_t_energy_info.u32_storage_bat_charge
energy_read_storage_bat_dischg(void)全局返回 g_t_energy_info.u32_storage_bat_dischg
energy_read_bat_chgrge(void)全局返回 g_t_energy_info.u32_bat_charge
energy_read_bat_dischg(void)全局返回 g_t_energy_info.u32_bat_dischg
energy_read_grid_chgrge(void)全局返回 g_t_energy_info.u32_grid_charge
energy_read_bat_dischg_feedin_grid(void)全局返回 g_t_energy_info.u32_bat_dischg_feedin_grid
energy_read_pv_chgrge(void)全局返回 g_t_energy_info.u32_pv_charge
energy_read_grid_consumption(u8 phase)全局按相位返回电网取电电量(A/B/C 相或总量)
energy_read_grid_feedin(u8 phase)全局按相位返回电网馈网电量
energy_read_pv_inverter_output(void)全局返回并网机 PV 发电量
energy_read_gc_output(void)全局返回油机发电量
energy_read_gc_charge(void)全局返回油机给电池充电量(恒 0,见第 7 节)
get_realtime_bat_chg(void)全局返回电池充电量 + 未落盘余量(s_u32_energy_bat_charge/10)
get_realtime_bat_dischg(void)全局返回电池放电量 + 未落盘余量
get_realtime_bat_chg_from_grid(void)全局返回电网充电池电量 + 未落盘余量
get_realtime_bat_dischg_feedin_grid(void)全局返回电池馈网电量 + 未落盘余量
get_average_power_of_gridmeter(u8 phase)全局按相位返回电网电表 1 分钟平均有功功率(带符号)
get_rtc_tick(void)全局返回 s_time_rtc_tick
set_system_function_enable(u8 u8_page, u8 u8_index, u8 u8_value)全局单 bit 改写 g_t_system_base_info.u_system_function1.u64_all 并投递保存作业
parallel_system_function_handle(void)全局定义但前置声明为 static(见第 7 节)检测并机通信方式变化,重建 t_dev_fun 功能位表

被注释掉的旧调试函数(不参与编译):printf_system_info()(:1744)、check_os_stack()(:1897)、check_task_status()(:1952)、printf_debug_info()(:2030)。

4. 核心逻辑解析

4.1 任务主循环与调度时序

task_system_aux(:2056)先做一次性的功能位初始化,然后进入死循环:

ststem_function1_init();        // :2059  旧版 64bit 功能位表
device_function_init();         // :2060  设备分组功能位表
para_device_function_init();    // :2061  并机功能位

for(;;)
{
    parallel_system_function_handle();          // :2065  并机方式变化检测(可能整体重建功能位)
    grid_chargr_energy_statistics();            // :2067  电网充电 / 电池馈网能量
    bat_total_energy_statistics();              // :2069  电池充放电能量
    generator_output_energy_statistics();       // :2071  油机发电能量
    rtc_refresh();                              // :2073  刷新 RTC + RTC 故障位
    internet_time_handle();                     // :2075  网络授时(DHCP/DNS 时间处理,1s 节流)
    monitor_software_run_blink();               // :2077  系统运行灯翻转
    calculation_average_power_of_gridmeter();   // :2079  电表平均功率
    system_function_release_processing_handle();// :2081  功能位落 Flash 检查
    board_test();                               // :2083  单板测试(仅 g_u8_single_board_test_enable 为真时动作)
    passthrough_detection();                    // :2085  RS485 透传检测
    IWDG_Feed();                                // :2091  喂独立看门狗
    OSTimeDlyHMSM(0, 0, 0, 400);                // :2092  400ms 周期
}

要点:

4.2 时间差测量与能量积分的通用模式

三个统计函数(grid_chargr_energy_statistics、bat_total_energy_statistics、generator_output_energy_statistics)共用同一套时间差模板(以 :93~:103 为例):

if (OSTimeGet() >= s_u32_time_tick)
{
    l_u32_time_differe = abs(OSTimeGet() - s_u32_time_tick);
    s_u32_time_tick = OSTimeGet();
}
else
{
    l_u32_time_differe += 0xffffffff - s_u32_time_tick;   // tick 计数器回绕
    l_u32_time_differe += OSTimeGet();
    s_u32_time_tick = OSTimeGet();
}
s_u32_time_sum += l_u32_time_differe;                     // 累计到 1min

能量累加与结算:

if (power > 0)
    s_u32_xxx_energy_temp += power * l_u32_time_differe;   // 单位 W·ms

if (s_u32_time_sum >= 1000*60)                             // 满 1 分钟
{
    s_u32_xxx_energy     += (s_u32_xxx_energy_temp / (s_u32_time_sum * 60));  // → Wh
    s_u32_xxx_energy_temp = (s_u32_xxx_energy_temp % (s_u32_time_sum * 60));  // 余数滚入下一分钟
    s_u32_consumer       += (s_u32_xxx_energy / 10);       // 以 10Wh 为单位进位
    s_u32_xxx_energy      = (s_u32_xxx_energy % 10);       // 余数保留
    s_u32_time_sum = 0;
}

换算推导:power[W] × Δt[ms] 累加得 W·ms;除以 (time_sum × 60) 且 time_sum 就是一分钟内累计的毫秒数(≈60000),即除以 3,600,000,而 1 Wh = 3,600,000 W·ms,所以中间量 s_u32_xxx_energy 的单位是 Wh。再 + (x / 10) 并把 %10 留作余数,说明文件级累加器 s_u32_energy_* 以 10 Wh 为单位步进。enenrgy_count_handle() 里又做了一次 /10 并把 %10 留回累加器,因此最终写进 g_t_energy_info.u32_bat_charge / u32_grid_charge 等字段的单位是 100 Wh(0.1 kWh),每 +1 代表 0.1 kWh。s_u32_time_sum 被清零但 _temp 的余数保留,所以下一分钟的分母从 0 重新累计,存在轻微的边界舍入漂移。

4.3 电网充电池 / 电池馈网能量(grid_chargr_energy_statistics,:75)

功率判据(:107~:146):

l_s32_meter_actpower   = meter_read_actpower(GRID_METER, PHASE_T);       // 电表总有功
l_s32_inv_ac_actpower  = inverter_read_inv_actpower(PHASE_ALL);          // 逆变器交流有功
l_s32_bat_dischg_power = inverter_read_inv_actpower(PHASE_ALL) - g_t_logic_info.t_logic_control_parameter.s32_ppv_inv;

4.4 电池充放电能量(bat_total_energy_statistics,:181)

if (bms_get_ready_enable() == FALSE)          // 未使能 battery ready 时才用 BMS 总功率
    l_s32_bat_total_power_temp = bms_read_total_power();

power > 0 记为放电(s_u32_dischg_energy_temp),<= 0 记为充电(s_u32_charge_energy_temp,用 abs() 取正),1 分钟结算进 s_u32_energy_bat_dischg / s_u32_energy_bat_charge。注意 l_s32_bat_total_power_temp 的初始值为 0,若 bms_get_ready_enable() 为真则恒为 0,会持续走「充电」分支但功率为 0,不产生能量——即关闭 BMS 功率源时本函数不再贡献电量(改由 enenrgy_count_handle 的 BMS 同步路径补齐)。

4.5 油机发电能量(generator_output_energy_statistics,:244)

只用 get_offgrid_mode_gc_power()(logic/generator.h:32)作为输入,> 0 时累加,1 分钟结算进 s_u32_energy_generator_output(单位 Wh,不做 /10 进位)。被 #if 0 屏蔽的 generator_charge_energy_statistics()(:291)本打算用 get_offgrid_mode_gc_charge_power() 统计油机充电量。

4.6 RTC 刷新(rtc_refresh,:341)

l_s8_rtc_state = PCF8563_Get_DateTime_Other(&l_t_time);
if (l_s8_rtc_state == 0) ems_set_fault_bit(1, FALSE);   // 读到时间 → 清 RTC 故障
else                     ems_set_fault_bit(1, TRUE);    // 读失败   → 置 RTC 故障

4.7 电网电表 1 分钟平均功率(calculation_average_power_of_gridmeter,:389)

if (abs(OSTimeGet() - s_u32_count_tick) >= 1000)   // 1s 采样节流
{
    s_u32_count_tick = OSTimeGet();
    if (s_u8_cnt >= 60)      // 已满 60 个样本:先减去旧平均值(滑动窗口)
    {
        s_s32_power_temp_a -= s_s32_average_power_of_gridmeter_a;   // ... b/c/t 同理
        s_s32_power_temp_a += meter_read_actpower(GRID_METER, PHASE_A);
    }
    else                     // 未满 60:累加并计数
    {
        s_s32_power_temp_a += meter_read_actpower(GRID_METER, PHASE_A);
        s_u8_cnt++;
    }
}

if (s_u8_cnt >= 60)   // 窗口满 → 平均值
    s_s32_average_power_of_gridmeter_a = s_s32_power_temp_a / 60;
else                  // 窗口未满 → 直接透传瞬时值
    s_s32_average_power_of_gridmeter_a = meter_read_actpower(GRID_METER, PHASE_A);

这是滑动窗口求和 + 除 60 的 1 分钟平均:窗口满后每来一个新样本就减掉「上一轮的平均值」再加新样本,从而保持 60 个样本的和。s_u8_cnt 只增不减(到 60 就停在 >= 60 分支),所以启动后 60 秒进入稳态平均,之前是瞬时值透传。

4.8 设备功能位初始化(device_function_init,:447~:1551)

这是文件里最长的函数(约 1100 行),全部是对 g_system_function_info.t_dev_fun.*_fun_u.bit.* 的逐位赋值,用 #if (INVERTER_TYPE == ...) 把不同厂商的表整段编译掉。实际生效的分支如下:

编译分支位置细分条件表编号(写入 *_table 字段)
INVERTER_MAX 或 ALPHAESS_TYPE:449~:803① get_inverter_sub() ∈ {SUB_S5,SUB_B5,SUB_S3_6,SUB_B3_6,SUB_S6,SUB_S8,SUB_S10,SUB_B10}(:450);② get_board_type()==BOARD_EMS3_6_3_E(:571);③ 其他ems=1, ems2=2, bms=3, inv=4, meter=5, evc=6, grid_regulation=7, ems3=8
KELONG_TYPE:805~:906无子型号细分同上
SET_TYPE:908~:1228① SUB_M10(:909);② SUB_S5_II(:1015,带中文逐位注释);③ SUB_S5(:1123)同上
GINLONG_TYPE:1230~:1331无同上
GOODWE_TYPE:1333~:1434无同上
INOVANCE_TYPE:1436~:1543无;额外置 u8_evc_second_three_parallel(:1535)同上

厂商/型号差异集中在这几类位:

ALPHAESS_TYPE 分支内第一段(:450)与第三段(:685 的 else)高度重复,差异仅在 u8_dry_1_fun/u8_dry_2_fun、u8_fcas_*、u8_14a_enwg、u8_deu_eza、u8_gfci_en 等少量位。

分支结束后有两条无条件语句与一条板型补丁:

g_system_function_info.t_dev_fun.inv_fun_u.bit.u8_support_afci = g_t_inv_info.t_afci_info.u8_support_afci_flag == TRUE ? 1 : 0;  // :802(在 ALPHAESS 块内)
g_system_function_info.t_dev_fun.ems_fun_2_u.bit.u8_non_fixed_domain_name_point = 1;   // :1544
g_system_function_info.t_dev_fun.ems_fun_3_u.bit.u8_ems_support_18031 = 1;             // :1545
if ((get_board_type()==BOARD_EMS3_6_1_E) || ...3_6_2_E || ...3_6_3_E || ...3_6_4_E)   // :1546
{
    g_system_function_info.t_dev_fun.ems_fun_2_u.bit.u8_sg_ready_2 = 1;   // :1548
    g_system_function_info.t_dev_fun.ems_fun_u.bit.u8_dry_2_fun   = 1;    // :1549
}

注意 :1544、:1545、:1546~:1550 三段位于所有 #if 之外,因此任何机型都会执行。

4.9 并机功能位(para_device_function_init,:1560)

4.10 旧版功能位表(ststem_function1_init,:1632)

对 g_system_function_info.u_system_function1(64 位位域,u64_all)逐个置位,是较老一代的「系统支持功能」位表(远程升级、自发自用、小时/15 分钟定时充放电、断点续传、并机、grid-forming、LCD/LED、各种物理接口等)。其中 u8_three_phase_network = 0(:1673)、u8_e_linker_wifi_pwd = 0(:1703)、u8_evc_gun_lock = 0(:1704)是少数默认关闭的位。

4.11 功能位落 Flash(system_function_release_processing_handle,:1715)

if (g_t_system_base_info.u_system_function1.u64_all == g_system_function_info.u_system_function1.u64_all)
    return;                                        // 一致 → 什么都不做

if ((g_t_system_base_info.u_system_function1.u64_all == 0) ||          // EEPROM 全 0(未初始化)
    (g_t_system_base_info.u_system_function1.u64_all != g_system_function_info.u_system_function1.u64_all))
{
    g_t_system_base_info.u_system_function1.u64_all = g_system_function_info.u_system_function1.u64_all;
    l_u8_change_flag = TRUE;
}

if (l_u8_change_flag == TRUE)
    save_job_post(SYSTEM_BASE_PARA_PAGE);          // :1732

作用是把固件里写死的 capability 位单向覆盖到 EEPROM 镜像:每次降版本或首次上电时,只要二者不同就以固件为准并触发一次保存。它不会把并机态下由 set_system_function_enable() 远程改写的位反向覆盖回去(因为 set_system_function_enable 改的就是 g_t_system_base_info 这一侧,二者在改写后即相等)。

4.12 能量汇总落盘(enenrgy_count_handle,:2103)

调用方:data/system_config/system_config.c:3063(case ENERGY_INFO_PAGE,由保存作业触发)。步骤:

  1. g_t_energy_info.u32_grid_charge += s_u32_energy_grid_charge/10,余数 %10 保留(:2105)。
  2. g_t_energy_info.u32_bat_dischg_feedin_grid += s_u32_bat_dischg_to_grid_energy_temp/10(:2108)。
  3. 电池电量有两条互斥来源(:2111~:2128):
    • g_t_bat_info.t_run_info.u8_energy_enable == FALSE:用本文件自己统计的 s_u32_energy_bat_charge/dischg 累加(/10 进位)。
    • 否则:把本地累加器清零;若 u8_energy_sync_flag == TRUE,直接整体覆盖为 BMS 的 u32_energy_charge / u32_energy_discharge(即由 BMS 作为电量权威源)。
  4. u32_pv_charge = u32_bat_charge - u32_grid_charge(不足则 0),即把电池总充电量减去电网充电量推算 PV 充电量(:2130~:2137)。
  5. 逆变器侧读数覆盖(:2139~:2162):仅当 inverter_read_comm_flag() == TRUE 且对应读数非 0 时,才用逆变器寄存器值覆盖 u32_storage_pv_input / u32_storage_inv_input / u32_storage_inv_output / u32_storage_bat_charge / u32_storage_bat_dischg 共 5 个字段。这里的 != 0 判据意味着「读数为 0 时保留旧值」,可防止通讯瞬时失败把电量清零。
  6. u32_generator_energy_output += s_u32_energy_generator_output 后立即清零累加器(:2164、:2165);u32_generator_energy_charge += s_u32_energy_generator_charge 后清零(:2214、:2215)——后者的源累加器无写入者,恒 0。
  7. 电表读数覆盖:GRID_METER 的 PHASE_T 取电/馈网(:2167~:2175),A/B/C 三相取电(:2178~:2191),A/B/C 三相馈网(:2194~:2207),以及 PV_METER 的 PHASE_T → u32_pv_inverter_output(:2209~:2212)。所有覆盖都以 meter_read_connect_status(...) == TRUE 且读数 != 0 为前提。

注意 u32_grid_charge 的两重缩放:在本函数里 += s_u32_energy_grid_charge/10(s_u32_energy_grid_charge 单位为 10 Wh),所以 u32_grid_charge 每 +1 代表 100 Wh。

4.13 只读访问器与「实时值」

15 个 energy_read_* 都是 g_t_energy_info 的单字段直读;energy_read_grid_consumption() / energy_read_grid_feedin() / get_average_power_of_gridmeter() 用 switch(phase) 映射 PHASE_A/B/C/T,default 返回 0。

4 个 get_realtime_* 把落盘值与未结算的分钟余量相加:

get_realtime_bat_chg()        → g_t_energy_info.u32_bat_charge  + (s_u32_energy_bat_charge / 10)
get_realtime_bat_dischg()     → g_t_energy_info.u32_bat_dischg  + (s_u32_energy_bat_dischg / 10)
get_realtime_bat_chg_from_grid()      → g_t_energy_info.u32_grid_charge + (s_u32_energy_grid_charge/10)
get_realtime_bat_dischg_feedin_grid() → g_t_energy_info.u32_bat_dischg_feedin_grid + (s_u32_bat_dischg_to_grid_energy_temp/10)

调用方为 net/ems.c(云端实时能量上报)。

4.14 远程改写功能位(set_system_function_enable,:2551)

if (u8_index > 63 || u8_value > 1) return;               // 参数越界保护
if (u8_page == 1)                                        // 只支持 page==1
{
    if (((g_t_system_base_info.u_system_function1.u64_all >> u8_index) & 0x01) != u8_value)
    {
        l_u64_temp = 1;
        g_t_system_base_info.u_system_function1.u64_all &= (~(l_u64_temp << u8_index));  // 清位
        l_u64_temp = u8_value;
        g_t_system_base_info.u_system_function1.u64_all |=  (l_u64_temp << u8_index);    // 置位
        save_job_post(SYSTEM_BASE_PARA_PAGE);                                             // 异步落盘
    }
}

读-改-写非原子,但调用方只有 net/server.c 的服务器报文解析路径,属单线程串行调用。u8_value > 1 直接 return,因此它不是任意写而是「置位/清位」语义。

4.15 并机方式变化的整体重建(parallel_system_function_handle,:2580)

static u8 s_u8_parallel_mode = 0;

if (s_u8_parallel_mode != parallel_read_config_machine_precept())   // 并机配置机种变了
{
    s_u8_parallel_mode = parallel_read_config_machine_precept();
    memset(&g_system_function_info.t_dev_fun, 0, sizeof(g_system_function_info.t_dev_fun));  // 整表清零
    if (s_u8_parallel_mode == PARA_COM_TYPE_USART3)   // 走 USART3 并机报文
        device_function_init_parallel_change();       // 只保留远程升级/加密/并机等必需位
    else
    {
        ststem_function1_init();                      // 非并机 → 完整重建
        device_function_init();
        para_device_function_init();
    }
    Set_Setup_Config_Extend1_Status(FALSE);           // 通知云端配置已变更(net/ems.h:511)
}

这是本任务里唯一会清空并重建 t_dev_fun 的路径:并机模式由从机变主机(或反过来)时,功能位表会瞬间被 memset 归零再按新身份填回,期间若有其他任务(如 net/ems.c 组包)正在读 t_dev_fun,会读到全 0 的中间态。

4.16 二进制状态机说明

本文件没有显式状态机(无 switch(state) 结构、无状态枚举)。其行为等价于一组各自带静态时间戳/累计器的周期采样-积分器,加上 device_function_init / parallel_system_function_handle 两处以「配置是否变化」为迁移条件的一次性重建逻辑。唯一的隐式迁移条件是 parallel_system_function_handle 里的 s_u8_parallel_mode 静态变量。

5. 对外接口

本文件导出的函数(声明在 system_aux.h,实现在本文件)

本文件导出的全局变量

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

来源模块具体符号
midware/meter/meter.hmeter_read_actpower(site, phase)、meter_read_einput(site, phase)、meter_read_eoutput(site, phase)、meter_read_connect_status(site)、GRID_METER、PV_METER、PHASE_A/B/C/T
midware/inverter/inverter.hinverter_read_inv_actpower(PHASE_ALL)、inverter_read_comm_flag()、inverter_read_energy_pv_input/inv_input/inv_output/bat_charge/bat_dischg()、get_inverter_sub()、INVERTER_SUB_E、INVERTER_TYPE 系列宏
midware/bms/bms.hbms_get_ready_enable()、bms_read_total_power()、g_t_bat_info.t_run_info.{u8_energy_enable, u8_energy_sync_flag, u32_energy_charge, u32_energy_discharge}
logic/logic.hg_t_logic_info.t_logic_control_parameter.s32_ppv_inv、.s32_pbat_inv
logic/generator.hget_offgrid_mode_gc_power()
midware/inverter/parallel.hparallel_read_config_machine_precept()、PARA_COM_TYPE_USART3
midware/inverter/inverter.hg_t_inv_info.t_config_data.u8_parallel_mode、g_t_inv_info.t_afci_info.u8_support_afci_flag
data/data.hems_set_fault_bit(index, value)、g_t_ems_info
data/system_config/system_config.hg_system_function_info、g_t_system_base_info、save_job_post(msg)、SYSTEM_BASE_PARA_PAGE
hardware/pcf8563/pcf8563.hPCF8563_Get_DateTime_Other()
hardware/gpio/gpio.hmonitor_software_run_blink()(系统运行灯翻转)
hardware/iwdg/iwdg.hIWDG_Feed()
midware/ethernt/ethernet.hinternet_time_handle()
midware/debug/board_test.hboard_test()
midware/debug/ate.hpassthrough_detection()
net/ems.hSet_Setup_Config_Extend1_Status(bSet)
system/board_init/board_init.hget_board_type()、BOARD_EMS3_6_1_E~BOARD_EMS3_6_4_E
UCOSIIOSTimeGet()、OSTimeDlyHMSM()
libcmktime()、abs()、memset()

6. 依赖关系

依赖

被依赖

7. 备注与风险点

确定性问题

  1. parallel_system_function_handle 的存储类不一致:文件顶部前置声明为 static void parallel_system_function_handle(void);(:62),但定义处写作 void parallel_system_function_handle(void)(:2580)没有 static。按 C 标准(C11 6.2.2p5),先有内部链接声明时后续定义仍是内部链接,因此它实际不会导出符号;但这是明显的写法不一致,Keil/ARMCC 在部分告警级别下会提示。它也未被 system_aux.h 声明,外部无从调用,说明这段代码是「想写成公共接口但实际自用」,属版本遗留痕迹。
  2. s_u32_energy_generator_charge 是死代码::41 声明、:2214/:2215 消费并清零,但唯一的写入者 generator_charge_energy_statistics() 整段被 #if 0 屏蔽(:283~:333)。于是 g_t_energy_info.u32_generator_energy_charge 与 energy_read_gc_charge() 恒为 0,但接口仍在 system_aux.h:79 对外暴露,云端/Modbus 会读到恒 0 的「油机充电电量」。
  3. 油机发电量的量纲与其它电量不一致(10× 偏差):generator_output_energy_statistics() 里 s_u32_energy_generator_output 与其它累加器一样以 10 Wh 为单位步进(:278、:279),但 enenrgy_count_handle() 在 :2164 是 u32_generator_energy_output += s_u32_energy_generator_output;——没有 /10,而 u32_grid_charge / u32_bat_charge 等都做了 /10。结果是 g_t_energy_info.u32_generator_energy_output 的单位是 10 Wh,其余电量字段是 100 Wh,两者相差 10 倍。与之相对,四个 get_realtime_*() 的余量拼接(u32_bat_charge + s_u32_energy_bat_charge/10)两侧单位是自洽的(都是 100 Wh)。建议只针对油机这一项与云端协议口径核对。
  4. l_u32_time_differe 的回绕分支写法可疑(:100~:103):l_u32_time_differe += 0xffffffff - s_u32_time_tick; 后紧跟 l_u32_time_differe += OSTimeGet();。因为进入该分支前 l_u32_time_differe 恒为 0(每次调用都是新初始化的局部变量),结果等价于 0xffffffff - tick + OSTimeGet(),比标准的 OSTimeGet() + (0xFFFFFFFF - tick) + 1 少 1ms;而同一函数的上分支用的是无 +1 的减法。1ms 量级的误差对能量积分影响可忽略,但属于公式不自洽。
  5. grid_chargr_energy_statistics 取电分支的边界遗漏(:112~:128):外层只判 meter_actpower > 0,内层只处理 inv_ac_actpower < 0。当 meter_actpower > 0 且 inv_ac_actpower >= 0 时 l_u32_grid_charge_power 保持 0(正确),但代码没有显式 else 说明,读者容易误认为漏判。
  6. 整表清零的并发窗口:parallel_system_function_handle() 在并机方式变化时 memset(&g_system_function_info.t_dev_fun, 0, ...)(:2587),随后原地重建。t_dev_fun 被 net/ems.c、modbus.c 等其它任务直接读取,没有任何临界区或原子切换保护,存在读到全 0 中间态的竞态。
  7. system_function_release_processing_handle 的覆盖语义有破坏性:只要 EEPROM 镜像与固件 capability 位不同,就用固件值整体覆盖(:1723~:1727)。这意味着 OTA 降级会把新版本通过 set_system_function_enable() 远程开启的位一次性清掉;且由于比较单位是 64 位整体(u64_all),任何一位差异都会触发全量覆盖。
  8. rtc_refresh 与 PCF8563_Get_DateTime_Other 的高频轮询:每 400ms 走一次 I²C 读 RTC。同一 I²C 总线上若还有其他慢速从机,此处没有超时/重入保护(依赖 PCF8563_Get_DateTime_Other 内部实现),是潜在的总线争用点。
  9. device_function_init 内部三段的复制粘贴::450~:568 与 :687~:799 两段几乎逐行相同,差异只在少数位。这类大段重复极易在后续改型时只改一处,是本文件最典型的可维护性风险。
  10. get_realtime_bat_chg 等在 BMS 同步模式下语义偏移:当 t_run_info.u8_energy_enable == TRUE 时 enenrgy_count_handle() 会把 s_u32_energy_bat_charge/dischg 清 0(:2121、:2122),故这四个 get_realtime_*() 在该模式下退化为与落盘值相同,不再包含「分钟余量」——行为随 BMS 配置改变,调用方需知晓。

可疑但影响较低的写法

  1. :2144 等 5 处逆变器读数覆盖使用「非 0 才覆盖」,好处是抗通讯失败,坏处是真的读到 0(例如逆变器刚复位、当日电量为 0)时会保留上一次的旧值,导致电量统计偏高而非偏低。
  2. 位域里的「表编号」字段容易被误读:device_function_init 中多处把值 2/3/4/5/6/7/8 赋给看起来像「位」的成员(如 u8_ems_table_2 = 2、u8_bms_table = 3、u8_inv_table = 4、u8_meter_table = 5、u8_evc_table = 6、u8_grid_regulation_table = 7、u8_ems_table_3 = 8)。这些成员实际是各功能组第 4 字节的 8 位表编号字段(System_config.h:332、:372、:412、:452、:492、:532、:572、:612 均为 u8 ..._table: 8),赋值 2~8 是正确的表号而非位值,阅读时勿误判为位赋值错误。
  3. ststem_function1_init()、enenrgy_count_handle()、get_inverter_sub 等拼写(ststem、enenrgy)已成为既定接口名,改名波及面大。
  4. 文件里保留了 4 个被注释掉的调试函数(共约 300 行死代码,:1737~:2047),其中 printf_debug_info() 在 DEBUG_SYSTEM_AUX==1 时被 task_system_aux 引用(:2088)——但该函数体已被注释,若把 DEBUG_SYSTEM_AUX 改为 1 会直接编译失败(未定义引用)。
  5. DEBUG_SYSTEM_AUX 打开时还会执行 g_t_task_run_status.u32_task_system_aux_cnt++(:2089),这是任务存活检测的埋点,属调试遗留。

未发现的问题:本文件无指针运算、无数组下标访问、无动态内存申请;set_system_function_enable 对 u8_index/u8_value 有显式范围保护(:2555)。