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

inovance_comm_can.c

源文件路径:midware/inverter/inovance/inovance_comm_can.c 所属模块:midware/inverter/inovance(汇川 Inovance 逆变器通信协议) 代码行数:790

1. 文件职责

本文件是汇川(Inovance)逆变器与 EMS 之间 CAN 总线私有协议 的 EMS 侧实现,与同目录的 inovance_comm_usart.c(SCI/Modbus-RTU 通道)构成一套"双通道"接口:本文件负责高频、实时的功率与电池限值下发以及运行数据接收,SCI 通道负责参数整定、安规配置与故障录波。

数据流位置:向上从 logic/(并机功率分配 parallel_*)、midware/inverter/pvcharger.*(BMS 数据)读取目标值与限值,向下通过 can1 硬件层把 8 字节标准帧发到逆变器;反向把逆变器上报的三相电网/备电/机侧功率、频率、运行模式写入 g_t_inv_info.t_run_info,供 logic/ 与 hmi/ 使用。

被谁驱动:由 inverter.c:1264 在 uCOS-II 任务 task_inverter_can()(inverter.c:1199)中每个循环调用一次 inovance_inverter_comm_can(),随后调用一次 inovance_inverter_data_check_can();该任务以 g_t_fast_dispatch_flag 事件标志加 30 ms 延时驱动。

初始化入口 inovance_comm_init_can() 在 inverter.c:103(逆变器类型识别后)以及 inovance_comm_usart.c:2307(inovance_upgrade_init())和 inovance_comm_usart.c:413(软重启流程)中被调用。

本文件不产生新任务、不适用信号量/消息邮箱,全部以"轮询 + 标志位 + tick 时间戳"方式与 uCOS-II 协作。

2. 关键数据结构与宏

名称类型说明
INOVANCE_INV_DEBUG_CAN宏本文件所有 #if 调试打印的总开关,定义在 inovance_comm_can.h:16,值为 0(默认关闭)
INOVANCE_EMS_INV_CAN_COMM_LOST_TIME宏CAN 通讯丢失判据,15 * 1000 ms(inovance_comm_can.h:17)
CAN_MSG_TYPEDEF_T结构体CAN 帧容器,定义在 hardware/can/can0.h:20:frame_id(u32)、frame_type(u8)、frame_len(u8)、frame_data[8]、reading_flag,#pragma pack(1) 紧凑对齐
CAN_FT_DATA宏帧类型 = 数据帧,赋值给 frame_type
INOVANCE_EMS_INV_IDCODE_PACK = 0x181宏EMS→逆变器 身份握手帧(inovance_comm_usart.h:125)
INOVANCE_EMS_INV_SET_CTRL_MODE = 0x190宏EMS→逆变器 三相平衡/不平衡控制模式(:126)
INOVANCE_EMS_CTRL_INV_AC_POWER1 = 0x192宏EMS→逆变器 交流有功功率设定(:127)
INOVANCE_EMS_CTRL_INV_BAT_STATE = 0x193宏EMS→逆变器 电池充放电限值 + 电池接入状态(:128)
INOVANCE_EMS_SEND_BMS_POWER_LIMIT = 0x194宏EMS→逆变器 BMS 最大充放电功率(:129)
INOVANCE_EMS_SET_INV_CTRL_ARG1 = 0x195宏EMS→逆变器 MPPT 百分比 / 备电频率偏移 / UPS 使能(:130)
INOVANCE_EMS_SEND_BAT_RUNNING_DATA = 0x1A0宏EMS→逆变器 电池运行数据(:131)
INOVANCE_EMS_SEND_BAT_PROTECT = 0x1A1宏EMS→逆变器 电池保护参数(:132)
INOVANCE_INV_EMS_IDCODE_PACK = 0x201宏逆变器→EMS 身份应答(:134)
INOVANCE_INV_EMS_RUN_INFO_PACK4 = 0x263宏逆变器→EMS 电网 A/B 相电压与有功(:135)
INOVANCE_INV_EMS_RUN_INFO_PACK5 = 0x264宏逆变器→EMS 电网 C 相电压、总有功、电网频率(:136)
INOVANCE_INV_EMS_RUN_INFO_PACK6 = 0x265宏逆变器→EMS 备电 A/B 相电压与有功(:137)
INOVANCE_INV_EMS_RUN_INFO_PACK7 = 0x266宏逆变器→EMS 备电 C 相、备电总有功与频率(:138)
INOVANCE_INV_EMS_RUN_INFO_PACK8 = 0x267宏逆变器→EMS 备电各相视在功率(:139)
INOVANCE_INV_EMS_RUN_INFO_PACK9 = 0x268宏逆变器→EMS 机侧各相有功(:140)
INOVANCE_INV_EMS_RUN_INFO_PACKA = 0x269宏逆变器→EMS 机侧总有功、逆变频率、运行模式、电池功率(:141)
INOVANCE_INV_INIT_TIME宏握手帧重发周期 2 * 1000 ms(inovance_comm_usart.h:27)
INOVANCE_TYPE_E枚举机型:INOVANCE_TYPE_T12=1 / T15=2 / T20=3 / T20_OLD=52(inovance_comm_usart.h:161),本文件用 t_run_info.u16_inv_type 决定爬坡上限
INOVANCE_TABLE_T结构体汇川通信总表 g_t_inovance_comm_info(inovance_comm_usart.h:719),本文件只用其中的 CAN 部分字段与 t_run_info.u16_inv_type
INVERTER_CONTROL_CMD_TABLE_T结构体逆变器控制命令表(inverter.h:1857),inovance_inverter_data_check_can() 用其静态实例做"上一拍值"缓存
INVERTER_TABLE_T结构体逆变器总表 g_t_inv_info(inverter.h:1929 外部声明),本文件读写其 t_run_info / t_control_cmd / t_config_data / t_safty_info
PHASE_L1/L2/L3枚举相序标识(inverter.h:348),用于 parallel_read_ac_three_phase_set_inv_power()
cNormalMode = 3宏逆变器正常运行模式(inverter.h:23)

