set_comm_can.c
senergys5_inv_set_ac_power_percent,第 120 行)4.6 DC 侧电流反算与工作模式决策(senergys5_inv_bat_parameter_calculate,第 182 行)4.7 加热电流覆盖(第 393~420 行)4.8 MPPT 启停4.9 事件帧 0x1A1 / 0x1984.10 参数变化检测(set_inverter_can_data_check,第 550 行)4.11 主循环门控与超时状态机(set_inverter_comm_can,第 679 行)4.12 通讯丢失判定与数据清零4.13 与 uCOS-II 的配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/inverter/set/set_comm_can.c所属模块:midware/inverter/set(盛能杰 S5 / S5-II / M10 逆变器通信) 代码行数:896
1. 文件职责
本文件实现 EMS 主控与盛能杰(Senergy)Smile5 / S5-II / M10 逆变器之间的 CAN 总线实时控制通道。它把上层能量调度算出的"AC 侧并网功率限幅百分比"和"DC 侧充放电电流"翻译成 0x199 控制帧下发,同时接收逆变器回传的 0x26B/0x26C/0x26D 运行帧,填充 g_t_inv_info.t_run_info 里的并网/备电/电池电压电流等实时量。它在数据流上是"调度结果 → 执行机构"的最后一跳,同时把执行机构的实测值回灌给上层。驱动方式是由 user 侧静态创建的 task_inverter_can 任务(midware/inverter/inverter.c:1199)驱动,经 OSFlagPend(PEND_INV_CALCULATE_DONE) 与功率计算任务同步后调用 set_inverter_can_data_check() 与 set_inverter_comm_can()。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
SET_COMM_CAN_T | 结构体 | EMS 侧 CAN 触发标志与节拍容器,定义在 set_comm_can.h:36-44,实例 g_t_set_comm_can_info 于本文件第 26 行定义 |
CAN_MSG_TYPEDEF_T | 结构体 | CAN 报文容器,定义在 hardware/can/can0.h:19-27:frame_id/frame_type/frame_len/frame_data[8]/reading_flag。#pragma pack(1) 紧凑排布 |
SET_EMS_INV_IDCODE_PACK | 宏 0x181 | EMS→INV 握手帧 ID |
SET_EMS_INV_ACCURACY_CALIBRATION | 宏 0x198 | EMS→INV 电池电流校准帧 ID |
SET_EMS_CTRL_INV_DC_POWER1 | 宏 0x199 | EMS→INV DC/AC 控制帧 ID(45 ms 周期) |
SET_EMS_SEND_BAT_PROTECT_ARG | 宏 0x1A1 | EMS→INV 电池保护参数帧 ID |
SET_INV_EMS_IDCODE_PACK | 宏 0x201 | INV→EMS 握手应答帧 ID |
SET_INV_EMS_PARA_SET_RESULT_PACK | 宏 0x202 | INV→EMS 参数设置结果应答帧 ID |
SET_INV_EMS_RETURN_RUN_INFO_PACK1/2/3 | 宏 0x26B/0x26C/0x26D | INV→EMS 运行数据回读帧 ID |
CAN_FT_DATA | 宏 0x00000000 | 数据帧类型(定义于 HALLIB/GD32F4xx_standard_peripheral/Include/gd32f4xx_can.h:673) |
SELF_USED_MODE | 枚举值 0 | 自发自用工作模式(set_comm_usart_s5.h:304,SENERGYS5_HYBRID_WORK_MODE_E) |
BACK_UP_MODE | 枚举值 3 | 备电优先工作模式(set_comm_usart_s5.h:307) |
MPPT_DEFAULT / MPPT_STOP | 枚举值 0 / 1 | MPPT 启停状态(set_comm_usart_s5.h:286-287,MPPT_CTRL_E) |
SUB_S5 / SUB_S5_II / SUB_M10 | 枚举值 | 逆变器子型号(midware/inverter/inverter.h:311/332/333),用于区分 CAN 波特率与协议分支 |
G98 / VDE4105_11_18 | 枚举值 20 / 1 | 安规类型(midware/inverter/inverter.h:213 / 194),用于 AC 功率硬限幅 |
SET_TYPE / INVERTER_MAX | 宏 2 / 7 | 逆变器类型判别宏(midware/inverter/inverter.h:165/170) |
TRUE / FALSE | 宏 | 工程统一布尔常量 |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
CAN_Send_Msg(CAN_MSG_TYPEDEF_T *TxMsg) | static | CAN 发送封装,发送前保证与上一帧至少间隔 15 ms(不足则 OSTimeDlyHMSM 主动让出 15 ms) |
send_ems_inv_idcode_cmd(void) | static | 组帧并发送 0x181 握手帧,数据域为 ASCII "AlphaESS" |
senergy_comm_init_can(void) | 全局 | 按子型号设置 CAN1 预分频(S5→8,S5-II/M10→4),并把握手标志、电池保护参数标志置 TRUE |
senergys5_inv_set_ac_power_percent(s32 s32_ac_power) | static | 把 AC 侧功率需求换算成 0~10000(即 0~100.00%)的并网功率限幅百分比写入 s_senergys5_control_info_tab |
senergys5_inv_bat_parameter_calculate(s32 s32_dc_power) | static | 由 DC 侧功率需求(+放电/−充电)反算电池充/放电最大电流,并决定逆变器内部工作模式 |
send_dc_ctrl_inv_power_1(void) | static | 0x199 主控制帧的组帧发送函数,内部串联限幅、静默功率斜坡、加热电流、MPPT 启停全部逻辑 |
send_ems_set_bat_protect_arg(void) | static | 组帧发送 0x1A1 电池保护参数(充放电截止电压与最大电流),成功后清除待发标志 |
send_ems_set_bat_cur_calibration(void) | static | 组帧发送 0x198 电池电流校准帧(当前电池电流/10),其余字节填 0 |
set_inverter_can_data_check(void) | 全局 | 下发前的参数准备:限幅电池最大充放电功率、比较 BMS 与本地配置的充放电截止电压/电流,变化时置位 0x1A1 待发标志 |
set_inverter_comm_ate_can_test(void) | 全局 | 产线 ATE 直通模式专用:1 s 周期只发 0x181 握手帧,不做数据收发 |
set_inverter_comm_can(void) | 全局 | CAN 通道主循环:门控判断 → 周期/事件发送 → 收帧解析 → 通讯丢失判定与清零 |
4. 核心逻辑解析
4.1 初始化与速率自适应
senergy_comm_init_can()(第 84 行)按 get_inverter_sub() 分支设置 CAN1 预分频:
| 子型号 | 预分频 | 说明 |
|---|---|---|
SUB_S5 | 8 | 老 S5 平台,对应较低速率 |
SUB_S5_II / SUB_M10 | 4 | 二代机 / M10,速率翻倍 |
只有当前预分频值与目标不一致时才调用 can1_mode_init(prescaler) 重新配置,避免重复初始化打断通信。初始化末尾无条件把两个"待发标志"置 TRUE,使复位、DSP 重启、型号识别成功后都能强制重新握手。
4.2 发送节拍与 CAN 帧限速
CAN_Send_Msg()(第 38 行)用文件级 static u32 s_u32_send_tick 实现了一个全局最小帧间隔:
if (abs(OSTimeGet() - s_u32_send_tick) < 15) // OS_TICKS_PER_SEC = 1000 → 15 ms
OSTimeDlyHMSM(0, 0, 0, 15);
s_u32_send_tick = OSTimeGet();
return can1_send_standard_pack(TxMsg);
本文件一帧 0x199 内实际会连续调用 CAN_Send_Msg 一次,但 0x199(45 ms 周期)与 0x198/0x1A1(事件帧)可能在同一轮内相邻发出,这个限速就是防止总线瞬时拥塞。注意:OSTimeDlyHMSM 会让出整个 task_inverter_can 任务的 CPU,因此 CAN 发送是"阻塞式节流"。
4.3 控制帧 0x199 的帧格式与数据域语义
send_dc_ctrl_inv_power_1()(第 241 行)负责组装:
| 字节 | 内容 | 单位/说明 |
|---|---|---|
| d0 | u8_set_inv_hybrid_work_mode | 0=自发自用,3=备电优先 |
| d1 | u8_set_mppt_start_stop_flag | 0=MPPT_DEFAULT(自控),1=MPPT_STOP |
| d2~d3 | u16_set_inv_ac_power_limit_percent | 大端,0.01% 精度(10000 = 100%) |
| d4~d5 | u16_set_bat_dischg_curr | 大端,0.1 A |
| d6~d7 | u16_set_bat_chg_curr | 大端,0.1 A |
固定 frame_len = 8、frame_type = CAN_FT_DATA。帧内不含长度/校验字段——CAN 控制器的 CRC 与 DLC 已保证完整性,这是标准 CAN 应用层"裸载荷"写法。
4.4 功率限幅链(0x199 的数据来源)
发送前的限幅顺序(第 253~301 行):
- 取源:
l_s16_dc_set_power = parallel_read_dc_total_set_bat_power()(并机逻辑汇总后的电池侧设定),l_s16_ac_set_actpower = parallel_read_dc_total_set_inv_power()。l_s16_ac_set_reactpower被硬置 0(无功不下发)。 - 四级限幅:分别按
u32_dc_rate_discharge_power/u32_dc_rate_charge_power/u32_ac_rate_discharge_power/u32_ac_rate_charge_power做正负方向钳位。 if (l_s16_ac_set_actpower < 50) l_s16_ac_set_actpower = 50;——AC 需求被强制抬到 50 W 以上。- 安规限幅:
u8_safety == G98时 AC 上限 3680 W;VDE4105_11_18且 S5 且并机使能时上限 4680 W。 - 静默功率斜坡(第 311~375 行):当 DC 需求为 0 且子型号不是 S5-II/M10 时,用局部
static s_s16_battery_static_power输出一个缓变量替代 0。两条斜坡规则:(u8_allow_charg_discharg_flag & 0x30) == 0(电池不允许充放电)时,按pvcharger_read_bat_curr():电流 ≥0 时每 3 s 减 2 W(下限 −100),电流 ≤25 时每 3 s 加 2 W(上限 +100)。- 否则按更快的 1 s 节拍:电流 >30 时每 1 s 减 2 W,电流 <0 时每 1 s 加 2 W。
目的是让逆变器输出一个 ±100 W 以内的"静默功率"来抵消待机功耗,避免电表侧出现持续反送电。该值同时写入全局 g_s16_battery_static_power。
- 其它情况(
l_s16_dc_set_power != 0或子型号为 S5-II/M10)把s_s16_battery_static_power清零并刷新节拍,做到"退出静默态立即归零"。
4.5 AC 限幅百分比换算(senergys5_inv_set_ac_power_percent,第 120 行)
if (abs(s32_ac_power) < 60)
s32_set_ac_power_temp = (s32_inv_rated_output_power < 60) ? 5 : 0; // 需求<60W → 视为"不馈网"
else
s32_set_ac_power_temp = s32_ac_power;
钳位到 [0, u32_nominal_ac_active_power]
percent = s32_set_ac_power_temp * 10000 / u32_nominal_ac_active_power // 0.01% 精度
- 60 W 是"死区"阈值:需求小于 60 W 一律折算成 0%(若额定输出小于 60 则给 5 W 的极小值,实际工程中不会触发)。
- 中间被注释掉的
-50偏置和四舍五入片段(第 152~158 行)说明历史上曾用"向下扣 50"来对抗电表偏向市电取电,现已废弃。 - 结果写入
s_senergys5_control_info_tab.u16_set_inv_ac_power_limit_percent。
4.6 DC 侧电流反算与工作模式决策(senergys5_inv_bat_parameter_calculate,第 182 行)
输入 s32_dc_power 约定 正=放电,负=充电。
- 先按
g_t_inv_info.t_run_info.u16_dc_max_charge_power/u16_dc_max_dischg_power双向钳位。 - 分流:
s32_dc_power >= 0(放电):l_s32_dc_dischrage_power = s32_dc_power,l_s32_dc_chrage_power = s32_bat_charge_power_limit,工作模式 =SELF_USED_MODE。若电池加热方式为逆变器加热(u8_heat_mode == 2)且无加热故障(read_bat_heat_fault_flag() == 0),改为BACK_UP_MODE。s32_dc_power < 0(充电):放电电流置 0,充电功率取绝对值,工作模式 =BACK_UP_MODE。
- 电流反算公式(第 225~226 行):
I_set = min( P_dc * 10000 / U_bat_total , I_max_rate * 10 )
其中 bms_read_total_volt() 为 BMS 总压,*10000/ 与 *10 把结果统一到 0.1 A;u16_chg_max_rate_cur / u16_dischg_max_rate_cur 是逆变器侧的最大倍率电流(0.1 A 精度)。写入 u16_set_bat_chg_curr / u16_set_bat_dischg_curr。
4.7 加热电流覆盖(第 393~420 行)
若 u8_set_heat_power_flag == TRUE:
u8_request_heat == 0(电池已无加热请求)→ 清标志并把充电电流写 0;- 否则按
u8_request_heat_bat_num直接覆盖充电电流:1→1000,2→2000,3→3000,≥4→4000(单位 0.1 A,即 100~400 A);若u8_bms_heat_cmd != 0xAA(EMS 尚未下发进入加热模式)则固定 2000。
这段逻辑会覆盖 4.6 算出的充电电流,是加热优先级高于能量调度的体现。
4.8 MPPT 启停
g_t_inv_info.t_run_info.u16_mppt_percentage == 0 → MPPT_STOP(1),否则 MPPT_DEFAULT(0)。即 MPPT 百分比为零时彻底关断 PV 输入。
4.9 事件帧 0x1A1 / 0x198
| 帧 | 触发条件 | 数据域 |
|---|---|---|
| 0x1A1 | g_t_set_comm_can_info.u8_ems_set_inv_bat_protect_arg_flag1 == TRUE | d0~d1 放电截止电压,d2~d3 放电最大电流×10,d4~d5 充电截止电压,d6~d7 充电最大电流×10(均大端) |
| 0x198 | s_senergys5_task_tab.u8_inv_old_version_flag == FALSE 且距上次 >200 ms 且 g_t_bat_info.t_run_info.u8_ems_send_bat_cur_flag == TRUE | d0~d1 = pvcharger_read_bat_curr()/10(大端),d2~d7 全 0 |
0x1A1 由 set_inverter_can_data_check() 在检出参数变化时置位,发送成功后(CAN_Send_Msg 返回 TRUE)立即清标志,属于"一次性事件帧"。0x198 由 BMS 模块(midware/bms/alpha/battery_comm.c:1361)置位 u8_ems_send_bat_cur_flag 触发,且带 200 ms 最小间隔;老版本协议(u8_inv_old_version_flag == TRUE,即 S5 全系或主版本号 ≤401 的 S5-II)不支持该校准帧,直接跳过。
4.10 参数变化检测(set_inverter_can_data_check,第 550 行)
职责是把 BMS 能力与本地配置合成一份"最终下发给逆变器的保护参数",流程:
- 最大充放电功率:取
abs(pvcharger_read_bat_max_charge_power())与放电对应值,按t_config_data.u32_dc_rate_*_power钳位后写入g_t_inv_info.t_run_info.u16_dc_max_charge_power/u16_dc_max_dischg_power,仅在数值变化时才写(避免无谓刷新)。 - 截止电压兜底:若电池已连接(
pvcharger_read_bat_connect_state() != 0)且本地配置的充/放电截止电压为 0,则用 BMS 上报值补齐。 - 四项参数合成(第 611~637 行):
- 放电截止电压:BMS 值低于本地配置则取本地配置(取高者);为 0 也取本地配置。
- 充电截止电压:BMS 值高于本地配置则取本地配置(取低者);为 0 也取本地配置。
- 充/放电最大电流:超过本地配置上限则钳到上限。没有 0 值兜底(若 BMS 返回 0,会直接把最大电流下发成 0)。
- 变化判定与置标志(第 639~650 行):电压不等、或电流差绝对值 >10(0.1 A 精度即 1 A)时,刷新
g_t_inv_info.t_run_info的四个字段并置u8_ems_set_inv_bat_protect_arg_flag1 = TRUE,驱动 0x1A1 重发。
4.11 主循环门控与超时状态机(set_inverter_comm_can,第 679 行)
进入正常通信的门控条件(第 685 行):
(!(is_inverter_firm_download_finished() || Get_Status_Is_Updated_From_Server(UPDT_INV3)))
&& (s_senergys5_control_info_tab.s8_dcdc_evocation_cnt >= 5)
is_inverter_firm_download_finished():主机/从机固件已下载完成 → 进入升级流程,CAN 让位;Get_Status_Is_Updated_From_Server(UPDT_INV3):DCDC/ARM 固件待升级;s8_dcdc_evocation_cnt >= 5:型号识别成功的标志(set_comm_usart_s5.c的senergy_model_recognition()在识别成功后写入 5)。
门控通过时的发送序列:
| 条件 | 动作 | 周期/节拍变量 |
|---|---|---|
u8_ems_inv_idcode_flag == TRUE | 发 0x181 | u32_ems_inv_idcode_tick 1000 ms |
| ↑ 为 FALSE | 发 0x199 | u32_ems_send_inv_ctrl_power_tick1 45 ms |
| 老版本标志为 FALSE 且 0x198 待发 | 发 0x198 | u32_ems_set_inv_accuracy_calibration_tick 200 ms |
u8_ems_set_inv_bat_protect_arg_flag1 == TRUE | 发 0x1A1 | 无节拍,事件驱动 |
关键状态迁移:收到 0x201(SET_INV_EMS_IDCODE_PACK,第 742 行)→ u8_ems_inv_idcode_flag = FALSE 且 u8_inverter_can_comm_lost_flag = FALSE。即"握手完成才进入数据交换态"。
每个节拍判断都带一段"时钟回绕保护":
else if (g_t_set_comm_can_info.u32_xxx_tick > OSTimeGet())
g_t_set_comm_can_info.u32_xxx_tick = OSTimeGet();
这是为了处理 OSTimeGet() 回绕后节拍基准值"超前于当前时间"的情况,直接把它拉回当前时刻。
收帧循环(第 735~805 行):
l_u32_inv_recv_tick = OSTimeGet();
while (get_can1_standard_pack(&l_t_inveter_info) == TRUE)
{
s_u32_inv_lost_tick = OSTimeGet();
switch (frame_id) { ... }
if (abs(l_u32_inv_recv_tick - OSTimeGet()) > 50) break; // 单次最多处理 50 ms
}
- 每成功取一帧就刷新
s_u32_inv_lost_tick("活着的证据"); - 50 ms 的
break是防御性限流,避免总线被灌爆时本任务长时间不返回。
三帧运行数据的解析映射:
| 帧 | 字节 | 目标变量 | 换算 |
|---|---|---|---|
| 0x26B | d0~d1 | s32_grid_actpower_a | 取负(-(s16)),说明逆变器以"反送电为正"上报,EMS 内部以"取电为正" |
| 0x26B | d2~d3 | u16_grid_freq | 原值 |
| 0x26B | d4~d5 | s32_backup_actpower_a | 有符号 |
| 0x26B | d6~d7 | u16_backup_freq | 若 u16_backup_volt_a == 0 则强制写 0(无备电输出时不报频率) |
| 0x26C | d0~d1 | u16_grid_volt_a | /10 → 0.1 V |
| 0x26C | d2~d3 | u16_backup_volt_a | /10 → 0.1 V |
| 0x26C | — | s16_backup_cur_a | 由 backup_actpower_t * 10 / backup_volt_a 间接算出 |
| 0x26D | d0 | s_senergys5_device_info_tab.u8_inv_hybrid_work_mode | 实际工作模式回读 |
| 0x26D | d1 | s_senergys5_device_info_tab.u8_mppt_start_stop_flag | MPPT 实际状态 |
| 0x26D | d2~d3 | s32_inv_actpower_a | 有符号 |
| 0x26D | d4~d5 | s16_bat_power | 有符号 |
| 0x26D | d6~d7 | u16_bat_volt(/10)与 s_senergys5_device_info_tab.u16_inv_bat_volt(原值) | 同一字节段两种精度各存一份 |
每帧解析末尾都会执行 s32_xxx_t = s32_xxx_a(把瞬时值拷贝成"平滑/供上层使用"的镜像)并刷新 u32_inv_send_ems_can_tick1。此外 0x26B 会把 s32_backup_actpower_t 存入 g_t_inv_info.t_run_info.u16_dsp_debug[18] 作为调试快照。
4.12 通讯丢失判定与数据清零
两道独立的超时:
- 收帧超时(第 808 行):
abs(OSTimeGet() - s_u32_inv_lost_tick) > 60 * 1000(60 s)→ 首次触发时can1_clear_standard_pack()清空接收环形缓冲、置u8_inverter_can_comm_lost_flag = TRUE、重新置位握手标志u8_ems_inv_idcode_flag = TRUE。 - 运行帧超时(第 887 行):
OSTimeGet() - u32_inv_send_ems_can_tick1 > 60 * 1000→ 同样重新置位握手标志。二者互为补充:前者看"有没有任何帧",后者看"有没有 0x26B/C/D 这组关键帧"。
丢失态数据处理(第 819~838 行):把并网/备电/逆变/电池相关的 13 个量全部写 0,并把 MPPT 与工作模式回读量清零,同时重新置位两个待发标志。这段清零在丢失标志为 TRUE 期间每一轮都会执行,属于"持续归零"策略而非一次性归零。
门控不通过时的 else 分支(第 863~884 行)做同样的一轮清零,但额外把 u8_inverter_can_comm_lost_flag = FALSE(第 866 行)——即在升级/未识别型号期间对上层宣称"CAN 未丢失",这是一个语义上容易引起误判的处理,详见风险点。
4.13 与 uCOS-II 的配合
- 任务:
task_inverter_can(midware/inverter/inverter.c:1199)在for(;;)中先OSFlagPend(g_t_fast_dispatch_flag, PEND_INV_CALCULATE_DONE, OS_FLAG_WAIT_SET_ALL | OS_FLAG_CONSUME, 20, &err)等待功率计算完成事件,超时 20 个 tick,再调用本文件两个函数。因此本文件的调用频率是"每轮功率计算一次 + 20 tick 兜底"。 - 本文件不使用信号量或消息邮箱,帧缓冲由
hardware/can/can1.c的环形数组 +reading_flag保护(中断写、任务读)。 - 唯一主动让出 CPU 的地方是
CAN_Send_Msg的 15 ms 限速延时和OSTimeDlyHMSM节流。没有其它阻塞点。 - 模块编译期由
INVERTER_TYPE(midware/inverter/inverter.h:172)为INVERTER_MAX或SET_TYPE时的get_inverter_type() == SET_TYPE判定启用。
5. 对外接口
本文件导出的函数(在 set_comm_can.h:51-54 声明):
| 函数 | 调用者 |
|---|---|
senergy_comm_init_can() | midware/inverter/inverter.c:76(初始化)、midware/inverter/set/set_comm_usart_s5.c:475(型号识别成功后重建 CAN) |
set_inverter_can_data_check() | midware/inverter/inverter.c:1223 |
set_inverter_comm_can() | midware/inverter/inverter.c:1224 |
set_inverter_comm_ate_can_test() | midware/inverter/inverter.c:1229(ATE 直通模式) |
本文件导出的全局变量:
| 变量 | 类型 | 使用方 |
|---|---|---|
g_t_set_comm_can_info | SET_COMM_CAN_T | set_comm_usart_s5.c:4433-4434(DSP 复位时重置 0x181/0x1A1 待发标志);无 extern 声明于任何头文件,靠 set_comm_can.h:47 暴露 |
g_s16_battery_static_power | s16 | 本文件写(第 369 行),全工程无任何读取点,也无 extern 声明 |
本文件调用的外部函数/变量:
| 来源模块 | 符号 |
|---|---|
hardware/can/can1.h | can1_send_standard_pack、get_can1_standard_pack、can1_mode_init、get_can1_prescaler、can1_clear_standard_pack |
midware/inverter/pvcharger.h | pvcharger_read_bat_curr、pvcharger_read_bat_max_charge_power、pvcharger_read_bat_max_dischg_power、pvcharger_read_bat_max_charge_current、pvcharger_read_bat_max_dischg_current、pvcharger_read_bat_charge_cut_off_volt、pvcharger_read_bat_dischg_cut_off_volt、pvcharger_read_bat_connect_state |
midware/bms/bms.h | bms_read_total_volt、g_t_bat_info(t_static_data.u8_heat_mode、t_run_info.u8_heat_mode/u8_request_heat/u8_request_heat_bat_num/u8_bms_heat_cmd/u8_allow_charg_discharg_flag/u8_ems_send_bat_cur_flag/u16_dischg_cut_rate_volt/u16_chg_cut_rate_volt) |
midware/inverter/parallel.h | parallel_read_dc_total_set_bat_power、parallel_read_dc_total_set_inv_power |
midware/inverter/inverter.h | g_t_inv_info(t_run_info/t_config_data/t_safty_info)、get_inverter_type、get_inverter_sub |
midware/inverter/set/set_comm_usart_s5.h | s_senergys5_control_info_tab、s_senergys5_device_info_tab、s_senergys5_task_tab(三者定义于 set_comm_usart_s5.c:37/43/44)、SELF_USED_MODE、BACK_UP_MODE、MPPT_DEFAULT/STOP |
data/fault_warn_log/fault_warn_log.h | read_bat_heat_fault_flag |
net/inv_download.h / net/upd_download.h | is_inverter_firm_download_finished、Get_Status_Is_Updated_From_Server、UPDT_INV3 |
logic/logic.h | g_t_logic_info(t_config_data.u8_max_feedin_percent、t_logic_control_parameter.s32_inv_rated_output_power、s32_bat_charge_power_limit) |
| uCOS-II | OSTimeGet、OSTimeDlyHMSM |
6. 依赖关系
依赖(#include 链,第 13~22 行):
| 头文件 | 用途 |
|---|---|
set_comm_can.h | 本模块的宏、结构体与函数声明 |
set_comm_usart_s5.h | s_senergys5_control_info_tab / _device_info_tab / _task_tab、工作模式与 MPPT 枚举 |
inverter.h | g_t_inv_info、get_inverter_type/sub、INVERTER_TYPE 编译期开关 |
meter.h | 电表侧接口(本文件未见直接引用,疑为模板遗留) |
bms.h | g_t_bat_info、bms_read_total_volt |
logic.h | g_t_logic_info 充电功率上限、额定输出功率 |
can1.h | CAN1 收发 API 与 CAN_MSG_TYPEDEF_T |
inv_download.h | is_inverter_firm_download_finished |
system_config.h | 系统配置层(本文件未见直接引用,疑为模板遗留) |
fault_warn_log.h | read_bat_heat_fault_flag |
被依赖:
midware/inverter/inverter.c:三个导出函数的唯一调用方(在task_inverter_can内)。midware/inverter/set/set_comm_usart_s5.c:通过g_t_set_comm_can_info重置待发标志,并在senergy_model_recognition()里调用senergy_comm_init_can()。
7. 备注与风险点
- 函数头注释张冠李戴:第 52~58 行的注释块名为
kelong_inverter_comm,实际函数是send_ems_inv_idcode_cmd;第 77~83 行注释名为ginlong_comm_init_can,实际是senergy_comm_init_can;第 114 行、第 176 行的@Name也是科隆模块残留。属模板拷贝遗留,会误导后续维护。 senergys5_inv_set_ac_power_percent()里的<60 W → 0%与调用侧的if (l_s16_ac_set_actpower < 50) = 50相互纠缠:第 293~296 行先把小于 50 的值(含所有负值)抬到 50,而 50 < 60 又会被折算成 0%。结果是第 279~282 行"AC 充电方向钳位"以及第 288~291 行无功钳位全部是死代码(永远被 50 覆盖)。若将来有人改动 60 这个阈值,这段逻辑会突然"复活",需特别小心。u16_ac_rate_power_temp被声明为u16却接收u32的u32_nominal_ac_active_power(第 124/145 行)。当额定 AC 有功功率超过 65535 W(大型机)时高 16 位被截断,百分比会算错。当前 S5 平台功率较小,暂不触发。- 0x198 帧组帧对有符号电流不友好(第 522 行):
pvcharger_read_bat_curr()返回s16,若为负(放电或反充),(curr/10) >> 8是算术右移,& 0x00ff后得到0xFF,与正数的高字节取值域混淆;接收端必须靠低字节+符号约定还原。建议只发绝对值或显式(s16)(curr/10)。 g_s16_battery_static_power是只写不读的全局量(第 27 行定义、第 369 行写),且send_dc_ctrl_inv_power_1()内部又用同名static s_s16_battery_static_power遮蔽它(第 257 行),两个变量名几乎相同极易误读。u8_inverter_can_comm_lost_flag在门控不通过时被强制写成 FALSE(第 866 行),即"CAN 完全不通信"期间上报层看到的是"未丢失"。若上层仅凭此标志判断逆变器在线,会在升级期间得到错误结论。s_u32_inv_lost_tick初始为 0:上电后若 60 s 内无任何帧到达,第一次判断abs(OSTimeGet() - 0) > 60000成立即进入丢失态,这个行为本身是正确的;但g_t_set_comm_can_info.u32_inv_send_ems_can_tick1初始亦为 0,第 887 行的判断在开机 60 s 后会无条件把u8_ems_inv_idcode_flag置 TRUE,与初始化时的置位重复但无害。- 静默功率斜坡是"每 3 s 变 2 W"的开放式积分,上限 ±100 W(第 320/332/348/360 行)。多次进入/退出该分支时用
s_u32_battery_static_tick = OSTimeGet()+ 清零做复位,但斜坡变量本身只在两块里共用一个 static,若两块分支在相邻轮次交替进入(u8_allow_charg_discharg_flag抖动),斜坡会保留历史值继续积分,收敛时间不确定。 u16_dsp_debug[18]接收一个s32有功功率(第 774 行):inverter.h:1699中u16_dsp_debug[30]是u16数组,超过 ±32767 W 的功率会溢出,仅作调试快照用,不影响控制。- 第 819~838 行的清零代码块与第 867~881 行的清零代码块内容几乎完全重复(13 个赋值),仅丢失标志的写法不同,建议抽成一个
static void set_can_run_info_zero(void)以消除重复带来的维护风险。 - 0x202 应答帧的处理为空壳(第 747~760 行):
switch内只有case SET_EMS_INV_IDCODE_PACK: break;和default: break;,即收到逆变器的参数设置结果后不做任何校验或重发,参数是否被接受完全不可知。所有下发帧都是"发出去就算成功"的盲发模型。 meter.h/system_config.h两个 include 在本文件中未见任何符号引用,属冗余包含。