dc_control.c
dc_control_total_power_self_use_calculate,183)4.3 DC 三相不平衡自发自用主算法(dc_control_three_phase_power_self_use_calculate,892)4.4 强充 / 强放 / 电池功率控制 / 无电池 / 油机4.5 限幅函数4.6 削峰填谷4.7 通信协议帧 / 超时重试4.8 与 uCOS-II 任务的配合4.9 关键全局变量的读写与触发条件5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
logic/dc_control.c所属模块:logic(能量调度核心) 代码行数:2033
1. 文件职责
本文件实现 EMS 的 DC 侧(直流耦合机 / 储能一体机)功率调度计算:把当前电表功率、PV 功率、电池实时功率、逆变器各相输出与电池允许充放电上限,换算成「逆变器 DC 侧有功设定」「电池功率设定」「并网点功率设定」,写入全局结构 g_t_logic_info。它处在数据流的计算末端,上游由 logic/logic.c:2902 的 task_system_logic_control 任务经 logic_charge_dischg_power_limit() / self_use_funtion() / bat_force_charge() / bat_force_discharge() / bat_power_control() / time_peakshaving_command() 等中间层函数驱动,下游由 logic/logic.c 的 logic_read_dc_total_set_*() 访问器与 midware/inverter/parallel.c(并机 SCI 下发)取走结果。文件分两条独立链路:DC 总功率控制与 DC 三相不平衡控制,另含油机充电模式与削峰填谷。除第 19 行的全局变量外,全部代码被 #ifdef INV_DC_CTRL(第 31 行 ~ 2031 行)包裹。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
g_u8_dc_disturbance_count | 全局变量(u8,dc_control.c:19) | DC 扰动计数,0~10 饱和。定义在 #ifdef 之外,因此任何配置下都参与编译;被 midware/inverter/inverter.c:41 以 extern 引用,用于生成「多次限制无效」的位掩码判据(inverter.c:2755-2762) |
INV_DC_CTRL | 条件编译宏 | 来自 midware/inverter/inverter.h:176,未定义时第 31~2031 行全部不参与编译 |
LOGIC_CONTROL_SPECIAL_HANDLE_T | 结构体(logic/logic.h:240) | 按逆变器型号整定的调节步长表,字段:u8_speed_para1、u8_speed_para2、u8_speed_test_11、u8_speed_test_13、u8_speed_test_3112、u8_speed_test_3121_1、u8_speed_test_3121_2、u8_speed_min_der_ac_power |
para_tables[] | 全局数组(logic/logic.c:52,长度 LOGIC_CTROL_SPECIAL_MAX_COUNT) | 索引为 g_t_logic_info.t_logic_control_parameter.u8_inv_type;下标 0 是通用值(speed_para1/2 = 0、test 值 50/50/30/30/30),下标 1 是 SNJ_M5 特殊值(speed_para1=1、test 值 30/30/5/2/5);u8_inv_type 由 inverter_get_special_control_inv_type()(midware/inverter/inverter.c:7985)决定,返回值只有 NOT_SPECIAL_HANDLE(0) 或 SNJ_M5_SPECIAL_HANDLE(1) |
LOGIC_CTROL_SPECIAL_MAX_COUNT | 宏(= 2,logic/logic.h:32) | para_tables[] 的长度 |
LOGIC_TABLE_T | 结构体(logic/logic.h:494) | 全局 g_t_logic_info 的类型;本文件写它的 s32_dc_total_set_bat_power / s32_dc_total_set_inv_power / s32_dc_total_set_grid_power(总功率侧)与 s32_dc_three_phase_set_bat_power / s32_dc_three_phase_set_inv_a/b/c_power(三相不平衡侧) |
LOGIC_CONTROL_PARAMETER_T | 结构体(logic/logic.h:138,#pragma pack(1)) | 全部输入源:s32_pgrid_metert/a/b/c、s32_pinvt/a/b/c、s32_pbat_inv、s16_ibat_inv、s32_bat_total_power、s32_ppv_inv、s32_pbackupt、u8_pv_status、u8_bat_status、u8_regulator_speed、u8_inv_phase_num、s32_max_feedin_power、s32_bat_charge_power_limit、s32_bat_dischg_power_limit、s32_inv_rated_output_power、s32_inv_power_fine_tuning_range、s32_bat_power_fine_tuning_range、s32_system_self_consumption、s32_system_control_accuracy、u8_disturbance_flag、u16_dc_disturbance_count、5 个 u32_calculation_*_tick |
PEAKSHVING_STATE_E | 枚举(logic/logic.h:99) | 削峰填谷状态:INITIAL_IDLE=0、NO_INVERTER_POWER_STATE(=1)、POWER_REGULATION_STATE(=2) |
LOGIC_CONFIG_TAB_T | 结构体(g_t_logic_info.t_config_data) | 本文件用到 GCRatedPower、GCRatePercent、GCOutputMode、GCChargePower |
GC_CHARGE | 宏(= 1,logic/logic.h:27) | 油机输出模式「充电功率模式」判据 |
FORCE_BAT_FEEDIN_MODE | 枚举成员(logic/logic.h:56) | u8_sequencecontrol_mode 的一种,强制电池馈网;本文件用它决定 max feedin 是否包含电池充电功率 |
PV_METER / PHASE_T | 枚举成员(midware/meter/meter.h:166、177) | 仅在 DEBUG_LOGIC 打印段里使用 |
DEBUG_LOGIC | 宏(= 0,logic/logic.h:17) | 调试打印开关 |
3. 函数清单
| 行号 | 函数名 | 作用域 | 说明(一句话) | ||
|---|---|---|---|---|---|
| 39 | dc_control_total_power_limit | 全局 | DC 总功率限幅:电池功率按符号钳到 ±允许充放电上限,逆变/并网设定钳到 [0, 逆变额定输出] | ||
| 83 | dc_control_total_power_bat_force_charge | 全局 | DC 强充:电池设定 = −充电上限,逆变设定按 pgrid_metert + max_feedin + pinvt 给(上限截额定、下限截 0),并网设定 = 逆变设定 − 后备功率 | ||
| 120 | dc_control_total_power_bat_force_discharge | 全局 | DC 强放:电池设定 = 放电上限(或逆变额定),逆变/并网设定同上 | ||
| 170 | dc_control_total_power_bat_power_control | 全局 | DC 电池功率控制:逆变设定 = −s16_bat_control_power,并网设定 = 逆变设定 − 后备功率 | ||
| 183 | dc_control_total_power_self_use_calculate | 全局 | DC 自发自用主算法:按电表三区间(馈网 / 买电 / 稳定)调节电池功率,含扰动计数与「多次无效即加速回退」机制 | ||
| 479 | dc_control_total_power_no_bat_calculate | 全局 | DC 无电池:电池设定置 0,` | max_feedin + pgrid_metert | > 50` 时按电表修正逆变与并网设定 |
| 504 | dc_control_total_power_gc_chgpower_calculate | 全局 | DC 油机充电模式:在「油机可用功率 − 后备功率」的额度内调节电池充电,输出逆变设定恒为 0 | ||
| 750 | dc_control_three_phase_power_limit | 全局 | DC 三相不均衡限幅:电池功率按符号钳位,三相逆变设定统一钳到 [10, 逆变额定/相数] | ||
| 818 | dc_control_three_phase_power_bat_force_charge | 全局 | DC 三相强充:电池设定 = −充电上限,三相设定 = 逆变额定/相数 | ||
| 839 | dc_control_three_phase_power_bat_force_discharge | 全局 | DC 三相强放:电池设定 = 放电上限;get_maxfeedin_priority_flag() 为真时三相按「各相电表 + maxfeedin/相数 + 各相逆变功率」给,否则给满 | ||
| 877 | dc_control_three_phase_power_bat_power_control | 全局 | DC 三相电池功率控制:电池设定 = −s16_bat_control_power,三相设定 = ` | s16_inv_control_power | / 相数` |
| 892 | dc_control_three_phase_power_self_use_calculate | 全局 | DC 三相不均衡自发自用主算法(本文件最大函数,1832−892 = 940 行):A/B/C 三相各跑一套相同的扰动状态逻辑,最后统一校准与限幅 | ||
| 1832 | dc_control_three_phase_power_no_bat_calculate | 全局 | DC 三相无电池:三相设定按 各相电表 + maxfeedin/3 + 各相逆变功率 给(` | 和 | > 50` 时),电池设定置 0 |
| 1869 | dc_time_valley_fill_calculate | 全局 | DC 填谷:以 s32_set_valley_value 为目标做三状态滞环,按 error / u8_regulator_speed 斜坡调整电池功率并限幅 | ||
| 1951 | dc_ctrl_time_valley_fill_command | 全局 | DC 填谷命令入口:dc_time_valley_fill_calculate() + dc_control_total_power_limit() | ||
| 1963 | dc_time_peak_clip_calculate | 全局 | DC 削峰:以 s32_set_peak_value 为上限、s32_set_delta_value 为回差给出电池与逆变设定 | ||
| 1987 | dc_ctrl_time_peak_clip_command | 全局 | DC 削峰命令入口:带 PEAKSHVING_STATE_E 双状态滞环,先限幅再计算,并做「已降到峰值以下」退出判断 |
共 17 个函数定义,与 logic/dc_control.h 的 17 条原型一一对应;其中 static 函数 0 个——所有内部实现都被写成全局可见。
4. 核心逻辑解析
4.1 节拍闸门与时间戳共享
除 dc_control_total_power_bat_power_control()(170)与 dc_time_peak_clip_calculate()(1963)外,所有计算函数都带同一段闸门:
if (abs(OSTimeGet() - g_t_logic_info.t_logic_control_parameter.u32_calculation_XXX_tick)
< g_t_logic_info.t_logic_control_parameter.u32_calculation_interval)
return;
g_t_logic_info.t_logic_control_parameter.u32_calculation_XXX_tick = OSTimeGet();
时间戳只有两个:
u32_calculation_dc_total_inv_tick:被 83、120、183、479、1987 共用;u32_calculation_dc_three_phase_inv_tick:被 818、839、892、1832 共用;- 另有
u32_calculation_dc_total_bat_tick作电池侧专用节拍(u32_calculation_interval_bat),仅在 183 与 504 内部用于「电池功率已跟上」的判决。
因此同一轮内先执行的函数会消耗该 tick,后续同链路的函数直接 return。logic/logic.c:2916-2920 在快调度事件 POST_PEND_FAST_ENABLE 到来时会把 u32_calculation_dc_total_inv_tick / u32_calculation_dc_total_bat_tick / u32_calculation_dc_three_phase_inv_tick 三者清 0,强制本轮立即计算。
dc_control_total_power_gc_chgpower_calculate()(504)的闸门被注释掉(dc_control.c:515-520),因此该函数每次被调用都完整执行一遍。
4.2 DC 自发自用主算法(dc_control_total_power_self_use_calculate,183)
这是本文件最核心的算法,输入取局部快照后按电表功率分三个区间。
前置计算
dc_ctrol_para_tables = ¶_tables[g_t_logic_info.t_logic_control_parameter.u8_inv_type];
// max feedin 的等效值(含电池充电功率)
if (u8_sequencecontrol_mode == FORCE_BAT_FEEDIN_MODE)
l_s32_max_feed_power = s32_max_feedin_power; // 强制电池馈网时不再叠加
else
l_s32_max_feed_power = s32_max_feedin_power
+ (bat_charge_limit + bat_total_power
- (bat_total_power < 0 ? para->u8_speed_para1 : para->u8_speed_para2)
* abs(bat_total_power / 60)); // 按电池功率大小做速率补偿
区间一:馈网(l_s32_meter_power_t < -50),前置 l_u8_dc_disturbance_flag = FALSE,再分三支:
bat_charge_power_limit == 0(电池已满):若|max_feed + meter| > para->u8_speed_test_11,把逆变设定直接设为meter + max_feed + pinvt以限制馈网;电池设定:bat_total_power > 60时取(0 − bat_total_power),否则固定 15W,且bms_read_relay_status() == 1时改为 50W。set_bat_power > -bat_charge_power_limit(电池还能充,优先充电):- 若
|set_bat − pbat_inv| < 30(已跟上)或距上次u32_calculation_dc_total_bat_tick超过u32_calculation_interval_bat:刷新 tick;再按g_u8_dc_disturbance_count是否非零分流 —— 非零则set_bat -= count * 100(扰动加速回退),为零则set_bat += meter / speed斜坡逼近;同时置l_u8_dc_disturbance_flag = TRUE。 - 否则(未跟上):仅在
u8_disturbance_flag == FALSE时刷新 tick 并set_bat += meter / speed。
- 若
- 其余(电池已达最大充电功率,需限制 PV):若
|max_feed + meter| > para->u8_speed_test_13,set_inv = meter + max_feed + pinvt。
区间二:买电(meter > s32_system_self_consumption):先无条件 set_inv = meter + max_feed + pinvt(完全放开 PV),再按「已跟上或电池节拍超时」分支:
- 有 PV:若
|pinvt| > (逆变额定 − s32_inv_power_fine_tuning_range)且pinvt > 0(逆变已接近满载)→ 若set_bat > 0则置扰动标志并按count*100或固定 30W 回退,否则set_bat -= 30;否则若|pinvt| > (逆变额定 − 500)→set_bat += (额定 − pinvt) / speed;否则set_bat += meter / speed。 - 无 PV:
set_bat += meter / speed。 - 未跟上时,仅在
u8_disturbance_flag == FALSE时set_bat += meter / speed。
区间三:稳定区(−50 ≤ meter ≤ 自耗):set_inv = meter + max_feed + pinvt,然后
- 有 PV:
bat_charge_power_limit == 0时——bat_total_power < 50→set_bat += (0 − bat_total_power) / 3,否则set_bat -= para->u8_speed_test_3112;charge_limit != 0且u8_disturbance_flag == TRUE时 →set_bat -= (set_bat < 0 && set_bat > -150) ? para->u8_speed_test_3121_1 : para->u8_speed_test_3121_2。 - 无 PV:
|bat_total_power| < 50→set_bat = 0;否则按s32_bat_power_fine_tuning_range做 ±5W 微调。
收尾
if (l_u8_dc_disturbance_flag == TRUE) g_u8_dc_disturbance_count++ 且饱和到 10;
else g_u8_dc_disturbance_count = 0;
if (|set_bat_power| < s32_system_control_accuracy) set_bat_power = 0; // 死区
if (set_inv_power < 0) set_inv_power = 0;
g_t_logic_info.s32_dc_total_set_bat_power = set_bat_power;
g_t_logic_info.s32_dc_total_set_inv_power = set_inv_power;
g_t_logic_info.s32_dc_total_set_grid_power = set_inv_power - s32_pbackupt;
4.3 DC 三相不平衡自发自用主算法(dc_control_three_phase_power_self_use_calculate,892)
A/B/C 三相各有一整块结构完全相同的代码(A 相 982-1238、B 相 1240-1496、C 相 1498-1755),只是把变量后缀换成 a/b/c,并用 l_u32_test[0/1/2] 记录走到哪一步(调试用)。三相之间的耦合通过「其余两相」的比较实现。
公共前置量
l_s32_max_feed_power = s32_max_feedin_power;
l_s32_single_phase_max_feed_power = s32_max_feedin_power / 3;
l_s32_inv_single_phase_rated_output_power = s32_inv_rated_output_power / 3;
l_s32_single_ac_max_power = 单相额定 + s32_inv_power_fine_tuning_range;
l_s32_load_power_X = max(pinvX + pgrid_meterX, 0),并钳到单相额定;
l_s32_ctrl_inv_X_phase_power = g_t_logic_info.s32_dc_three_phase_set_inv_X_power; // 以当前设定为起点
l_s32_ctrl_bat_power = g_t_logic_info.s32_dc_three_phase_set_bat_power;
每相的三分支
meter_X < -50(该相在馈网)- 若
bat_charge_power_limit == 0:single_phase_max_feed + meter_X < −50时,s_u8_wait_maxfeedin_tick[X] > 5就按(meter_X + single_max)/speed微调该相设定,否则把该相设定直接钉到load_X + single_max;未超 max feedin 时再看其余两相:若其余两相都 >single_ac_max且|inv_X + ctrl_X| < 200则ctrl_X += |meter_X|/speed;否则若其余两相都满足各自负载且总表未超 max feedin 则ctrl_X += 100,若其余两相有欠载且未达上限则ctrl_X += meter_X / speed,若其余两相设定与实际偏差 > 60W 则维持现状,否则s_u8_wait_pv_tick[X]++:有 PV 且该 tick > 10 时ctrl_X += |meter_X|/speed(或若ctrl_X − inv_X > 100则维持),无 PV 时ctrl_X -= 10。最后该相分支把电池设定强制为 0(l_s32_ctrl_bat_power = 0)。 - 若电池还能充:
wait_maxfeedin_tick[X] = 0;有 PV 则wait_pv_tick[X]++;当bat_charge_power_limit + bat_realtime_power > 100(仍有充电余量)时ctrl_bat += meter_X / speed,且若ctrl_X > 100则同时ctrl_X += meter_X / speed并清wait_pv_tick[X];若其余两相有欠载且未达上限则ctrl_X += meter_X / 2;若|meter_t + max_feed| < 50清wait_pv_tick[X],若meter_X + single_max < 0则ctrl_X += (meter_X + single_max)/speed;wait_pv_tick[X] > 10时清 0 并ctrl_X += |meter_X|/speed。
- 若
meter_X > 50(该相在买电):若inv_X < single_ac_max,有 PV 则wait_dc_tick[X]++、ctrl_X += meter_X/speed,且wait_dc_tick[X] > 10时ctrl_bat += meter_X/speed;无 PV 则ctrl_bat += meter_X/speed、ctrl_X += meter_X/speed并清wait_dc_tick[X]。- 稳定区:有 PV 时——
bat_charge_power_limit != 0 && meter_X < 0则ctrl_bat -= 10;max_feed > 100时wait_pv_tick[X]++,> 3时清 0 并ctrl_X += 10;若|bat_realtime − ctrl_bat| > 200则该偏差/5加到ctrl_bat(正负号按大小关系)。无 PV 时——meter_X < -10 && bat_realtime > 0则ctrl_bat -= 5、ctrl_X -= 5。
统一收尾(1757-1808)
if (三相 |meter| 都 < 50) // 三相都进入逻辑平衡
if (|bat_realtime - ctrl_bat| > 200) 按偏差/5 校准 ctrl_bat;
if (ctrl_bat > -50 && ctrl_bat < 0) ctrl_bat = 0; // 充电死区
三相各钳到 [10, 单相额定]; // 注意:最小 10W(不是 0)
写回 g_t_logic_info.s32_dc_three_phase_set_inv_a/b/c_power 与 s32_dc_three_phase_set_bat_power;
4.4 强充 / 强放 / 电池功率控制 / 无电池 / 油机
- 总功率侧强充(83):
set_bat = -bat_charge_power_limit;l = pgrid_metert + max_feedin + pinvt,l > 额定取额定、l > 0取l、否则 0;grid = inv − pbackupt。 - 总功率侧强放(120):
(bat_dischg_limit + ppv_inv) > 逆变额定时set_bat = 逆变额定,否则set_bat = bat_dischg_power_limit;逆变设定和并网设定与强充相同。 - 总功率侧电池功率控制(170):
g_t_logic_info.s32_dc_total_set_inv_power = abs(g_t_logic_info.s16_inv_control_power); // ← 立即被下一行覆盖 g_t_logic_info.s32_dc_total_set_inv_power = -g_t_logic_info.s16_bat_control_power; g_t_logic_info.s32_dc_total_set_grid_power = 逆变设定 - s32_pbackupt;
注意这里没有写 s32_dc_total_set_bat_power,与 AC 侧同名函数的行为不一致(AC 侧会同时给出充放电限值)。
- 总功率侧无电池(479):
set_bat = 0;|max_feedin + pgrid_metert| > 50时set_inv = pgrid_metert + max_feedin + pinvt、grid = inv − pbackupt。 - 油机(504):
可用功率 = GCRatedPower * GCRatePercent / 100 − s32_pbackupt;GCOutputMode == GC_CHARGE时与GCChargePower取小;把l_s32_grid_actpower = -(pinvt + 可用功率)当作等效电表功率,然后复用与 4.2 同构的三区间逻辑(同样的扰动计数、同样的 ±50W 划分),只是计数用的是函数内 static 的s_u8_dc_disturbance_count(513)而不是文件全局变量。最后:若可用功率 >= 0 && set_bat <= 0 && |set_bat| > (可用功率 + ppv_inv)则钳到-(可用功率 + ppv_inv);set_inv = 0;grid = inv − pbackupt;-18 < set_bat < 18归零;set_bat > 0也归零(只允许充电)。 - 三相侧强充(818):电池 =
−充电上限,三相 =逆变额定 / u8_inv_phase_num。 - 三相侧强放(839):电池 =
放电上限;get_maxfeedin_priority_flag() != TRUE时三相给满(额定/相数),否则三相 =各相电表 + max_feedin/相数 + 各相逆变功率。 - 三相侧电池功率控制(877):电池 =
−s16_bat_control_power,三相 =|s16_inv_control_power| / 相数。 - 三相侧无电池(1832):
single_max = max_feedin / 3;对每相,若|single_max + meter_X| > 50则set_inv_X = meter_X + single_max + pinv_X;电池 = 0。
4.5 限幅函数
dc_control_total_power_limit()(39):set_bat < 0→|set_bat| > 充电上限时取−充电上限;set_bat >= 0→|set_bat| > 放电上限时取+放电上限。set_inv与set_grid各自钳到[0, s32_inv_rated_output_power]。dc_control_three_phase_power_limit()(750):电池功率同上;三相设定统一钳到[10, s32_inv_rated_output_power / u8_inv_phase_num](最小 10W,且else if (< 0) → 0分支不可达,因为前一个if (< 10) → 10已覆盖所有负值)。
4.6 削峰填谷
填谷 dc_time_valley_fill_calculate()(1869) 是三状态滞环:
state 0: pgrid > valley + delta → 1; pgrid >= valley → 2
state 1: pgrid < valley → 0; pgrid <= valley + delta → 2
state 2: pgrid > valley + delta → 1; pgrid < valley → 0
state 0(已在谷上方过冲): error = pgrid - (valley + delta); // 负
state 1(已在谷内过高) : error = pgrid - valley; // 正
state 2(已达标) : 不改动
last_bat_power += error / speed;
然后按符号钳到 ±充放电上限,写入 g_t_logic_info.s32_dc_total_set_bat_power,并把 s32_dc_total_set_inv_power 固定设为 5(W)。static s32 s32_last_bat_power 在声明处初始化为 0(dc_control.c:1872),而代码注释写的是「需在初始化时赋值」,二者不一致。
填谷入口 dc_ctrl_time_valley_fill_command()(1951):dc_time_valley_fill_calculate() → dc_control_total_power_limit()。
削峰 dc_time_peak_clip_calculate()(1963):
if (pgrid >= peak) {
set_bat = (pinvt + |pgrid - peak|) * 1.1;
set_inv = set_bat;
} else if (pgrid <= peak - delta) {
set_bat = pinvt - |pgrid - peak| / speed;
set_inv = set_bat;
}
削峰入口 dc_ctrl_time_peak_clip_command()(1987) 结构与 AC 侧同构:
static s8 status = INITIAL_IDLE, old_status = INITIAL_IDLE;
闸门 → dc_control_total_power_limit(); // 先限幅
pgrid < peak 且 old_status != POWER_REGULATION_STATE → NO_INVERTER_POWER_STATE
否则 → POWER_REGULATION_STATE
NO_INVERTER_POWER_STATE : set_inv = 0
POWER_REGULATION_STATE : old_status = POWER_REGULATION_STATE; dc_time_peak_clip_calculate();
若 (pinvt + pgrid) < peak → old_status = NO_INVERTER_POWER_STATE, set_inv = 0, set_bat = 0
注意:退出判据用的是 s32_pinvt + s32_pgrid_metert(实际量),而 AC 侧 ac_ctrl_time_peak_clip_command() 用的是 s32_ac_total_set_inv_power + s32_pgrid_metert(设定量),两侧判据不同源。
4.7 通信协议帧 / 超时重试
本文件不实现任何通信协议:不组帧、不做校验(CRC/和校验/奇偶)、不重试、不直接收发 UART/CAN/以太网/SCI。它只消费已由 midware/meter、midware/bms、midware/inverter 解析好的实时量,并产出三个/六个功率设定值。唯一的「超时」概念是 4.1 里的计算节拍超时(u32_calculation_interval / u32_calculation_interval_bat),作用是「电池功率若长时间没跟上设定值就放开扰动计数」,不是通信重试。
4.8 与 uCOS-II 任务的配合
- 本文件不使用任何 uCOS-II API(无信号量
OSSemPend、无邮箱OSMboxPend、无任务创建),唯一触及的是OSTimeGet(),用于 4.1 的节拍闸门。 - 驱动方为
logic/logic.c:2902的task_system_logic_control:循环体OSTimeDlyHMSM(0, 0, 0, 280)(约 280ms 一拍);先OSFlagPend(g_t_fast_dispatch_flag, POST_PEND_FAST_ENABLE, OS_FLAG_WAIT_SET_ALL, 20, &err)等快调度事件,命中则把 5 个 tick 字段清 0;随后按inverter_get_sys_ctrl_state()、u8_grid_status走ongrid_logic_handle()/other_logic_handle(),这两层再按u8_sequencecontrol_mode分派到self_use_funtion()(logic.c:1823)、bat_force_charge()(logic.c:1761)、bat_force_discharge()(logic.c:1782)、bat_power_control()(logic.c:1803)、time_peakshaving_command()(logic.c:550)、油机模式等,最终落到本文件各入口;结束时OSFlagPost(..., POST_LOGIC_CALCULATE_DONE, ...)并OSTimeDlyResume(TASK_INVERTER_PRIO)唤醒逆变器任务。 - 并发安全:本文件写入的
g_t_logic_info字段是无锁普通全局变量。同一任务内顺序执行,读写不冲突;但与logic/logic.c:2668的dnsp_ramp_dc_control_handle()(会把s32_dc_total_set_*_power再覆写成渐进值)、midware/inverter/parallel.c的读取(logic_read_dc_total_set_*)之间没有互斥,靠任务优先级与节拍隐式串行。
4.9 关键全局变量的读写与触发条件
| 变量 | 读/写 | 触发条件与含义 |
|---|---|---|
g_t_logic_info.s32_dc_total_set_bat_power | 读+写 | 电池功率设定(负为充电、正为放电,由 dc_control_total_power_limit() 的钳位方向与 ..._bat_force_charge 赋值 −充电上限 可判定)。被 39/83/120/479/504/1869/1963/1987 改写 |
g_t_logic_info.s32_dc_total_set_inv_power | 读+写 | 逆变器 DC 侧有功设定,恒 ≥ 0(多处显式下限截 0) |
g_t_logic_info.s32_dc_total_set_grid_power | 读+写 | 并网点设定 = 逆变设定 − s32_pbackupt,每次写入都随 s32_dc_total_set_inv_power 一起刷新(83/120/170/183/479/504) |
g_t_logic_info.s32_dc_three_phase_set_bat_power | 读+写 | 三相不平衡电池设定,被 750/818/839/877/892/1832 改写 |
g_t_logic_info.s32_dc_three_phase_set_inv_a/b/c_power | 读+写 | 三相各相有功设定;892 以当前值为起点做增量迭代,最后钳到 [10, 单相额定] |
g_u8_dc_disturbance_count | 读+写 | 每拍按 l_u8_dc_disturbance_flag 自增(饱和 10)或清 0;!= 0 时电池功率按 count * 100 加速回退。被 midware/inverter/inverter.c:2755-2762 读取生成位掩码 |
g_t_logic_info.t_logic_control_parameter.u16_dc_disturbance_count | 只读 | 与 g_u8_dc_disturbance_count 同名不同物:前者来自配置/参数(logic.h:237),本文件 281/336 行用其做回退量,但它在本文件内从未被写入 |
g_t_logic_info.t_logic_control_parameter.u32_calculation_dc_total_inv_tick / ..._dc_total_bat_tick / ..._dc_three_phase_inv_tick | 读+写 | 节拍时间戳,见 4.1 |
g_t_logic_info.t_logic_control_parameter.u32_calculation_dc_total_bat_tick | 读+写 | 电池侧节拍,与 u32_calculation_interval_bat 配合判定「设置值末跟上实际值」 |
g_t_logic_info.s16_bat_control_power / s16_inv_control_power | 只读 | 上位指令的下发功率,仅在 170/877 使用 |
5. 对外接口
导出的函数与全局变量(供谁调用):
| 导出符号 | 调用方 |
|---|---|
g_u8_dc_disturbance_count(u8 全局变量) | midware/inverter/inverter.c:41(extern)、inverter.c:2755、inverter.c:2757、inverter.c:2762 |
dc_control_total_power_limit() | logic/logic.c:1745、logic/dc_control.c:1954、logic/dc_control.c:1998 |
dc_control_total_power_bat_force_charge() | logic/logic.c:1764 |
dc_control_total_power_bat_force_discharge() | logic/logic.c:1785 |
dc_control_total_power_bat_power_control() | logic/logic.c:1806 |
dc_control_total_power_self_use_calculate() | logic/logic.c:1857 |
dc_control_total_power_no_bat_calculate() | logic/logic.c:2026 |
dc_control_total_power_gc_chgpower_calculate() | logic/logic.c:2100 |
dc_control_three_phase_power_limit() | logic/logic.c:1746 |
dc_control_three_phase_power_bat_force_charge() | logic/logic.c:1765 |
dc_control_three_phase_power_bat_force_discharge() | logic/logic.c:1786 |
dc_control_three_phase_power_bat_power_control() | logic/logic.c:1807 |
dc_control_three_phase_power_self_use_calculate() | logic/logic.c:1858 |
dc_control_three_phase_power_no_bat_calculate() | logic/logic.c:2027 |
dc_ctrl_time_peak_clip_command() | logic/logic.c:558 |
dc_ctrl_time_valley_fill_command() | logic/logic.c:567 |
dc_time_valley_fill_calculate() | 无外部调用(仅 dc_control.c:1953) |
dc_time_peak_clip_calculate() | 无外部调用(仅 dc_control.c:2016) |
本文件不导出结构体/枚举(全部来自 logic/logic.h)。
本文件调用的外部函数/变量:
| 名称 | 来源模块 |
|---|---|
g_t_logic_info / para_tables[] | logic/logic.h:646-647,定义于 logic/logic.c:36、logic/logic.c:52 |
bms_read_relay_status() | midware/bms/bms.h:1185(dc_control.c:263,仅用于电池满后 15W/50W 判别) |
get_maxfeedin_priority_flag() | logic/logic.h:708(dc_control.c:849) |
meter_read_actpower(PV_METER, PHASE_T) | midware/meter/meter.h:471(仅 dc_control.c:1868,计算 l_s32_pv_power_t) |
inverter_read_pv_volt() / inverter_read_bus_volt() / inverter_read_bat_volt() / inverter_read_grid_actpower() | midware/inverter/inverter.h(仅 DEBUG_LOGIC 打印段 dc_control.c:733-734) |
OSTimeGet() / abs() / printf() | uCOS-II / C 标准库 / 调试串口 |
6. 依赖关系
依赖(include 与使用时):
- 直接 include(
dc_control.c:12-14):dc_control.h、logic.h、bms.h。logic.h提供LOGIC_TABLE_T/LOGIC_CONTROL_PARAMETER_T/LOGIC_CONTROL_SPECIAL_HANDLE_T/PEAKSHVING_STATE_E/para_tables/g_t_logic_info/LOGIC_CTROL_SPECIAL_MAX_COUNT/GC_CHARGE/DEBUG_LOGIC/get_maxfeedin_priority_flag。bms.h提供bms_read_relay_status()。
- 通过
logic.h的包含链间接获得:chip.h(基础类型、uCOS-II)、inverter.h(INV_DC_CTRL、型号宏)、system_config.h等。 - 编译开关依赖:
INV_DC_CTRL(midware/inverter/inverter.h:176)必须定义,否则第 31~2031 行不编译;第 19 行的g_u8_dc_disturbance_count不受该开关影响,始终编译。
被依赖(谁 include / 调用它):
logic/logic.c(include + 14 处调用)logic/dc_control.h的间接使用者:无其他模块 includedc_control.h
7. 备注与风险点
- 当前构建配置下第 31~2031 行全部不参与编译:
INVERTER_TYPE硬编码为INVERTER_NULL(midware/inverter/inverter.h:172),INV_DC_CTRL未定义;Keil 工程user/EMS_3.0.uvprojx的<Define>只有USE_STDPERIPH_DRIVER, GD32F407。该文件只在「DC 控机型」配置下生效,静态审查时容易误判其有效性;唯一在任何配置下都有效的是第 19 行的g_u8_dc_disturbance_count。
dc_control_total_power_bat_power_control()存在死存储(dc_control.c:172-173):g_t_logic_info.s32_dc_total_set_inv_power = abs(g_t_logic_info.s16_inv_control_power); g_t_logic_info.s32_dc_total_set_inv_power = -g_t_logic_info.s16_bat_control_power; // 立即覆盖
第一行是残留代码,无任何效果。此外本函数既不写 s32_dc_total_set_bat_power 也不做符号判据,与 AC 侧 ac_control_total_power_bat_power_control() 的行为(区分充放电、给限值)明显不对称。
dc_control_total_power_limit()把并网设定钳到非负(dc_control.c:64-74):s32_dc_total_set_grid_power = 逆变设定 − s32_pbackupt,当后备功率大于逆变设定(常见于夜间备电)时该值为负,但限幅函数会把它钳到 0,语义上等于「不允许反向并网」。若后续模块依赖该字段表达取电方向,会丢失信息。
- 三相不平衡的 A 相与 B/C 相判据不一致(
dc_control.c:992vs1250vs1508):A 相:if (l_s32_single_phase_max_feed_power + l_s32_meter_power_a < -50) B 相:if (l_s32_single_phase_max_feed_power + l_s32_meter_power_b < 0) C 相:if (l_s32_single_phase_max_feed_power + l_s32_meter_power_c < 0)
三相比对同一物理量使用了不同阈值,会导致「同样工况下 A 相走 max-feedin 限制分支、B/C 相走放开馈网分支」的三相不对称行为,是大概率的手写复制遗漏。
- 对
logic/ac_control.c的隐含链接依赖(当前未触发):本文件通过logic/logic.h/para_tables与 AC 侧耦合,而three_unbalance_power_inv_single_phase_full_cnt()这类共用辅助函数定义在logic/ac_control.c:1123且被INV_AC_CTRL包裹 —— 若将来dc_control.c复用 AC 侧函数,而编译配置只开了INV_DC_CTRL未开INV_AC_CTRL,会直接链接失败。本文件当前未调用任何 AC 侧函数,是安全的;dc_control.c:849调用的get_maxfeedin_priority_flag()定义在logic/logic.c(不在任何条件编译内),也是安全的。
s32_bat_total_power与s32_pbat_inv的符号约定混用:s32_bat_total_power来自parallel_read_system_bat_power()→bms_read_total_power()(midware/bms/bms.c:3475,由 BMS 电压 × 电流得到),而s32_pbat_inv来自逆变器采集。代码在同一个if里混用两者(如dc_control.c:270用l_s32_dc_total_set_bat_power与l_s32_bat_charge_power_limit比较、dc_control.c:274与l_s32_pbat_inv比较;dc_control.c:1104、1362、1621用charge_limit + bat_total_power > 100)。若两个来源的「充电为正/为负」约定不同,这些判据会在某些工况下反向。文件中没有任何注释说明换算关系,属跨模块隐含约定风险。
- 两套扰动计数器语义重复:文件全局
g_u8_dc_disturbance_count(19 行,被inverter.c使用)与dc_control_total_power_gc_chgpower_calculate()内 static 的s_u8_dc_disturbance_count(513 行)是两个独立变量,而u16_dc_disturbance_count(g_t_logic_info,logic.h:237)又被 281/336 行当作回退量读取却在本文件内从不写入。三个同义字段并存,极易出现「计数在涨但回退量恒为 0」的静默失效。
- 注释与代码矛盾的电池功率赋值(
dc_control.c:244、261、265,以及dc_control.c:547、556):注释写「电池充满后,控制电池功率归零」,代码却在电池仍充电(bat_total_power > 60)时赋charge_limit − bat_total_power(充电上限为 0 时为负值),否则赋固定 +15W / +50W(正为放电)。这意味着「电池充满」时反而下发一个小功率放电指令,仅靠上层dc_control_total_power_limit()把负值钳成 0 来兜底。语义可疑,需现场波形确认是否为有意为之。
dc_control_total_power_gc_chgpower_calculate()的节拍闸门被注释掉(dc_control.c:515-520):该函数每次调用都会完整执行,且它会读s_s32_.../ 静态扰动计数,其执行频次完全取决于调用方(logic/logic.c:2100的油机分支)的调用次数,与其它 DC 函数的节拍控制方式不一致。
dc_time_peak_clip_calculate()使用浮点字面量(dc_control.c:1968):(pinvt + |pgrid − peak|) * 1.1在s32表达式中引入double,在 GD32F407(Cortex-M4F,单精度 FPU)上会走软双精度路径,既增加耗时又会对结果做隐式截断。工程内其它地方一律用整数比例,此处是唯一的浮点混用。
dc_time_valley_fill_calculate()的 magic number 与初值:g_t_logic_info.s32_dc_total_set_inv_power = 5;(dc_control.c:1941)没有注释也没有宏;static s32 s32_last_bat_power = 0;(dc_control.c:1872)带的注释是「需在初始化时赋值」,但代码并未在任何初始化流程里赋值,属遗留 TODO。
dc_control_three_phase_power_limit()的不可达分支(dc_control.c:763-809):对每一相都是if (< 10) → 10; else if (> 单相额定) → 单相额定; else if (< 0) → 0;。第一个if已经把所有权值 < 10(含负数)的输入改成 10,因此第三个else if (< 0)永远不可达。属复制粘贴残留,无功能影响但显示限幅顺序的意图与实现不符(若要「先钳负为 0 再抬到 10」应把< 0判据前置)。
- 三相不平衡主算法以「当前设定值」为迭代起点(
dc_control.c:977-980):l_s32_ctrl_inv_X_phase_power = g_t_logic_info.s32_dc_three_phase_set_inv_X_power;。由于logic.c之后还会跑dnsp_ramp_dc_control_handle()等改写流程,这个「起点」可能已经被上一级改过,形成两级闭环叠加,调参时难以归因。
u8_regulator_speed直接做除数且从不校验是否为 0:全文共有 53 处引用该字段(Grep统计),其中约 48 处形如... / l_u8_regulator_speed(如dc_control.c:287、296、353、359、366、375、575、621、627、634、1000、1022、1047、1073、1107、1111、1142、1150、1167、1173、1179、1180、1258、1281、1305、1331、1365、1369、1400、1408、1425、1431、1437、1438、1516、1540、1564、1590、1624、1628、1659、1667、1684、1690、1697等)。该值由逆变器特性整定(在logic/logic.c按型号赋值),本文件没有任何 0 值保护。一旦型号未识别或配置缺失导致该值为 0,会产生除零(Cortex-M4 上表现为除零陷阱或未定义结果,取决于编译选项与SCB->CCR.DIV_0_TRP)。
dc_control_total_power_self_use_calculate()中dc_ctrol_para_tables的变量名拼写错误(dc_control.c:205,ctrol应为control)且索引u8_inv_type无边界校验。当前inverter_get_special_control_inv_type()只返回 0/1,para_tables[2]不会越界;但一旦增加型号,索引就越界读。
- 调试痕迹:
dc_control.c:730与dc_control.c:1810各有l_u16_test = l_u16_test;/l_u32_test[0] = l_u32_test[0];的自赋值(压制未使用告警);dc_control.c:1116的三相不平衡主算法用l_u32_test[3]记录了多达 6 层嵌套的条件路径号,仅在DEBUG_LOGIC下打印,属「用变量记录执行路径」的调试手法,正式代码里保留会误导阅读。
dc_control_total_power_bat_force_discharge()中s32_bat_total_power与s32_ppv_inv相关注释被注释掉(dc_control.c:133、141-144):// - g_t_logic_info.t_logic_control_parameter.s32_ppv_inv与// if (set_bat < 0) set_bat = 0;两处被注释,说明该处语义曾多次改动;当前实现允许set_bat为负(即强放模式下反而给充电设定),依赖dc_control_total_power_limit()兜底。
dc_control_three_phase_power_self_use_calculate()单函数 940 行、三层深嵌套、A/B/C 三段近乎复制:任一处修改都需要同步改三遍,本文件已出现第 4 点所述的不一致。这是本文件最大的可维护性风险。