3. 函数清单

函数名作用域说明
CAN_Send_Msg (42)staticCAN 发送统一出口:保证两帧之间至少间隔 2 ms,再调用 can1_send_standard_pack()
send_ems_inv_idcode_cmd (63)static发送 0x181 握手帧,数据域为 ASCII "AlphaESS"
set_inv_ctrl_mode (92)static发送 0x190 控制模式帧:三相不平衡置 2、平衡置 1、未知置 0
relink_grid_power_limit (131)static按机型额定功率与"重连后速率"计算重连功率爬坡上限(double,返回累积值)
send_ac_ctrl_inv_power (202)static发送 0x192 交流有功设定:平衡模式用总有功,不平衡模式用三相各自值,并按爬坡上限限幅
send_inv_ac_ctrl_bat_state (289)static发送 0x193:电池充/放电功率限值(u16)与电池接入状态
send_inv_bms_power_limit (326)static发送 0x194:BMS 最大充电/放电功率,成功则清 u8_set_inv_bms_power_limit_flag
send_inv_bms_running_data (363)static发送 0x1A0:电池电压、继电器状态、SOC(写死 100)、充电状态
send_ems_set_bat_protect (415)static发送 0x1A1:放电截止电压、放电最大电流、充电截止电压、充电最大电流,成功则清 u8_set_inv_bat_protect_flag
set_inv_ctrl_arg1 (454)static发送 0x195:MPPT 百分比、备电输出频率偏移、UPS 使能,成功则清 u8_set_inv_ctrl_arg1_flag
inovance_comm_init_can (487)全局初始化 CAN1 预分频为 8(若不等于 8 才重配),并置位全部首次下发标志、恢复默认控制参数
inovance_inverter_data_check_can (510)全局变化检测器:比对三相模式、UPS 使能、BMS 限值、电池保护参数,按变化置对应下发标志
inovance_inverter_comm_can (593)全局CAN 通讯主循环:握手 → 周期/事件下发 → 批量收帧解析 → 通讯丢失判定与重初始化

