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

parallel.c

源文件路径: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_limitstatic逐机计算禁充/禁放限值与系统预留 SOC 强充标志
parallel_local_info_handlestatic采集本机(逆变器 + BMS)数据填入 g_t_host_machine_running_info[0]
parallel_parallel_comm_initstatic并机配置变化时清零各机档案与运行信息
parallel_host_machine_logic_handlestatic主机角色功率分配主逻辑
parallel_from_machine_logic_handlestatic从机角色:把主机指令写回本地配置与执行器
parallel_one_machine_logic_handlestatic单机角色直通

3.2 全部函数

函数名作用域说明
parallel_sys_soc_limitstatic,第 60 行依据顺序控制模式与 SOC 计算该机 s16_max_charge_power_limit / s16_max_dischg_power_limit 及 u8_sys_force_charge_flag
parallel_local_info_handlestatic,第 161 行生成本机设备 ID/MAC、采集逆变器 ACDC 全量运行数据、BMS 电池数据与升级标志,并做并机配置自愈
parallel_parallel_comm_initstatic,第 284 行清零从机槽位([1..15])、从机侧结构体与网络状态,并根据"是否依赖通信卡"预置首选通信方案
parallel_machine_isWorkInNormalMode全局(非 static),第 310 行判定第 i 台机是否"地址有效 + 未丢失 + 工作于 cNormalMode"
parallel_host_machine_logic_handlestatic,第 334 行主机功率分配核心:SOC 均衡 + 双权重分配 + 满功率优先 + 自发自用追零 + 油机限制 + 升级降额 + 两台机功率搬移
parallel_from_machine_logic_handlestatic,第 850 行从机:把 g_t_from_machine_running_info 转为本地设定值并写回逆变器/BMS/逻辑配置
parallel_one_machine_logic_handlestatic,第 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 行)的固定顺序:

  1. parallel_local_info_handle() —— 刷新本机数据到 [0];
  2. 配置变化检测:把 u8_parallel_en / u8_parallel_mode / u8_parallel_com_type_can 三个配置与静态缓存比较,任一变化即调 parallel_parallel_comm_init() 全量重置并机状态;
  3. 角色分发:
条件调用
u8_parallel_en == FALSEparallel_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. 遍历 [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]",分四类:

末尾有配置自愈(第 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 且该阈值 <10000(禁充)
TIME_FORCE_CHARGE_MODE 且 SOC >= charger_cut_soc*10 且阈值 <10000
DISPATCH_MODE 且调度 u8ControlMode == CHARGE_CONTROL 且 SOC > u8ForceSOC*FORCE_SOC_MULT + 40
其它min(s32_bat_max_charge_power, s16_inverter_rated_bat_charge_power)

(阈值 < 1000) 是在排除"未配置/1000(100%)"这类占位值,避免把未设置当成"已到顶"。

放电限值 s16_max_dischg_power_limit(第 103-116 行):以下任一成立即置 0:

否则 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 低、第二台 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 均衡(第 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 行)

对每台正常机:

注意这个 if 是写反的:分母为 0 时用全值、分母非 0 时用 /2,与注释和常规预期相反,且与阶段 4 的 /10 权重不成体系(见第 7 节)。

即"电池已不能再充、电表功率还没到自发自用阈值"时,直接按"电表功率 + 最大馈网功率 + 本机实际功率"给逆变器下指令(用实测电表值闭环补偿),并把放电限值清零以免叠加。

阶段 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 行)

分两步:

  1. 无条件把主机指令搬到 [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] 取值,从机侧也就自然返回主机下发的值。
  2. 仅当已组网成功(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]。附加约束与主机路径同构:

4.7 通信入口与 UART 重配置:parallel_can_comm_handle() / parallel_sci_comm_handle()

方案UART3(外部 485 / Modbus)UART4(并机总线)
PARA_COM_TYPE_USART31152009600
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_* 函数会修改全局状态,属于设计上的坑:

  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/ 在完成一轮功率计算后置 TRUEparallel_host_machine_logic_handle 第 647 行(自发自用追零门禁)并在第 750 行清 FALSE每轮调度
g_t_host_machine_running_info[0].u8_bat_soc_priority_en / u16_bat_soc_full_power_thresholdparallel_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_userparallel.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_flaglogic/、inverter.c自检使能跳变 / 150 s 超时
g_t_inv_info.t_config_data.u8_parallel_preceptparallel_set_config_machine_preceptinverter_get_parallel_mode、interface.c、system_aux.c方案固化
g_t_config_save_flag.u8_set_inverter_config_flagparallel_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。

另外还导出两个未在头文件声明的全局函数(供本文件内部使用,但理论上外部可见):

主要调用方:

调用方用到的接口
midware/inverter/inverter.c:1273/1276/1303parallel_can_comm_handle、parallel_data_handle、parallel_sci_comm_handle
midware/inverter/inverter.c:2957parallel_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-461parallel_read_system_max_charge_power_limit
logic/dispatch.c:610,632,1014parallel_read_system_inv_grid_freq、parallel_read_system_bat_average_soc
data/resume_data/flash/flash_data_store.c:775,778parallel_read_dc_total_set_inv_power、parallel_read_dc_total_set_bat_power
net/ems.c:621-622,793-794,823-838parallel_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,1134parallel_set_full_power_priority_enable、parallel_set_bat_soc_threshold
midware/interface/modbus/modbus.c:2851-2859,4481-4505parallel_read_ac_total_set_*、parallel_read_dc_total_set_*、parallel_read_dc_three_phase_set_inv_power
midware/interface/modbus/sunspec_business.c:1437parallel_read_system_pv_status
midware/interface/interface.c:241-313parallel_read_config_machine_precept、parallel_set_config_machine_precept、g_t_parallel_total_machine_network
midware/auxiliary/system_aux.c:2584-2586parallel_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_Tparallel_can.c、parallel_sci.c(extern 声明)
g_t_host_machine_running_info[16]PARALLEL_HOST_MACHINE_RUNNING_INFO_Tparallel_can.c、parallel_sci.c、net/ems.c、net/server.c
g_t_from_machine_networking_infoPARALLEL_FROM_MACHINE_NETWORKING_PARAMETER_Tparallel_can.c、parallel_sci.c
g_t_from_machine_running_infoPARALLEL_FROM_MACHINE_RUNNING_INFO_Tparallel_can.c、parallel_sci.c
g_t_parallel_total_machine_networkPARALLEL_TOTAL_MACHINE_NETWORKING_Tparallel.h 已 extern;interface.c、inverter.c

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

按 #include(第 13-32 行)分组:

来源模块使用的函数/变量
inverter.h / inverter.cinverter_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.cbms_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.clogic_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.hg_t_dispatch_t.t_stsystemcmdinfo(u8ControlMode、u8ForceSOC)、CHARGE_CONTROL、FORCE_SOC_MULT
generator.hget_generator_running_flag、get_freq_dispatch_enable、get_inv_ups_reserve_soc_enable_default
data.h / data.cems_set_fault_bit、g_t_config_save_flag
ethernet.hIP_Address.u8_mac_addr
crc.hGetCrcCode_Modbus
modbus.hg_t_modbus_config.u8_modbus_baud_rate、BAUD_RATE_9600/115200/256000
parallel_can.h / parallel_sci.hparallel_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.huart3_init、uart4_init
upd_download.h / ems_download.h / bms_download.h / inv_download.hGet_Status_Is_Updated_From_Server、GetPercentDownloadBMS、IsDownloadBMS、UPDT_EMS、UPDT_INV1/2/3、UPDT_BMS
ems.hems_set_fault_bit 相关声明链
includes.h / chip.hOSTimeGet、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 被依赖

7. 备注与风险点

  1. 积分型修正量永不重置(竞态级风险)。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 增益决定,中间过程可能甩出很大的设定值。
  1. 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 时……"不符,属于可疑写法。
  1. 权重体系不自洽: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 在全工程从未被引用。三套系数之间没有可解释的配比关系。
  1. 硬编码只支持 2 台机的功率搬移(第 752-771 行)。四个 if/else if 只处理 [0] 和 [1],且是 else if 互斥链:当第 0 台放电超限且第 1 台充电超限时,只有第一个分支会执行。PARALLEL_MAX_NUM 明明是 16,这里却写死两机,说明该功能是为特定 2 机场景临时加的补丁。
  1. u8_parallel_selection 被无条件清零(第 274 行):g_t_inv_info.t_config_data.u8_parallel_selection = 0; 每 30 ms 执行一次,把 inverter.h:473 注释的"0 单相 / 1 三相"配置项永久钉死为单相。若上层(菜单/云端)尝试设置为三相,会被这里静默改回,属于配置被覆盖的高危点。
  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 功率"。
  1. 全局结构体无任何互斥保护。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/ 读到。没有临界区、没有信号量、没有双缓冲。
  1. 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() 回绕保护,说明作者意识到这个问题,但只在这一个函数里处理了)。
  1. 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 的竞争窗口。
  1. 故障位 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。
  1. 魔法数字: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/50 ms 发送周期、3600000 tick 强制切换周期,均无宏定义。
  1. parallel_read_system_inv_backup_freq 不 break(第 1535-1552 行):遍历时不断覆盖,最终取最后一个 >= 4000 的备电频率;而同族的 parallel_read_system_inv_grid_freq(第 1508 行)取第一个并 break。同一份数据两种取法,属不一致。
  1. 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 行的偏差判据被误触发。