parallel.c
parallel_local_info_handle()(第 161 行)4.3 SOC 限值与禁充禁放:parallel_sys_soc_limit(u8 idx)(第 60 行)4.4 主机功率分配:parallel_host_machine_logic_handle()(第 334 行)4.5 从机逻辑:parallel_from_machine_logic_handle()(第 850 行)4.6 单机逻辑:parallel_one_machine_logic_handle()(第 906 行)4.7 通信入口与 UART 重配置:parallel_can_comm_handle() / parallel_sci_comm_handle()4.8 parallel_read_* 系列的三种统计口径4.9 带副作用的"读"函数4.10 通信方案自适应与 inverter_get_parallel_mode()4.11 关键全局变量的读写关系5. 对外接口5.1 本文件导出的函数5.2 本文件导出的全局变量5.3 本文件调用的外部函数/变量6. 依赖关系6.1 依赖(本文件包含/使用的模块)6.2 被依赖7. 备注与风险点源文件路径:
midware/inverter/parallel.c所属模块:midware/inverter(逆变器中间件 / 并机) 代码行数:2279
1. 文件职责
本文件是 EMS 并机(多机并联)功能的业务核心:它把"本机 + 各从机"的实时数据汇成一组"系统级"量交给 logic/ 做能量调度,再把 logic/ 算出的系统级功率指令按 SOC 与功率上限拆成每台机的单机指令,交由 CAN/串口通信层下发。它在数据流上处于"逆变器/BMS 原始数据 → 并机聚合 → 能量调度 → 单机功率分配 → 通信下发"链条的正中间。文件同时承担三种角色的差异化处理:主机(主机 + 从机群)、从机(接收主机指令并写回本地逆变器/BMS)、单机(直通),由 g_t_inv_info.t_config_data 的 u8_parallel_en / u8_parallel_mode 决定分支。驱动来源是 inverter.c 的两个 uCOS-II 任务:task_inverter_can(30 ms 周期,调用 parallel_can_comm_handle() 与 parallel_data_handle())和 task_parallel_sci(100 ms 周期,调用 parallel_sci_comm_handle())。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
PARALLEL_HOST_MACHINE_NETWORKING_PARAMETER_T g_t_host_machine_networking_info[PARALLEL_MAX_NUM] | 结构体数组(全局) | 主机侧从机档案表,[0] 为本机,[1..15] 为从机;承载 u8_parallel_addr_en(槽位占用)、u8_parallel_lost_flag(心跳丢失)、u32_rcv_tick(最后收帧时刻) |
PARALLEL_HOST_MACHINE_RUNNING_INFO_T g_t_host_machine_running_info[PARALLEL_MAX_NUM] | 结构体数组(全局) | 每台机的实时镜像 + 下发指令缓存,同时充当发送缓冲区(通信层直接读它的"发送段"字段组帧)。[0] 为本机 |
PARALLEL_FROM_MACHINE_NETWORKING_PARAMETER_T g_t_from_machine_networking_info | 结构体(全局) | 从机角色下本机的地址/在线状态 |
PARALLEL_FROM_MACHINE_RUNNING_INFO_T g_t_from_machine_running_info | 结构体(全局) | 从机角色下收到的主机指令镜像 |
PARALLEL_TOTAL_MACHINE_NETWORKING_T g_t_parallel_total_machine_network | 结构体(全局) | 并机网络总状态:通信成功标志、当前尝试的通信方案、方案自适应定时器 |
PARALLEL_MAX_NUM | 宏(parallel.h:15) | 16,数组维度与所有遍历边界 |
PARALLEL_BAT_SOC_CTRL_RATE_SET | 宏(parallel.h:21) | 9,SOC 均衡中按"可充/可放容量占比"分配的权重(分母 10) |
PHASE_L1 / PHASE_L2 / PHASE_L3 / PHASE_ALL | 宏(inverter.h) | 相序索引,用于三相功率/电压分相读取 |
cNormalMode | 宏(inverter.h:23) | 3,逆变器正常工况码,只有此工况的机器参与功率分配 |
USER_FORCE_CHARGE_MODE / TIME_FORCE_CHARGE_MODE / TIME_FORBID_DISCHG_MODE / FORCE_BAT_FEEDIN_MODE / TIME_ALLOW_DISCHG_MODE / TIME_PEAK_SHAVING_MODE / DISPATCH_MODE | 宏/枚举(logic.h) | 顺序控制模式,parallel_sys_soc_limit 据此决定禁充/禁放 |
CHARGE_CONTROL | 宏(dispatch.h) | 调度控制模式中的"充电控制",用于调度模式下的 SOC 门槛判断 |
FORCE_SOC_MULT | 宏(logic.h/dispatch.h) | 调度强制 SOC 的倍率,u8ForceSOC * FORCE_SOC_MULT 得到 0.1% 单位门槛 |
RESERVE_SOC_ENABLE / RESERVE_SOC_DISABLE | 宏 | 预留 SOC 使能状态 |
UNIQUE_DEVICE_ID1/2/3 | 宏 | 芯片唯一 ID 三个 32 位字,用于生成并机设备标识 |
PARA_COM_RELY_ON_CARD / PARA_COM_TYPE_CAN / PARA_COM_TYPE_UART4 / PARA_COM_TYPE_USART3 / PARA_COM_TYPE_NONE | 枚举(parallel.h:41-46) | 并机通信方式,parallel_sci_comm_handle 据此初始化 UART |
OTHER_MACHINE | 枚举(parallel.h:29) | 2,parallel_data_handle 中角色缓存的初值 |
BAUD_RATE_9600/115200/256000 | 宏(modbus.h) | 外部 485(UART3)波特率档位 |
3. 函数清单
3.1 静态函数原型声明(第 46-52 行)
| 函数名 | 作用域 | 说明 |
|---|---|---|
parallel_sys_soc_limit | static | 逐机计算禁充/禁放限值与系统预留 SOC 强充标志 |
parallel_local_info_handle | static | 采集本机(逆变器 + BMS)数据填入 g_t_host_machine_running_info[0] |
parallel_parallel_comm_init | static | 并机配置变化时清零各机档案与运行信息 |
parallel_host_machine_logic_handle | static | 主机角色功率分配主逻辑 |
parallel_from_machine_logic_handle | static | 从机角色:把主机指令写回本地配置与执行器 |
parallel_one_machine_logic_handle | static | 单机角色直通 |
3.2 全部函数
| 函数名 | 作用域 | 说明 |
|---|---|---|
parallel_sys_soc_limit | static,第 60 行 | 依据顺序控制模式与 SOC 计算该机 s16_max_charge_power_limit / s16_max_dischg_power_limit 及 u8_sys_force_charge_flag |
parallel_local_info_handle | static,第 161 行 | 生成本机设备 ID/MAC、采集逆变器 ACDC 全量运行数据、BMS 电池数据与升级标志,并做并机配置自愈 |
parallel_parallel_comm_init | static,第 284 行 | 清零从机槽位([1..15])、从机侧结构体与网络状态,并根据"是否依赖通信卡"预置首选通信方案 |
parallel_machine_isWorkInNormalMode | 全局(非 static),第 310 行 | 判定第 i 台机是否"地址有效 + 未丢失 + 工作于 cNormalMode" |
parallel_host_machine_logic_handle | static,第 334 行 | 主机功率分配核心:SOC 均衡 + 双权重分配 + 满功率优先 + 自发自用追零 + 油机限制 + 升级降额 + 两台机功率搬移 |
parallel_from_machine_logic_handle | static,第 850 行 | 从机:把 g_t_from_machine_running_info 转为本地设定值并写回逆变器/BMS/逻辑配置 |
parallel_one_machine_logic_handle | static,第 906 行 | 单机:直接透传 logic_read_* 设定值,附加馈网/升级/油机约束 |
parallel_data_handle | 全局,第 970 行 | 并机业务入口:采本地数据 → 配置变化检测 → 按角色分发 → 置通信丢失故障位 |
parallel_can_comm_handle | 全局,第 1025 行 | CAN 并机通信入口(仅在通信方案为 CAN 时继续) |
parallel_sci_comm_handle | 全局,第 1053 行 | 串口并机通信入口:通信方案变化时重配 UART3/UART4 波特率,再分发 |
parallel_getParallelNum | 全局(非 static),第 1112 行 | 统计正常工作的机器数,为 0 时返回 1(防除零) |
parallel_read_system_self_consumption | 全局,第 1141 行 | 自发自用阈值(SET_TYPE 直取,否则 ×机数) |
parallel_read_system_compensation_power | 全局,第 1159 行 | 电表补偿功率 ×机数 |
parallel_read_system_maxfeedin_offset_power | 全局,第 1173 行 | 最大馈网偏置功率 ×机数 |
parallel_read_system_control_accuracy | 全局,第 1187 行 | 控制精度 ×机数 |
parallel_read_system_rated_input_power | 全局,第 1201 行 | 各机 s16_inverter_rated_input_power 求和 |
parallel_read_system_rated_output_power | 全局,第 1223 行 | 各机 s16_inverter_rated_output_power 求和 |
parallel_read_system_rated_charge_power | 全局,第 1245 行 | 各机 s16_inverter_rated_bat_charge_power 求和 |
parallel_read_system_rated_dischg_power | 全局,第 1267 行 | 各机 s16_inverter_rated_bat_dischg_power 求和 |
parallel_read_system_pv_status | 全局,第 1289 行 | 任一正常机 u8_feedin_flag 为真则为真 |
parallel_read_system_grid_status | 全局,第 1319 行 | 存在任一正常机即为真(有并机即认为系统并网) |
parallel_read_system_grid_voltage | 全局,第 1342 行 | 取首个正常机的对应相电网电压 |
parallel_read_system_grid_power | 全局,第 1382 行 | 各正常机对应相/全相电网功率求和 |
parallel_read_system_inv_power | 全局,第 1424 行 | 各正常机对应相/全相逆变功率求和 |
parallel_read_system_backup_power | 全局,第 1466 行 | 各正常机对应相/全相备电功率求和 |
parallel_read_system_inv_grid_freq | 全局,第 1508 行 | 取首个 s16_Fgrid >= 4000(≥40.00 Hz)的有效电网频率 |
parallel_read_system_inv_backup_freq | 全局,第 1535 行 | 取最后一个 s16_Fbackup >= 4000 的有效备电频率 |
parallel_read_system_inv_pv_power | 全局,第 1561 行 | 各正常机 4 路 PV 功率求和 |
parallel_read_system_inv_bat_curr | 全局,第 1586 行 | 各正常机逆变侧电池电流求和 |
parallel_read_system_inv_bat_power | 全局,第 1608 行 | 各正常机逆变侧电池功率求和 |
parallel_read_system_bat_rated_charge_power | 全局,第 1630 行 | 各正常机电池最大充电功率求和 |
parallel_read_system_bat_rated_dischg_power | 全局,第 1652 行 | 各正常机电池最大放电功率求和 |
parallel_read_system_cmd_force_charge_flag | 全局,第 1674 行 | 汇总电池可充功率,为 0 时清 g_t_logic_info.Force_Charge_Local(带副作用) |
parallel_read_system_sys_force_charge_flag | 全局,第 1702 行 | 任一台机 u8_sys_force_charge_flag 为真即为真 |
parallel_read_system_user_force_charge_flag | 全局,第 1729 行 | 用户强充模式下汇总充电限值为 0 则清 g_t_logic_info.Force_Charge_user(带副作用) |
parallel_read_system_self_test_flag | 全局,第 1757 行 | 自检状态机:使能跳变时切 P-V/P-F 降载与 MPPT 百分比,150 s 超时自动关闭 |
parallel_read_system_bat_force_charge_flag | 全局,第 1796 行 | 任一台机 BMS 强制充电位为真即为真 |
parallel_read_system_bat_low_soc | 全局,第 1824 行 | 各机 SOC 最小值(初值 1000) |
parallel_read_system_bat_average_soc | 全局,第 1847 行 | 各正常机 SOC 平均值;无正常机时回退本机 SOC |
parallel_read_system_bat_volt | 全局,第 1881 行 | 取首个正常机电池电压 |
parallel_read_system_bat_power | 全局,第 1904 行 | 各正常机电池功率求和 |
parallel_read_system_max_charge_power_limit | 全局,第 1926 行 | 各正常机最大充电限值求和 |
parallel_read_system_max_dischg_power_limit | 全局,第 1948 行 | 各正常机最大放电限值求和 |
parallel_read_gc_control_en | 全局,第 1974 行 | 返回 [0].u8_gc_control_en(油机使能) |
parallel_read_freq_dispatch_en | 全局,第 1986 行 | 返回 [0].u8_freq_dispatch_en(调频调度使能) |
parallel_read_dc_total_set_bat_power | 全局,第 1998 行 | 返回 [0].s16_dc_total_set_bat_power |
parallel_read_dc_total_set_inv_power | 全局,第 2010 行 | 返回 [0].s16_dc_total_set_inv_power |
parallel_read_dc_total_set_grid_power | 全局,第 2022 行 | 返回 [0].s16_dc_total_set_grid_power |
parallel_read_dc_three_phase_set_bat_power | 全局,第 2034 行 | 返回 [0].s16_dc_three_phase_set_bat_power |
parallel_read_dc_three_phase_set_inv_power | 全局,第 2046 行 | 按相返回 [0] 的 DC 三相逆变设定功率 |
parallel_read_ac_total_set_inv_power | 全局,第 2078 行 | 返回 [0].s16_ac_total_set_inv_power |
parallel_read_ac_total_set_bat_charge_limit_power | 全局,第 2090 行 | 返回 [0].s16_ac_total_set_bat_charge_limit_power |
parallel_read_ac_total_set_bat_dischg_limit_power | 全局,第 2102 行 | 返回 [0].s16_ac_total_set_bat_dischg_limit_power |
parallel_read_ac_total_set_bat_forced_charge_en | 全局,第 2114 行 | 返回 [0].s16_ac_total_set_bat_forced_charge_en |
parallel_read_ac_three_phase_set_inv_power | 全局,第 2126 行 | 按相返回 [0] 的 AC 三相逆变设定功率 |
parallel_read_ac_three_phase_set_bat_charge_limit_power | 全局,第 2158 行 | 返回 [0].s16_ac_three_phase_set_bat_charge_limit_power |
parallel_read_ac_three_phase_set_bat_dischg_limit_power | 全局,第 2170 行 | 返回 [0].s16_ac_three_phase_set_bat_dischg_limit_power |
parallel_read_ac_three_phase_set_bat_forced_charge_en | 全局,第 2182 行 | 返回 [0].s16_ac_three_phase_set_bat_forced_charge_en |
parallel_read_backup_en | 全局,第 2194 行 | 返回 [0].u8_inv_backup_en |
parallel_set_backup_en | 全局,第 2206 行 | 写 [0].u8_inv_backup_en |
parallel_set_config_machine_precept | 全局,第 2217 行 | 写 t_config_data.u8_parallel_precept 并置 g_t_config_save_flag.u8_set_inverter_config_flag 触发保存 |
parallel_read_config_machine_precept | 全局,第 2232 行 | 读 t_config_data.u8_parallel_precept |
parallel_set_full_power_priority_enable | 全局,第 2243 行 | 写 [0].u8_bat_soc_priority_en |
parallel_read_full_power_priority_enable | 全局,第 2254 行 | 读 [0].u8_bat_soc_priority_en |
parallel_set_bat_soc_threshold | 全局,第 2265 行 | 写 [0].u16_bat_soc_full_power_threshold |
parallel_read_bat_soc_threshold | 全局,第 2276 行 | 读 [0].u16_bat_soc_full_power_threshold |
4. 核心逻辑解析
4.1 总体调度时序与任务配合
task_inverter_can (30 ms 周期, inverter.c:1301-1304)
├─ parallel_can_comm_handle() // 只发/收 CAN 帧,不做功率计算
└─ parallel_data_handle() // 全部功率分配与状态维护
task_parallel_sci (100 ms 周期, inverter.c:1316-1318)
└─ parallel_sci_comm_handle() // 只做 Modbus 收发与组帧
两个任务之间没有信号量、没有消息邮箱,唯一的同步手段是共享全局结构体 g_t_host_machine_running_info / g_t_from_machine_running_info 和 OSTimeGet() 时间戳。logic/ 任务通过 parallel_read_* 读这些结构体,属于典型的"裸共享 + 时间片错开"设计(见第 7 节风险)。
parallel_data_handle()(第 970 行)的固定顺序:
parallel_local_info_handle()—— 刷新本机数据到[0];- 配置变化检测:把
u8_parallel_en/u8_parallel_mode/u8_parallel_com_type_can三个配置与静态缓存比较,任一变化即调parallel_parallel_comm_init()全量重置并机状态; - 角色分发:
| 条件 | 调用 |
|---|---|
u8_parallel_en == FALSE | parallel_one_machine_logic_handle() |
u8_parallel_en != FALSE && u8_parallel_mode != FALSE(=1 主机) | parallel_host_machine_logic_handle() |
u8_parallel_en != FALSE && u8_parallel_mode == FALSE(=0 从机) | parallel_from_machine_logic_handle() |
- 遍历
[1..15],任一u8_parallel_lost_flag为真则ems_set_fault_bit(21, TRUE);并单独用ems_set_fault_bit(20, g_t_from_machine_networking_info.u8_parallel_lost_flag != FALSE)报告"从机自身与主机失联"。
ems_set_fault_bit(index, value)(data/data.c:338)就是置/清 g_t_ems_info.u_ems_fault.u32_all 的第 index 位,故障位 20 = 从机侧并机通信丢失,21 = 主机侧有从机丢失。
4.2 本机数据采集:parallel_local_info_handle()(第 161 行)
无输入参数,纯粹"把散落各处的本机数据搬进 [0]",分四类:
- 身份:
l_u32_unique_id[3] = {UNIQUE_DEVICE_ID1,2,3},计算一次GetCrcCode_Modbus()得到 16 位设备 ID,高/低字节存入u8_unique_device_id[0..1](static l_u16_unique_id_crc缓存,只算一次);u8_mac[0..2]取IP_Address.u8_mac_addr[3..5];u8_parallel_addr = PARALLEL_BROADCAST_ADDR(0)、u8_parallel_addr_en = TRUE,即主机永远占 0 号槽。 - 控制参数(发送段):顺序控制模式(
logic_read_sequencecontrol_mode())、安规(inverter_read_safety/safesub())、N-N 短接(t_config_data.u8_nn_short_detect_state)、DRM 模式、u8_bb_model = (u8_bb_enable << 4) | u8_bb_model(把 BB 使能与模式打包进一个字节)、三相不平衡模式、MPPT 百分比、逆变器开关机、BMS 关机。 - 运行数据(接收段):
inverter_read_rated_*4 个额定功率;inverter_read_pv_volt/power(PV_CHANAEL1..4)共 8 个;电网/备电/逆变三相电压功率 + 全相功率 + 频率;母线侧inverter_read_bat_volt/curr/power();u8_feedin_flag = inverter_get_pv_start_status();u8_work_mode = inverter_read_run_mode()。注意s32_Ppv_pvcharger/s32_Pbat_pvcharger被硬置 0(第 209-210 行),即主机本地不使用 PV 充电器数据。 - 电池数据:容量、
BAT_WORK_FLAG_U四位(其中bit_bat_force_charge_flag = (bms_read_status_flag()==FORCE_CHG_FORBID_DISCHG_E) || (==FORCE_CHG_ALLOW_DISCHG_E))、电池组数、SOC/电压/电流/功率、最大充放电功率;升级标志:EMS 取Get_Status_Is_Updated_From_Server(UPDT_EMS),逆变器取三个 INV 通道的或,BMS 取Get_Status_Is_Updated_From_Server(UPDT_BMS) || (GetPercentDownloadBMS() > 50 && IsDownloadBMS())。
末尾有配置自愈(第 264-274 行):
if (g_t_inv_info.t_config_data.u8_parallel_en == FALSE) {
g_t_inv_info.t_config_data.u8_parallel_mode = 0;
g_t_inv_info.t_config_data.u8_parallel_com_type_can = PARA_COM_RELY_ON_CARD;
if (parallel_read_config_machine_precept() != PARA_COM_TYPE_NONE)
parallel_set_config_machine_precept(PARA_COM_TYPE_NONE);
}
g_t_inv_info.t_config_data.u8_parallel_selection = 0; // 恒清零,注意!
4.3 SOC 限值与禁充禁放:parallel_sys_soc_limit(u8 idx)(第 60 行)
对第 idx 台机计算两个限值和系统强充标志,是功率分配的前置约束。SOC 单位为 0.1%,故所有 logic_read_*_soc() 结果 *10。
充电限值 s16_max_charge_power_limit 的分支(第 79-100 行):
| 条件 | 结果 |
|---|---|
USER_FORCE_CHARGE_MODE 且 SOC >= user_charger_cut_soc*10 且该阈值 <1000 | 0(禁充) |
TIME_FORCE_CHARGE_MODE 且 SOC >= charger_cut_soc*10 且阈值 <1000 | 0 |
DISPATCH_MODE 且调度 u8ControlMode == CHARGE_CONTROL 且 SOC > u8ForceSOC*FORCE_SOC_MULT + 4 | 0 |
| 其它 | min(s32_bat_max_charge_power, s16_inverter_rated_bat_charge_power) |
(阈值 < 1000) 是在排除"未配置/1000(100%)"这类占位值,避免把未设置当成"已到顶"。
放电限值 s16_max_dischg_power_limit(第 103-116 行):以下任一成立即置 0:
SOC <= 系统预留 SOC + 4(进入预留区,0.4% 迟滞);TIME_FORBID_DISCHG_MODE(时段禁放);FORCE_BAT_FEEDIN_MODE且SOC <= feedin_cut_soc + 4;TIME_ALLOW_DISCHG_MODE且SOC <= allow_discharge_cut_soc + 4;- 调度充电控制且
SOC <= u8ForceSOC*FORCE_SOC_MULT - 4; TIME_PEAK_SHAVING_MODE且SOC <= peakshving_discharge_cut_soc + 4。
否则 min(s32_bat_max_dischg_power, s16_inverter_rated_bat_dischg_power)。
系统预留 SOC 强充 u8_sys_force_charge_flag(第 119-142 行),带 0.6% 迟滞的双门限状态机:
if (logic_read_ups_reserve_flag() != RESERVE_SOC_ENABLE && get_inv_ups_reserve_soc_enable_default() == TRUE)
→ FALSE // 预留 SOC 功能关但逆变器默认开:强制清零
elif (bit_bms_work_flag == FALSE || u16_bat_soc == 0)
→ FALSE // BMS 不工作或 SOC 为 0:清零
else
SOC < reserve - 10 (1%) → TRUE
SOC > reserve - 4 (0.4%) → FALSE
中间区间保持上一次值 // 迟滞带
第 144-148 行是一段被注释掉的"油机逻辑 SOC 模式限制",说明该限制后来挪到了主机逻辑里(第 667-670 行)。
4.4 主机功率分配:parallel_host_machine_logic_handle()(第 334 行)
这是本文件最核心的算法。分 6 个阶段:
阶段 1:读取系统级指令(第 370-374 行)
l_s32_logic_dc_total_set_inv_power = logic_read_dc_total_set_inv_power();
l_s32_logic_ac_total_set_inv_power = logic_read_ac_total_set_inv_power();
l_s32_logic_ac_total_set_bat_charge_limit_power= logic_read_ac_total_set_bat_charge_limit_power();
l_s32_logic_ac_total_set_bat_dischg_limit_power= logic_read_ac_total_set_bat_dischg_limit_power();
阶段 2:满功率优先标志(第 377-393 行)
若 parallel_read_full_power_priority_enable() == TRUE,遍历所有正常机:
- 任一台
SOC < parallel_read_bat_soc_threshold()*10→l_s8_parallel_full_power_input_priority_temp = TRUE(需要尽快喂饱,进入"满功率输入"); - 否则 →
l_s8_parallel_full_power_output_priority_temp = TRUE(进入"满功率输出")。
注意两个标志可以同时为真(第一台 SOC 低、第二台 SOC 高时),后续代码对二者分别独立判断。
阶段 3:聚合本机与各从机的可用能力(第 396-432 行)
parallel_sys_soc_limit(i); // 先算第 i 台的充放限值
if (work_mode == cNormalMode) {
sum_bat_max_charge += max_charge_power_limit;
sum_bat_max_dischg += max_dischg_power_limit;
l_s32_bat_charge_capacity[i] = bat_capacity*(1000 - SOC)/1000; // 可充电量
l_s32_bat_dischg_capacity[i] = bat_capacity*SOC/1000; // 可放电量
logic_dc_total_set_inv_power -= s16_Pinvt; // 扣掉该机实际逆变功率
parallel_num++;
}
// 防除零
if (sum_bat_charge_capacity == 0) sum_bat_charge_capacity = 1;
if (sum_bat_dischg_capacity == 0) sum_bat_dischg_capacity = 1;
logic_dc_total_set_inv_power -= s16_Pinvt 是关键预处理:DC 侧逆变总需求在后续按"平均分配 + 补偿本机实际功率"处理(第 554 行),所以先在总量里扣除每台机的实际功率。
阶段 4:AC 侧电池充/放限值的 SOC 均衡双权重分配(第 434-510 行)
先累加当前各机的 AC 充/放限值:
l_s32_ac_set_bat_charge_limit_power_total += [i].s16_ac_total_set_bat_charge_limit_power;
l_s32_ac_set_bat_discharge_limit_power_total += [i].s16_ac_total_set_bat_dischg_limit_power;
再逐机计算(两条公式结构完全一致,以充电为例):
// 基础项:按"可充电量占比"分配,权重 PARALLEL_BAT_SOC_CTRL_RATE_SET/10 = 0.9
base = logic_ac_charge_limit * bat_charge_capacity[i] * 9 / sum_bat_charge_capacity / 10;
// 修正项:静态累加器,按"功率上限占比"消除静态误差
if (abs(logic_ac_charge_limit - ac_set_bat_charge_limit_power_total) > 2)
s_..._extra += (logic_ac_charge_limit - ac_set_bat_charge_limit_power_total)
* [i].s16_max_charge_power_limit / sum_bat_max_charge / 2;
[i].s16_ac_total_set_bat_charge_limit_power = base + s_..._extra;
这是一个"0.9 × 容量占比 + 积分型误差修正"的混合分配器:
- 容量占比项让 SOC 高(剩余可充容量小)的机少充、SOC 低的多充,实现 SOC 均衡;
s_s32_logic_ac_total_set_bat_charge_limit_power_extra/..._dischg_limit_power_extra(第 358-359 行,static)是累加型积分器:只要"逻辑总需求"与"上一拍各机实际分配之和"偏差超过 2(0.2 kW),就按该机功率上限占比追加修正,且/2相当于 0.5 的积分增益(不是宏PARALLEL_BAT_LIMT_CTRL_RATE_SET=1所暗示的 0.1)。
随后满功率优先旁路 SOC 均衡(第 465-472 行):input_priority 为真时充电限值改为 logic_ac_charge_limit / parallel_num;output_priority 为真时放电限值改为 logic_ac_dischg_limit / parallel_num。
最后夹紧(第 484-500 行):
if (charge_limit > max_charge_power_limit) charge_limit = max_charge_power_limit;
else if (charge_limit < 2) charge_limit = 0; // 小于 0.2 kW 直接归零,防微功率抖动
if (dischg_limit > max_dischg_power_limit) dischg_limit = max_dischg_power_limit;
else if (dischg_limit < 2) dischg_limit = 0;
阶段 5:DC/AC 总功率与三相功率逐机分配(第 516-748 行)
对每台正常机:
- DC 侧电池总功率(第 524-553 行):按
logic_read_dc_total_set_bat_power()的符号分流。> 0走放电公式(用bat_dischg_capacity/sum_bat_max_dischg),<= 0走充电公式(用bat_charge_capacity/sum_bat_max_charge)。两条分支都先算双权重和,再:if (sum_capacity == 0 || sum_max == 0) set = 双权重和; // 分母为 0 时不折半 else set = 双权重和 / 2; // 折半
注意这个 if 是写反的:分母为 0 时用全值、分母非 0 时用 /2,与注释和常规预期相反,且与阶段 4 的 /10 权重不成体系(见第 7 节)。
- DC 侧逆变总功率(第 554-559 行):
set = l_s32_logic_dc_total_set_inv_power / parallel_num + [i].s16_Pinvt,负值截 0。这是"总需求扣掉各机实际后平均分,再加上本机实际"的写法,等价于把总需求按机数均分但同时补偿本机当前出力,收敛更快。 - DC 侧电网总功率:直接取
logic_read_dc_total_set_grid_power()(未按机数拆分)。 - DC 三相电池功率:同 DC 总电池功率的两分支结构,用
logic_read_dc_three_phase_set_bat_power()。 - DC 三相逆变功率:直接取
logic_read_dc_three_phase_set_inv_power(PHASE_Lx)。 - AC 侧逆变总功率(第 598-643 行):
logic_ac_total_set_inv_power > 0(放电)时,若bms_get_ready_enable() == FALSE && !output_priority走双权重分配,否则logic / parallel_num平均分;再按"分母是否为 0 或满功率输出优先"决定是否/2。< 0(充电)时逻辑对称,用input_priority。 - 自发自用追零(第 645-651 行):
if (pgrid_metert < system_self_consumption && [i].s32_bat_max_charge_power == 0 && (u8_logic_power_calculation_sync_flag == TRUE || inverter_get_parallel_mode() == PARA_COM_TYPE_CAN)) { [i].s16_max_dischg_power_limit = 0; [i].s16_ac_total_set_inv_power = pgrid_metert + s32_max_feedin_power + [i].s16_Pinvt; }
即"电池已不能再充、电表功率还没到自发自用阈值"时,直接按"电表功率 + 最大馈网功率 + 本机实际功率"给逆变器下指令(用实测电表值闭环补偿),并把放电限值清零以免叠加。
- 满功率输出时追零(第 656-660 行):
pgrid_metert > self_consumption && output_priority时,额外叠加pgrid_metert / u8_regulator_speed / parallel_num,实现"满功率输出时也把电表追到零"。 - AC 三相不均衡功率(第 679-685 行):三相逆变功率与三相充电限值直接
logic_read_*()/parallel_num;三相放电限值却用logic_read_ac_three_phase_set_bat_dischg_limit_power() * [i].s16_max_dischg_power_limit / sum_bat_max_dischg(按功率上限占比),与同组其它字段的分配方式不一致(见第 7 节)。 - 油机限制(第 667-670 行):
SOC >= gc_charge_limit_soc + 4 && get_generator_running_flag() != FALSE→s16_ac_total_set_inv_power = 0(油机运行时不允许电池放电)。 - 馈网门禁(第 672-677 行):
u8_feedin_flag == FALSE && 放电限值 == 0 && 逆变设定 > 0→ 逆变设定清零(没有 PV 又不允许放电,就不该有逆变输出)。 - 升级降额(第 703-722 行):
bit_bms_upgrade_flag为真 → 清零 DC 总/三相电池功率、AC 总/三相充放限值与强制充电使能;else if bit_inv_upgrade_flag→ 只清零 DC 总/三相电池功率。即"BMS 在升级时彻底冻结电池充放,逆变器在升级时只冻结 DC 侧"。 - 非正常工况(第 724-746 行):
u8_work_mode != cNormalMode时把该机全部下发字段清零(DC 与 AC 的总量与三相量、强制充电使能全 FALSE)。
阶段 6:收尾(第 750-788 行)
g_t_logic_info.t_logic_control_parameter.u8_logic_power_calculation_sync_flag = FALSE; // 消费掉同步标志
// 两台机之间的功率搬移(硬编码 [0] 和 [1])
if([0].dc_set_bat_power > [0].max_dischg_limit) { [1].dc_set_bat_power += [0].dc - [0].max_dischg; [0].dc = [0].max_dischg; }
else if([1].dc_set_bat_power > [1].max_dischg_limit) { [0].dc_set_bat_power += [1].dc - [1].max_dischg; [1].dc = [1].max_dischg; }
else if([0].dc_set_bat_power < -[0].max_charge_limit) { [1].dc_set_bat_power += [0].dc + [0].max_charge; [0].dc = -[0].max_charge; }
else if([1].dc_set_bat_power < -[1].max_charge_limit) { [0].dc_set_bat_power += [1].dc + [1].max_charge; [1].dc = -[1].max_charge; }
inverter_set_mppt_percentage([0].u16_mppt_percentage);
[0].u8_gc_control_en = get_generator_running_flag();
[0].u8_freq_dispatch_en = get_freq_dispatch_enable();
注意这四个 if/else if 只处理 [0] 与 [1] 两台机,且是互斥的 else if 链——如果第 0 台同时充电超限、第 1 台又放电超限,只有第一个分支生效。
4.5 从机逻辑:parallel_from_machine_logic_handle()(第 850 行)
分两步:
- 无条件把主机指令搬到
[0](第 852-872 行):g_t_from_machine_running_info里所有 DC/AC 总与三相设定值、强制充电使能、u8_drm_mode(取本地g_t_logic_info.u8_drm_mode)逐字段写入g_t_host_machine_running_info[0]。这样做的目的是让parallel_read_*系列无需区分角色——它们都从[0]取值,从机侧也就自然返回主机下发的值。 - 仅当已组网成功(
u8_parallel_addr_en != FALSE)时同步配置(第 874-893 行)——这是"主机说了算"的配置单向覆盖:logic_set_sequencecontrol_mode()、inverter_set_three_unbalance_mode()、inverter_set_mppt_percentage()、inverter_write_safety()、inverter_write_safesub()、inverter_set_n_n_short();u8_bb_model拆回t_config_data.u8_bb_enable = (bb_model >> 4)与u8_bb_model = bb_model & 0x0F;u8_set_inv_on_off写g_t_inv_info.t_run_info,u8_set_bms_shutdown写g_t_bat_info.t_run_info;u8_inv_backup_en/u8_gc_control_en/u8_freq_dispatch_en/u8_drm_mode写入[0]。
4.6 单机逻辑:parallel_one_machine_logic_handle()(第 906 行)
u8_parallel_en == FALSE 时执行:先 parallel_sys_soc_limit(0) 得到本机充放限值(虽然不直接用其返回值,但会写 u8_sys_force_charge_flag 与限值字段),然后把 logic_read_* 的所有 DC/AC 设定值直接透传到 [0]。附加约束与主机路径同构:
- 馈网门禁(
u8_feedin_flag == FALSE && 放电限值 == 0 && 逆变设定 > 0→ 逆变设定 0); - BMS 升级 → 清零电池相关指令;逆变器升级 → 只清零 DC 侧;
- 刷新
u8_gc_control_en/u8_freq_dispatch_en;inverter_set_mppt_percentage(logic_read_mppt_percentage())。
4.7 通信入口与 UART 重配置:parallel_can_comm_handle() / parallel_sci_comm_handle()
- CAN 入口(第 1025 行):
inverter_get_parallel_mode() != PARA_COM_TYPE_CAN直接 return;否则按u8_parallel_mode分发到parallel_can_comm_host_machine_handle()/parallel_can_comm_from_machine_handle()。 - SCI 入口(第 1053 行):用
static s_u8_parallel_mode缓存inverter_get_parallel_mode(),只在变化时重新初始化 UART(避免每 100 ms 重配):
| 方案 | UART3(外部 485 / Modbus) | UART4(并机总线) |
|---|---|---|
PARA_COM_TYPE_USART3 | 115200 | 9600 |
PARA_COM_TYPE_UART4 | 按 g_t_modbus_config.u8_modbus_baud_rate(9600/115200/256000,非法值回落 9600) | 115200 |
PARA_COM_TYPE_NONE | 同上 | 9600 |
随后只有方案为 UART4 或 USART3 才继续分发。这里 g_t_modbus_config.u8_modbus_baud_rate 是外部 Modbus 从机通道的波特率,与并机通道共用 UART3 —— 所以采用 USART3 并机方案时,外部 Modbus 会被强制固定在 115200,interface.c:320-328 也为此把 modbus 波特率校正为 9600 后再调 pvcharger_send_host_machine_info()。
4.8 parallel_read_* 系列的三种统计口径
约 40 个读取函数并非同一口径,实现上分三类,调用方(logic/logic.c:937-1151)必须知道差异:
| 口径 | 判据 | 例子 |
|---|---|---|
| 求和(系统总量) | parallel_machine_isWorkInNormalMode(i),累加 | parallel_read_system_rated_*_power、..._grid_power、..._inv_power、..._bat_power、..._max_*_power_limit、..._inv_pv_power、..._inv_bat_curr/power |
| 取首个有效值(系统代表值) | parallel_machine_isWorkInNormalMode(i) + break | ..._grid_voltage、..._bat_volt;频率类则附加 >= 4000 门槛(..._inv_grid_freq 用 break 取首个,..._inv_backup_freq 不 break 取最后一个,属不一致) |
| 逻辑或(布尔汇总) | 任一机为真即为真 | ..._pv_status、..._grid_status、..._sys_force_charge_flag、..._bat_force_charge_flag |
| 取最值/平均值 | 遍历全部 | ..._bat_low_soc(最小)、..._bat_average_soc(正常机求平均,无正常机回退 [0]) |
[0] 直取(本机/系统指令) | 不遍历 | 全部 parallel_read_dc_* / parallel_read_ac_* / ..._gc_control_en / ..._freq_dispatch_en / ..._backup_en |
另外注意"取首个/求平均"类用 parallel_machine_isWorkInNormalMode(要求 cNormalMode),而 ..._cmd_force_charge_flag / ..._sys_force_charge_flag / ..._user_force_charge_flag / ..._self_test_flag 用的是更宽松的"地址有效 + 未丢失"(不要求 cNormalMode)。这个差异是有意的:故障机也可能在强制充电。
4.9 带副作用的"读"函数
三个 parallel_read_* 函数会修改全局状态,属于设计上的坑:
parallel_read_system_cmd_force_charge_flag()(第 1674 行):先累加所有在线机的s32_bat_max_charge_power,若总和为 0 →g_t_logic_info.Force_Charge_Local = FALSE,然后返回它。parallel_read_system_user_force_charge_flag()(第 1729 行):累加s16_max_charge_power_limit,若当前为USER_FORCE_CHARGE_MODE且总和为 0 →g_t_logic_info.Force_Charge_user = FALSE。parallel_read_system_self_test_flag()(第 1757 行):完整的 150 s 自检状态机:
if (grid_status != TRUE) g_t_logic_info.u8_self_test_enable = FALSE; // 无电网不允许自检
if (s_u8_self_test_enable != g_t_logic_info.u8_self_test_enable) { // 使能沿
inverter_set_p_v_mode_down_load_flag(enable == FALSE); // 自检开 → 释放 P-V 降载
inverter_set_p_f_mode_down_load_flag(enable == FALSE); // 自检开 → 释放 P-F 降载
g_t_logic_info.u16_mppt_percentage = (enable == TRUE) ? 0 : 10000; // 自检开 → MPPT 0%,关 → 100%
s_s32_self_test_tick = OSTimeGet();
}
if (abs(OSTimeGet() - s_s32_self_test_tick) >= 150*1000) g_t_logic_info.u8_self_test_enable = FALSE; // 150 s 超时
else if (s_s32_self_test_tick > OSTimeGet()) s_s32_self_test_tick = OSTimeGet(); // tick 回绕保护
自检期间把 MPPT 百分比打到 0、解除 P-V/P-F 降载,正是为了能"满功率跑一次曲线";150 s 后强制退出,防呆。
4.10 通信方案自适应与 inverter_get_parallel_mode()
inverter.c:2957:
u8 inverter_get_parallel_mode(void) {
if (g_t_inv_info.t_config_data.u8_parallel_en == TRUE)
return parallel_read_config_machine_precept() != 0
? parallel_read_config_machine_precept() // 已持久化的实际方案
: g_t_parallel_total_machine_network.u8_run_machine_precept; // 尚未确定 → 当前尝试的方案
return PARA_COM_TYPE_NONE;
}
parallel_parallel_comm_init()(第 284 行)在初始化时按 u8_parallel_com_type_can 预置 u8_run_machine_precept:PARA_COM_RELY_ON_CARD → PARA_COM_TYPE_CAN,否则 PARA_COM_TYPE_UART4。之后由 interface.c:239 parallel_precept_change() 每隔 PARALLEL_MSTER_CHECK_TIME(15 s) / PARALLEL_SLAVE_CHECK_TIME(30 s) 轮换,或每 3600000 tick 触发一次强制切换,直到 u8_comm_success_flag 置位(任一通信成功)后调用 parallel_set_config_machine_precept() 固化。
4.11 关键全局变量的读写关系
| 变量 | 写方 | 读方 | 触发条件 |
|---|---|---|---|
g_t_host_machine_running_info[0] | parallel_local_info_handle(本机数据)、各 *_logic_handle(下发指令)、parallel_set_* 系列(阈值配置) | parallel_read_* 全系列、parallel_can.c / parallel_sci.c 组帧、net/ems.c、net/server.c、各厂商逆变器通信层 | 每 30 ms 任务周期 |
g_t_host_machine_running_info[1..15] | parallel_can.c / parallel_sci.c 收包解析 | 主机功率分配、parallel_read_* 汇总 | 收到从机帧时 |
g_t_host_machine_networking_info[i].u32_rcv_tick | 通信层收帧成功 | 通信层 15 s 心跳判定、parallel_parallel_comm_init 初值 | 每次收帧 |
g_t_parallel_total_machine_network.u8_comm_success_flag | 通信层收帧成功置 TRUE;interface.c 切换方案时清零 | interface.c 方案固化判据 | 收帧 / 切换 |
g_t_logic_info.t_logic_control_parameter.u8_logic_power_calculation_sync_flag | 由 logic/ 在完成一轮功率计算后置 TRUE | parallel_host_machine_logic_handle 第 647 行(自发自用追零门禁)并在第 750 行清 FALSE | 每轮调度 |
g_t_host_machine_running_info[0].u8_bat_soc_priority_en / u16_bat_soc_full_power_threshold | parallel_set_full_power_priority_enable / parallel_set_bat_soc_threshold(由 net/server.c:1130/1134 下发) | parallel_host_machine_logic_handle | 云端设参 |
g_t_logic_info.Force_Charge_Local / Force_Charge_user | parallel.c:1689 / parallel.c:1744(清零) | logic/ | 可充功率汇总为 0 |
g_t_logic_info.u8_self_test_enable / u16_mppt_percentage / P-V、P-F 降载标志 | parallel_read_system_self_test_flag | logic/、inverter.c | 自检使能跳变 / 150 s 超时 |
g_t_inv_info.t_config_data.u8_parallel_precept | parallel_set_config_machine_precept | inverter_get_parallel_mode、interface.c、system_aux.c | 方案固化 |
g_t_config_save_flag.u8_set_inverter_config_flag | parallel_set_config_machine_precept 置 TRUE | 断电续传/配置保存任务 | 方案变更 |
g_t_ems_info.u_ems_fault.u32_all 第 20/21 位 | parallel_data_handle | 告警上报 | 每 30 ms |
5. 对外接口
5.1 本文件导出的函数
按 parallel.h 声明、本文件实现的全部全局函数(完整清单见第 3.2 节):parallel_data_handle、parallel_can_comm_handle、parallel_sci_comm_handle、约 40 个 parallel_read_system_*(含 ..._self_consumption / ..._compensation_power / ..._maxfeedin_offset_power / ..._control_accuracy / ..._self_test_flag / 各类 force_charge_flag / SOC 与功率汇总)、约 20 个 parallel_read_dc_* / parallel_read_ac_*、parallel_read_gc_control_en、parallel_read_freq_dispatch_en、parallel_read_backup_en、parallel_set_backup_en、parallel_read_config_machine_precept、parallel_set_config_machine_precept、parallel_read_full_power_priority_enable、parallel_set_full_power_priority_enable、parallel_read_bat_soc_threshold、parallel_set_bat_soc_threshold。
另外还导出两个未在头文件声明的全局函数(供本文件内部使用,但理论上外部可见):
parallel_machine_isWorkInNormalMode(size_t i)(第 310 行)parallel_getParallelNum(void)(第 1112 行)
主要调用方:
| 调用方 | 用到的接口 |
|---|---|
midware/inverter/inverter.c:1273/1276/1303 | parallel_can_comm_handle、parallel_data_handle、parallel_sci_comm_handle |
midware/inverter/inverter.c:2957 | parallel_read_config_machine_precept、g_t_parallel_total_machine_network |
logic/logic.c:166,937-1151,1577,1662,2398-2411,2437,2600-2605 | 绝大多数 parallel_read_system_* / parallel_read_backup_en / parallel_set_backup_en |
logic/ac_control.c:460-461 | parallel_read_system_max_charge_power_limit |
logic/dispatch.c:610,632,1014 | parallel_read_system_inv_grid_freq、parallel_read_system_bat_average_soc |
data/resume_data/flash/flash_data_store.c:775,778 | parallel_read_dc_total_set_inv_power、parallel_read_dc_total_set_bat_power |
net/ems.c:621-622,793-794,823-838 | parallel_read_dc_total_set_*、parallel_read_ac_total_set_*、parallel_read_full_power_priority_enable、parallel_read_bat_soc_threshold、g_t_host_machine_running_info[] |
net/server.c:1130,1134 | parallel_set_full_power_priority_enable、parallel_set_bat_soc_threshold |
midware/interface/modbus/modbus.c:2851-2859,4481-4505 | parallel_read_ac_total_set_*、parallel_read_dc_total_set_*、parallel_read_dc_three_phase_set_inv_power |
midware/interface/modbus/sunspec_business.c:1437 | parallel_read_system_pv_status |
midware/interface/interface.c:241-313 | parallel_read_config_machine_precept、parallel_set_config_machine_precept、g_t_parallel_total_machine_network |
midware/auxiliary/system_aux.c:2584-2586 | parallel_read_config_machine_precept |
各厂商逆变器通信层(alphaess_comm_can.c:243-445、alphaess_comm_usart.c:4075,5620、ginlong_comm_can.c:111-204、ginlong_comm_usart.c:2387,5130、inovance_comm_can.c:216-376、inovance_comm_usart.c:2536、goodwe_comm.c:1694,2586) | parallel_read_gc_control_en、parallel_read_freq_dispatch_en、parallel_read_dc_*、parallel_read_ac_*、parallel_read_ac_three_phase_* |
5.2 本文件导出的全局变量
| 变量 | 类型 | 使用方 |
|---|---|---|
g_t_host_machine_networking_info[16] | PARALLEL_HOST_MACHINE_NETWORKING_PARAMETER_T | parallel_can.c、parallel_sci.c(extern 声明) |
g_t_host_machine_running_info[16] | PARALLEL_HOST_MACHINE_RUNNING_INFO_T | parallel_can.c、parallel_sci.c、net/ems.c、net/server.c |
g_t_from_machine_networking_info | PARALLEL_FROM_MACHINE_NETWORKING_PARAMETER_T | parallel_can.c、parallel_sci.c |
g_t_from_machine_running_info | PARALLEL_FROM_MACHINE_RUNNING_INFO_T | parallel_can.c、parallel_sci.c |
g_t_parallel_total_machine_network | PARALLEL_TOTAL_MACHINE_NETWORKING_T | parallel.h 已 extern;interface.c、inverter.c |
5.3 本文件调用的外部函数/变量
按 #include(第 13-32 行)分组:
| 来源模块 | 使用的函数/变量 |
|---|---|
inverter.h / inverter.c | inverter_read_rated_ac_discharge_power、inverter_read_rated_ac_charge_power、inverter_read_rated_dc_charge_power、inverter_read_rated_dc_discharge_power、inverter_read_pv_volt/power、inverter_read_grid_volt/actpower/freq、inverter_read_backup_*、inverter_read_inv_actpower/freq、inverter_read_bat_volt/curr/power、inverter_get_pv_start_status、inverter_read_run_mode、inverter_read_safety、inverter_read_safesub、inverter_write_safety/safesub、inverter_get_three_unbalance_mode、inverter_set_three_unbalance_mode、inverter_get_parallel_mode、inverter_set_n_n_short、inverter_set_mppt_percentage、inverter_set_p_v_mode_down_load_flag、inverter_set_p_f_mode_down_load_flag、inverter_get_chg_from_grid_limit、inverter_get_offset_meter_power、inverter_get_offset_maxfeedin_power、inverter_get_control_accuracy、g_t_inv_info |
bms.h / bms.c | bms_get_connect_state、bms_read_work_status、bms_read_relay_status、bms_read_status_flag、bms_get_store_num、bms_read_battery_capacity、bms_read_total_soc/volt/cur/power、bms_read_max_charge_power、bms_read_max_dischg_power、bms_get_ready_enable、g_t_bat_info |
logic.h / logic.c | logic_read_sequencecontrol_mode、logic_set_sequencecontrol_mode、logic_read_dc_total_set_bat_power/inv_power/grid_power、logic_read_dc_three_phase_set_bat_power、logic_read_dc_three_phase_set_inv_power、logic_read_ac_total_set_inv_power、logic_read_ac_total_set_bat_charge_limit_power/dischg_limit_power/forced_charge_en、logic_read_ac_three_phase_set_*、logic_read_user_charger_cut_soc、logic_read_charger_cut_soc、logic_read_feedin_cut_soc、logic_read_ups_reserve_soc、logic_read_ups_reserve_flag、logic_read_allow_discharge_cut_soc、logic_read_peakshving_discharge_cut_soc、logic_read_gc_charger_cut_soc、logic_read_mppt_percentage、g_t_logic_info |
dispatch.h | g_t_dispatch_t.t_stsystemcmdinfo(u8ControlMode、u8ForceSOC)、CHARGE_CONTROL、FORCE_SOC_MULT |
generator.h | get_generator_running_flag、get_freq_dispatch_enable、get_inv_ups_reserve_soc_enable_default |
data.h / data.c | ems_set_fault_bit、g_t_config_save_flag |
ethernet.h | IP_Address.u8_mac_addr |
crc.h | GetCrcCode_Modbus |
modbus.h | g_t_modbus_config.u8_modbus_baud_rate、BAUD_RATE_9600/115200/256000 |
parallel_can.h / parallel_sci.h | parallel_can_comm_host_machine_handle、parallel_can_comm_from_machine_handle、parallel_sci_comm_host_machine_handle、parallel_sci_comm_from_machine_handle |
usart3.h / usart4.h | uart3_init、uart4_init |
upd_download.h / ems_download.h / bms_download.h / inv_download.h | Get_Status_Is_Updated_From_Server、GetPercentDownloadBMS、IsDownloadBMS、UPDT_EMS、UPDT_INV1/2/3、UPDT_BMS |
ems.h | ems_set_fault_bit 相关声明链 |
includes.h / chip.h | OSTimeGet、memset、abs、TRUE/FALSE、PHASE_L1/L2/L3/ALL、cNormalMode、UNIQUE_DEVICE_ID1/2/3、INVERTER_TYPE/SET_TYPE |
6. 依赖关系
6.1 依赖(本文件包含/使用的模块)
parallel.h、includes.h、parallel_can.h、parallel_sci.h、inverter.h、bms.h、upd_download.h、ems_download.h、bms_download.h、inv_download.h、ethernet.h、logic.h、dispatch.h、generator.h、data.h、usart3.h、usart4.h、crc.h、ems.h、modbus.h。
依赖方向:parallel.c → {inverter(本机实时数据 + 执行器)、bms(电池)、logic(系统级指令)、dispatch、generator、data(故障位)};parallel.c ← {parallel_can.c、parallel_sci.c}(通信层只读写结构体)。
6.2 被依赖
midware/inverter/inverter.c(任务调度task_inverter_can/task_parallel_sci+inverter_get_parallel_mode间接依赖本文件的parallel_read_config_machine_precept)midware/inverter/parallel_comm/parallel_can.c、parallel_comm/parallel_sci.c(读写本文件的 5 个全局结构体)midware/interface/interface.c(通信方案自适应、PV 充电器共存)midware/interface/modbus/modbus.c、midware/interface/modbus/sunspec_business.cmidware/auxiliary/system_aux.clogic/logic.c、logic/ac_control.c、logic/dispatch.cdata/resume_data/flash/flash_data_store.cnet/ems.c、net/server.c- 第三方逆变器通信层:
alphaess_comm_can.c、alphaess_comm_usart.c、ginlong_comm_can.c、ginlong_comm_usart.c、inovance_comm_can.c、inovance_comm_usart.c、goodwe_comm.c
7. 备注与风险点
- 积分型修正量永不重置(竞态级风险)。
s_s32_logic_ac_total_set_bat_charge_limit_power_extra/..._dischg_limit_power_extra(第 358-359 行)是static累加器,只有parallel_parallel_comm_init()里没有清零它们。场景:主机带 4 台机运行一段时间后从机掉线/配置变更(触发parallel_parallel_comm_init),累加器保留旧值,新配置下第一次分配就会带上一个来路不明的偏置,且该偏置会继续被累加修正、没有饱和/衰减逻辑(只在偏差 ≤2 时停止累加,但永不回退)。理论上偏差反向时会被拉回,但收敛速度由/2增益决定,中间过程可能甩出很大的设定值。
if (分母==0) set = 全值; else set = 全值/2;的判断被写反。出现在第 529-537、544-552、567-575、582-590、610-619、633-642 行共 6 处。当任一正常机的可充/可放容量为 0(例如 SOC=100% 或有台机 SOC=0)时,sum_*_capacity就被强制成 1(第 425-432 行的防除零),所以正常情况下永远走/2分支;只有当l_s32_sum_bat_max_charge/dischg为 0(所有机都被禁充/禁放)时才走"全值"分支——此时把未折半的全量指令直接下发,逻辑上是反的。这两条判据与注释"分母为 0 时……"不符,属于可疑写法。
- 权重体系不自洽:SOC 均衡用
PARALLEL_BAT_SOC_CTRL_RATE_SET/10 = 0.9(第 447、456 行),修正项却用硬编码/2(第 451、460 行)而不是PARALLEL_BAT_LIMT_CTRL_RATE_SET/10 = 0.1;DC 侧分配又整体/2(第 536、551 行)。宏PARALLEL_BAT_LIMT_CTRL_RATE_SET在全工程从未被引用。三套系数之间没有可解释的配比关系。
- 硬编码只支持 2 台机的功率搬移(第 752-771 行)。四个
if/else if只处理[0]和[1],且是else if互斥链:当第 0 台放电超限且第 1 台充电超限时,只有第一个分支会执行。PARALLEL_MAX_NUM明明是 16,这里却写死两机,说明该功能是为特定 2 机场景临时加的补丁。
u8_parallel_selection被无条件清零(第 274 行):g_t_inv_info.t_config_data.u8_parallel_selection = 0;每 30 ms 执行一次,把inverter.h:473注释的"0 单相 / 1 三相"配置项永久钉死为单相。若上层(菜单/云端)尝试设置为三相,会被这里静默改回,属于配置被覆盖的高危点。
parallel_from_machine_logic_handle无条件覆写[0](第 852-872 行):从机在尚未组网成功时也会把g_t_from_machine_running_info(初值全 0,或被memset清零)写进g_t_host_machine_running_info[0]的所有下发给定量,随后由parallel_read_*交给logic/与厂商通信层。虽然第 874 行的配置同步有u8_parallel_addr_en门禁,但功率指令的搬运没有。若从机在组网前被logic/读走这些 0 值,会表现为"并机从机在未联网时反复下发 0 功率"。
- 全局结构体无任何互斥保护。
task_inverter_can(30 ms,parallel_data_handle+parallel_can_comm_handle)、task_parallel_sci(100 ms,parallel_sci_comm_handle)、logic/任务、net/任务都直接读写g_t_host_machine_running_info[16]。该结构体每个元素 200+ 字节,通信层按字段逐个赋值(parallel_can.c:377-475、parallel_sci.c:478-530),中途被打断就会出现"半新半旧"的一帧数据被主机功率分配或logic/读到。没有临界区、没有信号量、没有双缓冲。
abs()用于无符号 tick 差值。文件中大量abs(OSTimeGet() - s_u32_send_tick) >= N(如第 169、197、206、259、1291、1617、1777 行)。OSTimeGet()返回INT32U,相减后传入abs():tick 溢出/回绕时差值可能落在高位,abs对负值的行为与预期不同(第 1781 行专门写了s_s32_self_test_tick > OSTimeGet()回绕保护,说明作者意识到这个问题,但只在这一个函数里处理了)。
parallel_sci_comm_handle每个周期都调用inverter_get_parallel_mode()3 次(第 1057、1059、1093 行),而该函数内部又会调parallel_read_config_machine_precept()。虽然是纯读,但 100 ms 任务里做 3 次判空调用属于可读性问题,也放大了与interface.c写u8_parallel_precept的竞争窗口。
- 故障位 20/21 的语义容易混淆:
ems_set_fault_bit(20, ...)表示"本机作为从机与主机失联",ems_set_fault_bit(21, ...)表示"本机作为主机有从机失联"。但parallel_data_handle里不论当前是主机、从机还是单机,两个位都会被写。单机模式下u8_parallel_en == FALSE,g_t_from_machine_networking_info保持初值(u32_rcv_tick = OSTimeGet()),15 s 后u8_parallel_lost_flag仍为 FALSE,所以位 20 恰好为假;但单机模式的[1..15]里如果残留了历史从机数据(u8_parallel_lost_flag在上次断电前被置真),位 21 会误报。parallel_parallel_comm_init只在三个配置字段变化时触发,而单机模式下parallel_local_info_handle第 266 行会把u8_parallel_mode改回 0 —— 这会与静态缓存的s_u8_parallel_mode比较,从而每 30 ms 触发一次parallel_parallel_comm_init()。这构成一个实际的每周期全量清零循环(因为第 274 行的u8_parallel_selection = 0和 266 行的u8_parallel_mode = 0会让缓存比较反复失配),代价是每 30 ms 一次约2×15×sizeof(结构体)的memset。
- 魔法数字:SOC 迟滞
+4/-4/-10(0.4%/0.4%/1%)、功率死区2(0.2 kW)、积分门限2、>= 4000的频率有效门槛(40.00 Hz)、150*1000(150 s)自检超时、15000(15 s)心跳超时、3500/500/50ms 发送周期、3600000tick 强制切换周期,均无宏定义。
parallel_read_system_inv_backup_freq不break(第 1535-1552 行):遍历时不断覆盖,最终取最后一个>= 4000的备电频率;而同族的parallel_read_system_inv_grid_freq(第 1508 行)取第一个并break。同一份数据两种取法,属不一致。
u8_work_mode == cNormalMode之外的分支会清空全部下发量(第 724-746 行),但对未组网的槽位(u8_parallel_addr_en == FALSE)不进入该分支,其g_t_host_machine_running_info[i]可能保留上一次的残值(parallel_can.c只在"丢失"时才memset,parallel_sci.c在"丢失"时同时清addr_en,两条路径不一致),这些残值会被parallel_read_*的遍历类函数统计进去吗?——不会,因为遍历一律带parallel_machine_isWorkInNormalMode(i)或u8_parallel_addr_en != FALSE判据。但第 434-441、443-510 行的l_s32_ac_set_bat_charge_limit_power_total累加用的是parallel_machine_isWorkInNormalMode(i)(要求cNormalMode),而[0]是本机,本机若不在cNormalMode则本机也不参与累加,此时l_s32_ac_set_bat_charge_limit_power_total会漏掉本机已有的设定值,导致第 449/458 行的偏差判据被误触发。