说明:文件顶部第 25-33 行另有一份前向声明列表,其中声明了 CAN_Send_Msg、send_ems_inv_idcode_cmd、send_ac_ctrl_inv_power、set_inv_ctrl_arg1、send_ems_set_bat_protect、send_inv_bms_running_data、send_inv_bms_power_limit、send_inv_ac_ctrl_bat_state、set_inv_ctrl_mode 共 9 个;relink_grid_power_limit 因定义在使用点之前(131 行 vs 230 行)未列入声明。

4. 核心逻辑解析

4.1 握手与"上线"状态机

inovance_inverter_comm_can()(593)先用一个 u8_ems_inv_idcode_flag 把流程切成两半:

该标志在 inovance_comm_init_can()(496)被重新置 TRUE,因此每次通讯丢失或初始化后都会重新走一遍握手流程。这种"握手-放行"设计确保逆变器在未确认 EMS 身份前不会接受功率指令。

4.2 下发通道的周期与事件划分

握手完成后,else 分支(614-646)按三种节奏下发:

触发方式帧 ID周期/条件位置
条件标志(变化驱动)0x190 set_inv_ctrl_mode()u8_set_inv_ctrl_mode_flag == TRUE615-618
周期 50 ms0x192 → 0x1A0 → 0x193(三帧连发)(OSTimeGet() - u32_ems_send_inv_ctrl_power_tick) > 50620-630
条件标志0x195 set_inv_ctrl_arg1()u8_set_inv_ctrl_arg1_flag == TRUE632-635
条件标志0x194 send_inv_bms_power_limit()u8_set_inv_bms_power_limit_flag == TRUE637-640
条件标志0x1A1 send_ems_set_bat_protect()u8_set_inv_bat_protect_flag == TRUE642-645

每个 tick 比较后面都跟一段 else if(tick > OSTimeGet()) 的"回绕补偿"(如 627-630),把时间戳重新对齐到当前 tick,避免 u32 tick 溢出后长时间不再下发。

被置为 TRUE 的四个条件标志只在对应发送函数返回 TRUE 时才清零(119-122、350-353、474-477、441-444),即"发送成功才销账",形成一次到位的重发机制。

CAN_Send_Msg()(42)本身还有一层节流:若距上次发送不足 2 个 tick,则 OSTimeDlyHMSM(0,0,0,2) 延时 2 ms,保证 50 ms 内连发 3 帧时不会挤在同一时刻。

该函数只在 send_ac_ctrl_inv_power() 中按 500 ms 周期被调用(228-236),内部再按实际 tick 差计算真实经过时间:

delta_ticks  = current_tick - prev_relink_tick              // 正常情况
             = OS_TICKS_PER_SEC - prev + current + 1        // 回绕分支
delta_time_sec = delta_ticks * 0.001                        // 隐含 1 tick = 1 ms
accumulated_error += delta_time_sec - 0.5                   // 记录与理论 0.5 s 的偏差
rate_per_sec = (g_t_inv_info.t_safty_info.u16_rev_machine_rate / 1000.0) / 60.0
increment    = rate_per_sec * inv_power_limit * delta_time_sec
relink_power_limit = min(relink_power_limit + increment, inv_power_limit)

4.4 交流功率下发与限幅 send_ac_ctrl_inv_power()

216-219 行按三相平衡状态取设定值:

238-245 行用爬坡值做双向限幅:

若 l_s16_ac_set_actpower > 0 且 |值| > relink_power_limit  → 取 +relink_power_limit
若 l_s16_ac_set_actpower ≤ 0 且 |值| > relink_power_limit  → 取 -relink_power_limit

即无论充电还是放电,幅度都被限制在同一个爬坡窗口内。帧数据域排布(267-277)为 4 个 16 位有符号量,大端(高字节在前):[0:1]=总有功、[2:3]=A 相、[4:5]=B 相、[6:7]=C 相,每相只发送 s32 的低 16 位。

