system_aux.c
grid_chargr_energy_statistics,:75)4.4 电池充放电能量(bat_total_energy_statistics,:181)4.5 油机发电能量(generator_output_energy_statistics,:244)4.6 RTC 刷新(rtc_refresh,:341)4.7 电网电表 1 分钟平均功率(calculation_average_power_of_gridmeter,:389)4.8 设备功能位初始化(device_function_init,:447~:1551)4.9 并机功能位(para_device_function_init,:1560)4.10 旧版功能位表(ststem_function1_init,:1632)4.11 功能位落 Flash(system_function_release_processing_handle,:1715)4.12 能量汇总落盘(enenrgy_count_handle,:2103)4.13 只读访问器与「实时值」4.14 远程改写功能位(set_system_function_enable,:2551)4.15 并机方式变化的整体重建(parallel_system_function_handle,:2580)4.16 二进制状态机说明5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
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 周期
}
要点:
- 无阻塞式 IPC。整个文件不使用任何
OSSemPend/OSMboxPend,唯一的跨任务通信是save_job_post()(:1732、:2568),它内部OSMemGet+OSQPost(SaveJob_Q, ...)(data/system_config/system_config.c:2688),把「页号」投给task_system_data任务异步落 Flash。因此本文件对 EEPROM/Flash 的写入是间接、异步、去重的。 - 任务周期 400ms,但内部各功能各有自己的节流:
calculation_average_power_of_gridmeter()用 1s 节流;internet_time_handle()用 1s 节流(midware/ethernt/ethernet.c:1080);grid_chargr_energy_statistics()等用累计时间 ≥ 60000ms 触发 1 分钟结算;system_function_release_processing_handle()每轮都做比较但只有变化时才投递保存作业。 - 看门狗:
IWDG_Feed()位于循环末尾,也就是说前面任一统计函数卡死都会导致喂狗失败复位——这是设计意图,也意味着这些函数必须保持非阻塞。 - 任务优先级 8,栈 512(
UCOSII/uCOS-CONFIG/app_cfg.h:59、:86)。OS_TICKS_PER_SEC=1000(os_cfg.h:51),故文中所有OSTimeGet()差值均按毫秒解读。
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;
- 取电分支(
meter_actpower > 0,电网向系统送电):仅当inv_ac_actpower < 0(逆变器在从电网吸电)时才认为在给电池充电,取两者绝对值较小者为充电功率(:118~:126),避免把逆变器输出被电表读数重复计入。 - 馈网分支(
meter_actpower <= 0):仅当g_t_logic_info.s32_pbat_inv > 0(逆变器采到电池在放电)时,令l_s32_bat_dischg_feedin_grid_power = -l_s32_meter_actpower(取最小值abs(meter),:139)或l_s32_bat_dischg_power(:143),即取「倒送电网功率」与「电池放电功率」的较小者作为电池馈网功率。 - 两路功率分别累加到
s_u32_energy_grid_charge_temp(:151)和s_u32_bat_grid_dischg_energy_temp(:156),按 4.2 的模式 1 分钟结算,最终落到s_u32_energy_grid_charge和s_u32_bat_dischg_to_grid_energy_temp。
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 故障
ems_set_fault_bit(1, ...)对应EMS_FAULT_U的 bit1,即u8_rtc_fault(data/data.h:78),最终出现在g_t_ems_info.u_ems_fault。- 成功后把
RTC_Years/Months/Days/Hours/Minutes/Seconds逐个拷进SYSTEM_DATETIME,同时整体拷贝到g_t_sys_datetime。 time_t换算(:367~:373):tm_year = bYears + NOW_BASE_YEAR - TIME_BASE_YEAR(即 RTC 的 0~99 年 + 100,得到自 1900 起的偏移);tm_mon = bMonths - 1;其余原样;最后mktime(&tmStart)存入s_time_rtc_tick。每次循环都会重新PCF8563_Get_DateTime_Other()走一次 I²C,即 RTC 的刷新率与任务周期一致(400ms)。DEBUG_SYSTEM_AUX == 1时才打印时间(:375~:379)。
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) | 同上 |
厂商/型号差异集中在这几类位:
- 是否支持
u8_fcas_*/u8_ffr_function/u8_sffr_function(澳英电网辅助服务) u8_parallel_inv(并机)、u8_grid_forming、u8_generator_en/u8_gc_ats(油机 + 双电源)u8_au_dispatch(南澳调度)、u8_de_rrcr(德国 RRCR)、u8_14a_enwg(德国 §14a)u8_data_encryption/u8_comm_encry_process(加密能力;GOODWE_TYPE两个都为 0,:1338、:1345)u8_three_phase_unbalance(三相不均衡)、u8_phase_sequence_adaptive(相序自适应)、u8_mid_meter_function(MID 电表)
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)
ALPHAESS/INVERTER_MAX下,只有SUB_S5/SUB_B5/SUB_S3_6/SUB_B3_6/SUB_S6/SUB_S8才使能u8_en_para_force_discharge(并机强制馈网)与u8_para_grid_meter_limit(取电限制),其余子型号清 0(:1567~:1574)。INVERTER_MAX/GINLONG_TYPE下无条件置 1(:1578、:1579)。
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,由保存作业触发)。步骤:
g_t_energy_info.u32_grid_charge += s_u32_energy_grid_charge/10,余数%10保留(:2105)。g_t_energy_info.u32_bat_dischg_feedin_grid += s_u32_bat_dischg_to_grid_energy_temp/10(:2108)。- 电池电量有两条互斥来源(
: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 作为电量权威源)。
u32_pv_charge = u32_bat_charge - u32_grid_charge(不足则 0),即把电池总充电量减去电网充电量推算 PV 充电量(:2130~:2137)。- 逆变器侧读数覆盖(
: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 时保留旧值」,可防止通讯瞬时失败把电量清零。 u32_generator_energy_output += s_u32_energy_generator_output后立即清零累加器(:2164、:2165);u32_generator_energy_charge += s_u32_energy_generator_charge后清零(:2214、:2215)——后者的源累加器无写入者,恒 0。- 电表读数覆盖:
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,实现在本文件)
task_system_aux(void *p_arg)—— 由user/main.c:167的OSTaskCreateExt()创建,OSTaskNameSet(..., "task_system_aux", ...)(user/main.c:176)enenrgy_count_handle(void)—— 由data/system_config/system_config.c:3063(保存ENERGY_INFO_PAGE时)调用- 15 个
energy_read_*()—— 主要调用方:net/ems.c、data/resume_data/flash/flash_data_store.c、midware/interface/modbus/modbus.c、midware/interface/modbus/sunspec_business.c、hmi/ui/ui_main.c、hmi/ui/ui_history_*.c、midware/inverter/set/set_comm_usart_s5.c、midware/bms/alpha/battery_comm.c get_realtime_bat_chg/get_realtime_bat_dischg/get_realtime_bat_chg_from_grid/get_realtime_bat_dischg_feedin_grid——net/ems.cget_average_power_of_gridmeter(u8)——net/ems.c、data/resume_data/flash/flash_data_store.cget_rtc_tick(void)——logic/logic.c、net/ems.c、net/server.c、data/resume_data/flash/flash_data_store.cset_system_function_enable(u8,u8,u8)——net/server.cparallel_system_function_handle(void)—— 仅本文件task_system_aux内部调用,无外部调用者(见第 7 节)
本文件导出的全局变量
g_t_energy_info—— 定义于:35,被data/data.c、data/system_config/system_config.c(读写 + CRC + 落 Flash)使用SYSTEM_DATETIME—— 定义于:36,被data/fault_warn_log/fault_warn_log.c、data/net_event_log/net_event_log.c、logic/logic.c、logic/generator.c、hmi/key.h、hmi/ui/ui_setting_user_sys_date.c、midware/bms/alpha/battery_comm.c、midware/evc/evc.c等使用g_t_sys_datetime—— 定义于:37,被logic/logic.c、midware/bms/bms.c使用
本文件调用的外部函数 / 变量
| 来源模块 | 具体符号 |
|---|---|
midware/meter/meter.h | meter_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.h | inverter_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.h | bms_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.h | g_t_logic_info.t_logic_control_parameter.s32_ppv_inv、.s32_pbat_inv |
logic/generator.h | get_offgrid_mode_gc_power() |
midware/inverter/parallel.h | parallel_read_config_machine_precept()、PARA_COM_TYPE_USART3 |
midware/inverter/inverter.h | g_t_inv_info.t_config_data.u8_parallel_mode、g_t_inv_info.t_afci_info.u8_support_afci_flag |
data/data.h | ems_set_fault_bit(index, value)、g_t_ems_info |
data/system_config/system_config.h | g_system_function_info、g_t_system_base_info、save_job_post(msg)、SYSTEM_BASE_PARA_PAGE |
hardware/pcf8563/pcf8563.h | PCF8563_Get_DateTime_Other() |
hardware/gpio/gpio.h | monitor_software_run_blink()(系统运行灯翻转) |
hardware/iwdg/iwdg.h | IWDG_Feed() |
midware/ethernt/ethernet.h | internet_time_handle() |
midware/debug/board_test.h | board_test() |
midware/debug/ate.h | passthrough_detection() |
net/ems.h | Set_Setup_Config_Extend1_Status(bSet) |
system/board_init/board_init.h | get_board_type()、BOARD_EMS3_6_1_E~BOARD_EMS3_6_4_E |
UCOSII | OSTimeGet()、OSTimeDlyHMSM() |
| libc | mktime()、abs()、memset() |
6. 依赖关系
依赖
- 头文件:
system_aux.h、app_cfg.h、meter.h、bms.h、data.h、inverter.h(重复 include 两次,:18、:19)、logic.h、key.h、ct.h、ate.h、board_test.h、gpio.h、generator.h、iwdg.h、ethernet.h、system_config.h、board_init.h、ems.h - 间接依赖:
hardware/pcf8563(RTC)、hardware/flash(经debug.h的ASSERT_FLASH_SECTOR_SIZE)、UCOSII(tick 与任务 API)
被依赖
- 直接
#include "system_aux.h"的文件超过 20 个,覆盖data/(data.c、system_config.c、fault_warn_log.c、net_event_log.c、flash_data_store.c)、logic/(logic.c、generator.c)、net/(ems.c、server.c)、hmi/(ui_main.c及 7 个ui_history_*.c、ui_setting_user_sys_date.c、key.h)、midware/(bms.c、alpha/battery_comm.c、evc.c、evc_ii.c、set/set_comm_usart_s5.c、modbus.c、sunspec_business.c) - 反向依赖(本文件被谁调用):
user/main.c创建任务;data/system_config/system_config.c调enenrgy_count_handle();其余多为读g_t_energy_info/SYSTEM_DATETIME/get_rtc_tick()。 - 无任何模块反向调用
parallel_system_function_handle(),也无模块引用device_function_init*/ststem_function1_init等 static 函数。
7. 备注与风险点
确定性问题
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声明,外部无从调用,说明这段代码是「想写成公共接口但实际自用」,属版本遗留痕迹。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 的「油机充电电量」。- 油机发电量的量纲与其它电量不一致(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)。建议只针对油机这一项与云端协议口径核对。 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 量级的误差对能量积分影响可忽略,但属于公式不自洽。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说明,读者容易误认为漏判。- 整表清零的并发窗口:
parallel_system_function_handle()在并机方式变化时memset(&g_system_function_info.t_dev_fun, 0, ...)(:2587),随后原地重建。t_dev_fun被net/ems.c、modbus.c等其它任务直接读取,没有任何临界区或原子切换保护,存在读到全 0 中间态的竞态。 system_function_release_processing_handle的覆盖语义有破坏性:只要 EEPROM 镜像与固件 capability 位不同,就用固件值整体覆盖(:1723~:1727)。这意味着 OTA 降级会把新版本通过set_system_function_enable()远程开启的位一次性清掉;且由于比较单位是 64 位整体(u64_all),任何一位差异都会触发全量覆盖。rtc_refresh与PCF8563_Get_DateTime_Other的高频轮询:每 400ms 走一次 I²C 读 RTC。同一 I²C 总线上若还有其他慢速从机,此处没有超时/重入保护(依赖PCF8563_Get_DateTime_Other内部实现),是潜在的总线争用点。device_function_init内部三段的复制粘贴::450~:568与:687~:799两段几乎逐行相同,差异只在少数位。这类大段重复极易在后续改型时只改一处,是本文件最典型的可维护性风险。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 配置改变,调用方需知晓。
可疑但影响较低的写法
:2144等 5 处逆变器读数覆盖使用「非 0 才覆盖」,好处是抗通讯失败,坏处是真的读到 0(例如逆变器刚复位、当日电量为 0)时会保留上一次的旧值,导致电量统计偏高而非偏低。- 位域里的「表编号」字段容易被误读:
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 是正确的表号而非位值,阅读时勿误判为位赋值错误。 ststem_function1_init()、enenrgy_count_handle()、get_inverter_sub等拼写(ststem、enenrgy)已成为既定接口名,改名波及面大。- 文件里保留了 4 个被注释掉的调试函数(共约 300 行死代码,
:1737~:2047),其中printf_debug_info()在DEBUG_SYSTEM_AUX==1时被task_system_aux引用(:2088)——但该函数体已被注释,若把DEBUG_SYSTEM_AUX改为 1 会直接编译失败(未定义引用)。 DEBUG_SYSTEM_AUX打开时还会执行g_t_task_run_status.u32_task_system_aux_cnt++(:2089),这是任务存活检测的埋点,属调试遗留。
未发现的问题:本文件无指针运算、无数组下标访问、无动态内存申请;set_system_function_enable 对 u8_index/u8_value 有显式范围保护(:2555)。