248-258 行是一段被整块注释掉的 TOR_SAFTY_CONTROL_BAT 强制充电取反逻辑,属版本遗留代码。

4.5 电池相关帧的数据来源

4.6 变化检测器 inovance_inverter_data_check_can()

522-523 行:若仍在握手阶段(u8_ems_inv_idcode_flag == TRUE)直接返回,不检测。

三类检测:

  1. 三相不平衡模式变化(525-529):与静态缓存 s_u8_inv_three_unbalance_mode 比较,变化则置 u8_set_inv_ctrl_mode_flag。
  2. UPS 使能变化(531-535):与静态 INVERTER_CONTROL_CMD_TABLE_T s_t_run_control_cmd 的 u8_ups_enable 比较,变化则置 u8_set_inv_ctrl_arg1_flag。注意只用了该结构体一个字段,其余字段未被使用。
  3. BMS 功率限值变化(539-544):最大充/放功率任一变化即置 u8_set_inv_bms_power_limit_flag。

电池保护参数部分(546-583)是核心限值算法:

l_u16_dischg_max_rate_cur = pvcharger_read_bat_max_dischg_current()
l_u16_chg_max_rate_cur    = pvcharger_read_bat_max_charge_current()
l_u16_dischg_max_rate_volt = pvcharger_read_bat_dischg_cut_off_volt()
l_u16_chg_max_rate_volt    = pvcharger_read_bat_charge_cut_off_volt()

充电截止电压 > 配置上限 → 压回配置上限        (552-555)
放电截止电压 < 配置下限 → 抬回配置下限        (557-560)
放电最大电流 > 配置上限 → 压回配置上限        (562-565)
充电最大电流 > 配置上限 → 压回配置上限        (567-570)

只有四项中任一与 g_t_inv_info.t_run_info 现存值不同时才写入并置 u8_set_inv_bat_protect_flag(573-583)。电压限值方向不对称是刻意为之:充电截止电压是上限、放电截止电压是下限,二者都被"收紧"到配置允许范围内。

4.7 CAN 接收解析循环

648-778 行是接收主体:

l_u32_inv_recv_tick = OSTimeGet();
while(get_can1_standard_pack(&l_t_inverter_info) == TRUE) {
    switch(frame_id) { ... }
    if(abs(l_u32_inv_recv_tick - OSTimeGet()) > 10) break;   // 单次最多解析 10 ms
}

get_can1_standard_pack() 从 can1 驱动的接收队列取帧,队列深度 CAN1_STANDARD_PACK_LEN = 80(hardware/can/can1.h:18)。每个 case 内都把两个字节拼成一个量并写回 g_t_inv_info.t_run_info,同时刷新 u32_ems_inv_can_comm_tick。解析出的字段映射如下:

帧 ID字节映射(大端)写入的全局量
0x263(664-667)[0:1] / [2:3] / [4:5] / [6:7]u16_grid_volt_a、s32_grid_actpower_a、u16_grid_volt_b、s32_grid_actpower_b
0x264(679-682)[0:1] / [2:3] / [4:5] / [6:7]u16_grid_volt_c、s32_grid_actpower_c、s32_grid_actpower_t、u16_grid_freq
0x265(694-699)[0:1] / [2:3] / [4:5] / [6:7]u16_backup_volt_a、s32_backup_actpower_a、u16_backup_volt_b、s32_backup_actpower_b(有功负值被钳到 0)
0x266(711-716)[0:1] / [2:3] / [4:5] / [6:7]u16_backup_volt_c、s32_backup_actpower_c、s32_backup_actpower_t、u16_backup_freq(有功负值被钳到 0)
0x267(728-731)[0:1]…[6:7]s32_backup_apparentpower_a/b/c/t
0x268(743-745)[0:1]/[2:3]/[4:5]s32_inv_actpower_a/b/c
0x269(756-759)[0:1]/[2:3]/[4]/[5:6]s32_inv_actpower_t、u16_inv_freq、u8_run_mode、s16_bat_power
0x201(654-661)无数据解析握手完成标志

注意所有解析都把 16 位值强转 (s16) 后再赋给 s32 字段(如 665 行),即符号扩展由 (s16) 强制转换完成;电压类字段则强转 (u16)。

4.8 通讯丢失判定与重初始化

if(abs(u32_ems_inv_can_comm_tick - OSTimeGet()) >= INOVANCE_EMS_INV_CAN_COMM_LOST_TIME /*15000*/)
{
    inovance_comm_init_can();
    g_t_inv_info.t_run_info.u8_inverter_can_comm_lost_flag = TRUE;
}

15 s 内没有收到任何帧(u32_ems_inv_can_comm_tick 未被刷新)即判为丢失:重新执行 inovance_comm_init_can()(重新握手 + 重新置全部下发标志),并置位丢失标记,由 inverter_lost_handle_can()(inverter.c)与 inverter_lost_handle() 转成故障位。

4.9 升级期间的互斥

598-599 行的门限:

if(!(is_inverter_firm_download_finished() || Get_Status_Is_Updated_From_Server(UPDT_INV3)))

这是本文件与 inovance_upgrade.c 之间唯一的协作点,走的是 net/inv_download.h 与 net/upd_download.h 提供的升级状态标志,不用信号量。

5. 对外接口

5.1 本文件导出(供外部调用)

名称类型调用方
inovance_comm_init_can函数inverter.c:103(机型识别后初始化)、inovance_comm_usart.c:2307(inovance_upgrade_init)、inovance_comm_usart.c:413(DSP 软重启后)
inovance_inverter_comm_can函数inverter.c:1264,位于 task_inverter_can() 主循环
inovance_inverter_data_check_can函数inverter.c:1265,紧随上者之后

本文件没有导出任何全局变量(static 变量均为函数内局部静态)。

5.2 本文件调用/引用的外部符号

来源模块符号
hardware/can/can1.hcan1_send_standard_pack、get_can1_standard_pack、can1_mode_init、get_can1_prescaler
hardware/can/can0.hCAN_MSG_TYPEDEF_T、CAN_FT_DATA
midware/inverter/inverter.hg_t_inv_info、inverter_get_three_unbalance_mode、inverter_read_run_mode、cNormalMode、PHASE_L1/L2/L3
midware/inverter/parallel.hparallel_read_ac_total_set_inv_power、parallel_read_ac_three_phase_set_inv_power、parallel_read_ac_total_set_bat_charge_limit_power、parallel_read_ac_three_phase_set_bat_charge_limit_power、parallel_read_ac_total_set_bat_dischg_limit_power、parallel_read_ac_three_phase_set_bat_dischg_limit_power、parallel_read_ac_total_set_bat_forced_charge_en、parallel_read_ac_three_phase_set_bat_forced_charge_en
midware/inverter/pvcharger.hpvcharger_read_bat_connect_state、pvcharger_read_bat_volt、pvcharger_read_bat_soc、pvcharger_read_bat_relay_state、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
net/inv_download.his_inverter_firm_download_finished
net/upd_download.h(经 inv_download.h 传递)Get_Status_Is_Updated_From_Server、UPDT_INV3
logic/logic.hg_t_logic_info.t_logic_control_parameter.u8_grid_status
midware/inverter/inovance/inovance_comm_usart.hg_t_inovance_comm_info、全部 CAN 帧 ID 宏、INOVANCE_TYPE_E、INOVANCE_INV_INIT_TIME
uCOS-IIOSTimeGet、OSTimeDlyHMSM

5.3 本文件写入的全局状态

6. 依赖关系

依赖(本文件 include/使用)

被依赖(谁用到本文件)

7. 备注与风险点

  1. SOC 假数据:send_inv_bms_running_data() 第 383 行 l_u8_bat_soc = 100; 硬编码为 100,注释写明是为规避逆变器内部 SOC 逻辑。副作用是逆变器侧完全看不到真实 SOC,任何依赖 SOC 的逆变器保护失效。
  2. 接收缓冲区无长度上限:while(get_can1_standard_pack(...)) 的解析循环没有帧数上限,只用 10 ms 时间窗兜底(774-777)。若 10 ms 内可解析的帧数不足,剩余帧会留到下一个 30 ms 周期,可能加剧队列积压(队列深度 80 帧)。
  3. relink_grid_power_limit() 中的误差补偿是死代码:第 178 行 //delta_time_sec += accumulated_error; 被注释,accumulated_error 只做累加与清零,实质不影响输出;第 176 行阈值 abs(accumulated_error * 10) >= 5 的 10 倍放大属魔法数字。
  4. tick 回绕处理不完整:142-144 行的回绕分支写成 OS_TICKS_PER_SEC - prev_relink_tick + current_tick + 1,这是"秒内计数器"的写法,而 OSTimeGet() 返回的是 32 位累计 tick,正确做法应是 (u32)(current - prev)。由于 32 位 tick 在 1000 tick/s 下约 49.7 天才回绕,该分支实际几乎不可达,但逻辑本身错误。
  5. 窄化赋值/隐式截断(多处):
    • 216 行 l_s16_ac_set_actpower = parallel_read_ac_total_set_inv_power();,s32 → s16。
    • 296-297 行 parallel_read_ac_three_phase_set_bat_charge_limit_power() 等返回 s32,赋给 u16。
    • 333-334 行 pvcharger_read_bat_max_charge_power()/..._max_dischg_power() 返回 s32,赋给 u16。
    • 374 行 pvcharger_read_bat_volt() 返回 s16,赋给 u16。
    • 240/244 行 l_s16_ac_set_actpower = relink_power_limit;,double → s16 隐式转换(当前上限 20000 不会溢出,但依赖该上限)。
    • 546-550 行 pvcharger_read_bat_max_dischg_current() 等返回 s16,赋给 u16,若 BMS 上报负值(无效值)会回绕成接近 65535 的大数,随后的"大于配置上限则压回"判断将失效,把无效保护值原样下发给逆变器。
  6. send_ac_ctrl_inv_power() 与 send_inv_ac_ctrl_bat_state() 的平衡判据写法不一致:前者用 == FALSE / != FALSE(216-219),后者用 == FALSE 的二分(296-297)。若 inverter_get_three_unbalance_mode() 返回非 0/1 的值(1 以外的真值),两处行为会不同——set_inv_ctrl_mode()(97-108)的三分支写法正好说明该函数可能存在第三种返回值。
  7. default 分支静默按 20 kW 处理(165-167):新增机型而未更新 switch 时,重连爬坡上限会按 T20 的 20000 W 计算,可能超过实际额定功率。
  8. 头注释与实际签名不符(版本遗留):如 86 行注释 static u8 set_inv_ctrl_mode(void) 实际为 void(92 行);125 行注释 static void relink_grid_power_limit(void) 实际返回 double(131 行);320 行注释 static u8 send_inv_bms_power_limit(void) 实际为 void(326 行)。
  9. 冗余 include:bms.h 在第 4 行与第 16 行重复包含;crc.h、data.h、system_config.h、wireless_driver.h、ems.h、meter.h、can0.h、bms_download.h 在本文件内未检出直接引用的符号(CAN_MSG_TYPEDEF_T/CAN_FT_DATA 来自 can0.h,其余应属历史遗留)。
  10. 被注释掉的整块逻辑:248-258 行 TOR_SAFTY_CONTROL_BAT 强制充电取反逻辑;175-181 行的调试 printf 与误差补偿代码。
  11. 无应用层校验:CAN 帧不含 CRC 或序号,仅依赖 CAN 控制器硬件 CRC/自动重发;数据有效性完全靠周期性刷新帧体现,单帧错误无法在应用层识别。
  12. 魔法数字:50 ms 下发周期(620)、500 ms 爬坡调用周期(228)、10 ms 收帧时间窗(774)、20000/15000/12000 W 额定功率、u8_ups_enable = 0x02(495)、u8_bat_chg_state = 2(377)均直接硬编码。