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

alphaess_comm_can.c

源文件路径:midware/inverter/alphaess/alphaess_comm_can.c 所属模块:midware/inverter/alphaess(AlphaESS 逆变器 CAN 通信 + VAVE 电表芯片校准) 代码行数:1403

1. 文件职责

本文件实现 AlphaESS 储能逆变器的 CAN 侧通信主逻辑,是 EMS 与逆变器 DSP 之间除 SCI(串口)之外的第二条控制通道。它承担四件事:一是完成 EMS→逆变器的身份握手(0x181 "AlphaESS"),二是把 g_t_inv_info 中已经算好的功率/电池/备份/UPS 指令周期性组帧下发,三是接收并解析逆变器与 VAVE 电表芯片回传的 0x263~0x269、0x301~0x310 运行数据,把电网/光伏三相电压、电流、功率填进 g_t_meter.t_grid_info.t_data / g_t_pv_info.t_data,四是承载 DSP 电表芯片的整定/校准参数(0x340~0x350、0x360、0x361)下发与回读。

它处于「逻辑层算出功率 → CAN 下发 → 逆变器执行 → CAN 回传实际值 → 逻辑层闭环」这条数据流的中段,被 task_inverter_can(inverter.c:1199)以 30 ms 周期调用;alphaess_inverter_can_data_check() 则在同一次任务里被紧接着调用,负责把 CAN 收到的数据"搬运"到全局电表结构并置位相应的重发标志。

2. 关键数据结构与宏

名称类型说明
ALPHAESS_INV_DEBUG宏调试打印总开关,本文件内为 (0),默认关闭
ALPHAESS_EMS_INV_IDCODE_PACK (0x181)宏EMS→INV 握手帧 ID,数据域为 ASCII "AlphaESS"
ALPHAESS_EMS_CTRL_BACKUP (0x184)宏EMS→INV 备份/UPS 使能控制帧
ALPHAESS_EMS_INV_SET_CTRL_MODE (0x190)宏EMS→INV 控制模式帧(单相/三相不平衡)
ALPHAESS_GINLONG_EMS_CTRL_INV_AC_POWER1 (0x192)宏EMS→INV AC 有功功率给定(总有功 + A/B/C 相)
ALPHAESS_EMS_CTRL_INV_AC_POWER2 (0x193)宏EMS→INV 直流侧充/放电限值 + 软启动 + 电池连接状态 + 电池总电流
ALPHAESS_EMS_READ_BAT_CHG_DISCHG (0x194)宏EMS→INV 电池最大充/放电功率(变化时才发)
ALPHAESS_EMS_SET_INV_CTRL_ARG1 (0x195)宏EMS→INV MPPT 百分比 + 备份输出频率偏移
ALPHAESS_EMS_SEND_BAT_STATE (0x1A0)宏EMS→INV 电池运行状态(电压/继电器/SOC/强充使能)
ALPHAESS_EMS_SEND_BAT_PROTECT (0x1A1)宏EMS→INV 电池保护参数(放电截止电压/电流、充电截止电压/电流)
ALPHAESS_INV_EMS_IDCODE_PACK (0x201)宏INV→EMS 握手应答帧 ID
ALPHAESS_INV_EMS_RETURN_RUN_INFO_PACK4~PACKA (0x263~0x269)宏INV→EMS 运行数据回传帧,共 7 帧
DSP_METER_RUN_INFO_PACK1~PACK10 (0x301~0x310)宏VAVE 电表芯片回传运行数据(正反向电能、三相电压/电流/有功/视在、版本号)
DSP_METER_CALI_DATA_PACK0~PACKA (0x340~0x34A)宏VAVE 电表芯片校准数据下行帧(电压/电流/PV 电流/放电功率/充电功率校准点、校准模式、CT 自检)
DSP_METER_CALI_DATA_PACKB (0x350)宏双向帧:下行为相数据校准,上行为 0x350 回读帧
DSP_METER_CALI_DATA_PACKC (0x360)宏设置储能逆变发电量 / 买电量
DSP_METER_CALI_DATA_PACKD (0x361)宏设置并网机发电量
ZERO_CONFIRM_CNT宏值为 5,连续 N 次采样为 0 才判定为真实 0(毛刺滤波阈值)
zero_glitch_filter_t结构体采样值偶发 0 毛刺滤波器状态:s32_last_value + u8 zero_cnt
ALPHAESS_OPERATION_MODE_E枚举逆变器工作模式:上电/等待/旁路/自检/并网/离网/自测试/故障/升级/关机/直流模式,另含 ALPHAESS_POWER_ON_E_2 = 255
SEND_CALIBRATION_DATA_FLAG联合体16 位校准发送标志位域(电压/电流/PV 电流/放电功率 0-1/PV 放电功率 0-1/充电功率 0-1/校准模式/CT 自检/相位校准/发电量设置),含 bit14/bit15 保留位
DSP_METER_CALIBRATION_DATA结构体DSP 电表全部可写参数容器,含 R/S/T 三相各类校准点、校准模式、CT 自检、相位数据、储能逆变与并网机发电量;#pragma pack(1) 紧凑布局
CAN_MSG_TYPEDEF_T结构体外部类型(hardware/can/can0.h:21),CAN 报文描述:frame_id / frame_type / frame_len / frame_data[8] / reading_flag
METER_DATA_T结构体外部类型(midware/meter/meter.h:189),标准电表数据(电压/电流/有功/无功/功率因数/频率/电能)
CAN_FT_DATA宏CAN 数据帧类型标志(can0.h)
cWaitMode/cNormalMode/cUPS/cFaultMode/cCheckMode/cSelfTest/cByPassMode/cDCMODE宏外部宏(inverter.h:20~27),逆变器统一运行模式码

3. 函数清单

函数名作用域说明
alphaess_comm_init_can全局CAN 初始化与 CAN 侧标志复位;预分频不为 4 时重新 can1_mode_init(4)
ems_ate_reset_dsp全局ATE 复位 DSP:连发 AC 功率、DC 限值、电池可用功率、电池状态四类帧
CAN_Send_MsgstaticCAN 发送封装,保证相邻两帧至少间隔 1 个 tick,转发 can1_send_standard_pack
parse_alphaess_work_mode全局把 ALPHAESS 工作模式码翻译为项目统一运行模式码(cWaitMode 等)
send_ems_inv_idcode_cmdstatic发送 0x181 握手帧,数据域 ASCII "AlphaESS"
set_inv_ctrl_modestatic发送 0x190 控制模式帧(单相=1 / 三相不平衡=2),成功后清标志
send_ac_ctrl_inv_power1static组发 0x192:总有功 + A/B/C 三相有功,含放电/充电额定功率限幅
send_dc_ctrl_inv_power_2static组发 0x193:电池充/放电限值、软启动、电池连接状态、电池总电流
send_ems_read_bat_powerstatic组发 0x194:电池最大充/放电功率,仅当值变化或强制发送时发出
send_bat_run_infostatic组发 0x1A0:电池电压、继电器状态、SOC、强充使能
send_bat_protect_infostatic组发 0x1A1:放电截止电压/电流、充电截止电压/电流(4 × s16)
send_bat_statestatic组合调用 send_bat_run_info + send_bat_protect_info
ems_ctrl_inv_backupstatic发送 0x184 备份/UPS 使能帧,成功后清 u8_set_backup_enable_flag
ems_ctrl_inv_inv_ctrl_arg1static发送 0x195 MPPT 百分比 + 备份输出频率偏移,成功后清标志
ems_send_340packstatic发送 0x340:R/S/T 相电网电压校准点,发完清标志并清缓冲区
ems_send_341packstatic发送 0x341:R/S/T 相电网电流校准点
ems_send_342packstatic发送 0x342:R/S/T 相 PV 电流校准点
ems_send_343packstatic发送 0x343:R/S/T 相电网放电功率校准点 0
ems_send_344packstatic发送 0x344:R/S/T 相电网放电功率校准点 1
ems_send_345packstatic发送 0x345:R/S/T 相 PV 放电功率校准点 0
ems_send_346packstatic发送 0x346:R/S/T 相 PV 放电功率校准点 1
ems_send_347packstatic发送 0x347:R/S/T 相电网充电功率校准点 0
ems_send_348packstatic发送 0x348:R/S/T 相电网充电功率校准点 1
ems_send_349packstatic发送 0x349:校准模式、复位校准模式、清除电量指令
ems_send_34Apackstatic发送 0x34A:CT 自检模式使能
ems_send_350packstatic发送 0x350:R/S/T 相校准数据 + 校准模式触发
ems_send_360packstatic发送 0x360:储能逆变发电量(高/低字节)与买电量(高/低字节)
ems_send_361packstatic发送 0x361:并网机发电量(高/低字节)
ems_send_dsp_meter_calibration_datastatic扫描 send_cali_data_flag 的 14 个 bit,按 bit 序号分派到 ems_send_3xxpack
alphaess_inverter_comm_can全局本文件主入口:握手 + 周期下发 + CAN 接收解析 + 链路丢失检测
alphaess_inverter_can_data_check全局CAN 侧数据后处理:把 DSP 电表数据搬到全局电表结构,检测三不平衡/UPS 变化置重发标志
zero_glitch_filter全局采样值偶发 0 毛刺滤波:非 0 直接更新,0 则沿用上次值,连续 5 次 0 才放行真 0
zero_glitch_filter_reset全局复位毛刺滤波器状态

4. 核心逻辑解析

4.1 CAN 帧格式与总线配置

全部报文使用 CAN 2.0 标准帧(11 位 ID),frame_len 恒为 8,数据域多字节字段一律 大端(高字节在前)。命令字即 CAN ID 本身,没有额外帧头与长度域;帧的语义完全由 ID 区分,因此 0x350 这类 ID 是双向复用的(下行校准数据、上行回读)。

总线由 alphaess_comm_init_can() 配置:can1_mode_init(4)(can1.c:47),预分频 = 4,BS1 = 16TQ、BS2 = 4TQ、SJW = 3TQ,注释标明 250 kbps。同时初始化四个 CAN 侧标志位(u8_ems_inv_idcode_flag、u8_set_inv_ctrl_mode_flag、u8_set_backup_enable_flag、u8_set_inv_ctrl_arg_flag1 全置 TRUE)并预置 u16_mppt_percentage = 10000(100.00%)、u8_ups_enable = 0x02。

CAN_Send_Msg()(:98)是唯一的发送出口,用一个 static tick 保证 相邻帧间隔 ≥ 1 tick(1 ms):

if(abs(OSTimeGet() - s_u32_send_tick) < 1) OSTimeDlyHMSM(0, 0, 0, 1);
s_u32_send_tick = OSTimeGet();
return can1_send_standard_pack(TxMsg);

4.2 链路建立状态机(握手)

alphaess_inverter_comm_can() 内部是一个以 g_t_alphaess_comm_info.u8_inv_parameter_init_flag 为闸门的隐式状态机:

u8_inv_parameter_init_flag 由 alphaess_comm_usart.c:119 / :3659 / :4424 置 TRUE,在 :5112 被清除,是跨文件的同步点。

4.3 AC 功率下发与限幅算法(0x192)

send_ac_ctrl_inv_power1()(:229)先判 ATE 模式:

随后做符号相关限幅:

if (l_s16_ac_set_actpower > 0 && abs(...) > u32_ac_rate_discharge_power)  → 取 +放电额定
else if (l_s16_ac_set_actpower <= 0 && abs(...) > u32_ac_rate_charge_power) → 取 -充电额定

注意三相独立值 l_s16_inv_ac_set_a/b/c_real_actpower 没有参与限幅,只对总有功限幅,这是一个潜在的一致性缺口。

4.4 电池状态与保护参数下发(0x1A0 / 0x1A1 / 0x193 / 0x194)

4.5 校准数据分派(0x340~0x361)

ems_send_dsp_meter_calibration_data()(:978)是本文件的一个小型"命令分发器",for (i = 0; i < 13; i++) 扫描 dsp_meter_calibration_data.send_cali_data_flag.u16_all 的第 i 位,命中则 switch(i) 调对应 ems_send_3xxpack()。每个 pack 函数统一模式:填 ID → 填 8 字节大端数据 → CAN_Send_Msg → 若成功清对应 bit → memset 清零数据段。

bit 序号到帧的映射(:986~:1040): 0→340, 1→341, 2→342, 3→343, 4→344, 5→345, 6→346, 7→347, 8→348, 9→349, 0x0A→34A, 0x0B→350, 0x0C→360, 0x0D→361。

主循环里对这套标志做了两次处理:函数入口处(:1063)先统一发送并把 u16_all 清零一次;运行态分支内(:1118)又判断了一次 u16_all(此时恒为 0,属冗余代码)。

4.6 接收解析与数据落地

接收端是一个 while(get_can1_standard_pack(&l_t_inveter_info) == TRUE) 的排空循环(:1128),逐帧 switch(frame_id):

帧 ID落地目标
0x201清 u8_ems_inv_idcode_flag、清 u8_inverter_can_comm_lost_flag、刷新 s_u32_can_rcv_tick
0x263u16_grid_volt_a/b、s32_grid_actpower_a/b
0x264u16_grid_volt_c、s32_grid_actpower_c/t、u16_grid_freq
0x265u16_backup_volt_a/b、s32_backup_actpower_a/b
0x266u16_backup_volt_c、s32_backup_actpower_c/t、u16_backup_freq
0x267s32_backup_apparentpower_a/b/c/t
0x268s32_inv_actpower_a/b/c
0x269s32_inv_actpower_t、u16_inv_freq、u8_run_mode(经 parse_alphaess_work_mode)、s16_bat_power、u8_inv_bat_breaker_statuse
0x301dsp_meter_grid_data.u32_energy_output[PHASE_T] / u32_energy_input[PHASE_T](32 位四字节拼接)
0x302/0x303dsp_meter_grid_data.u16_volt[PHASE_A/B/C],经毛刺滤波;0x303 另填 u16_frequent
0x304/0x305dsp_meter_grid_data.s16_current[A/B/C]
0x306dsp_meter_pv_data.s16_current[A/B],并用 电压 × 电流 / 1000 反算 s32_actpower[PHASE_A]
0x307dsp_meter_pv_data.s16_current[PHASE_C]
0x308/0x309dsp_meter_grid_data.s32_actpower[A/B] 与 [C]/[PHASE_T]
0x30A/0x30B空实现(break),未使用
0x30C/0x30Ddsp_meter_pv_data.s32_actpower[A/B/C],经毛刺滤波;0x30D 另填 [PHASE_T]
0x30F空实现
0x3108 字节写入 g_t_meter.t_grid_info.u8_vave_software_version[0..7]
0x350回读 s16_calib_r/s/t_data 与 u16_calib_mode_trigger

u8_run_mode 的更新有双重门控(:1188):u8_inverter_comm_lost_flag == FALSE 且 is_inverter_firm_download_finished() == FALSE 时才允许刷新运行模式,避免升级期间模式跳变。

排空循环有一个看门狗(:1302):if(abs(l_u32_inv_recv_tick - OSTimeGet()) > 10) break;,即处理累计超过 10 tick 仍未排空就强制退出,防止 CAN 持续灌帧饿死同任务里的其他处理。

4.7 链路丢失与自愈时序

15 s 无 0x201/0x263~0x269 → alphaess_comm_init_can()(重新配 CAN + 复位标志)
55 s 仍无                  → g_t_inv_info.t_run_info.u8_inverter_can_comm_lost_flag = TRUE

alphaess_comm_init_can() 会把四个标志重新置 TRUE,因此重连后会自动重跑"握手 → 一次性命令 → 周期下发"流程。注意阈值判断是嵌套的:只有 ≥15000 才继续判 ≥55000,所以 55 s 的判据一定在 15 s 之后才生效。

4.8 alphaess_inverter_can_data_check():数据搬运与标志置位(:1330)

  1. 若 u8_ems_inv_idcode_flag == TRUE(还没握手)直接 return。
  2. 仅在 BOARD_EMS3_6_3_E / BOARD_EMS3_6_4_E 两种板型上,把 DSP 电表数据搬到全局电表:
    • 未插电网电表(meter_get_meter_or_ct(GRID_METER) == FALSE)时,若逆变器处于 cUPS 则整个 METER_DATA_T 清零,否则 memcpy dsp_meter_grid_data;
    • 未插 PV 电表时,若系统模式为 DC_SYSTEM_MODE 则清零,否则 memcpy dsp_meter_pv_data。
  3. 用 static s_u8_inv_three_unbalance_mode 做边沿检测,三不平衡模式变化时置 u8_set_inv_ctrl_mode_flag = TRUE(下一轮会重发 0x190)。
  4. 用 static s_t_run_control_cmd.u8_ups_enable 做边沿检测,UPS 使能变化时置 u8_set_backup_enable_flag = TRUE(重发 0x184)。

4.9 定时/调度与 uCOS-II 协作

调用点任务优先级循环节拍说明
alphaess_inverter_comm_can()task_inverter_can(inverter.c:1249)15(app_cfg.h:66)OSTimeDlyHMSM(0,0,0,30) = 30 ms等 g_t_fast_dispatch_flag 的 PEND_INV_CALCULATE_DONE 事件标志,20 tick 超时
alphaess_inverter_can_data_check()同上(inverter.c:1250)1530 ms紧随其后
alphaess_comm_init_can()inverter_init()(inverter.c:90)、alphaess_comm_usart.c:5692、本文件自愈路径——三处初始化

时间基准为 OSTimeGet(),OS_TICKS_PER_SEC = 1000(UCOSII/uCOS-CONFIG/os_cfg.h:51),即 1 tick = 1 ms。

任务与逻辑层的同步:task_inverter_can 里 g_t_alphaess_comm_info.u32_ems_send_inv_ctrl_power_tick1 = 0(inverter.c:1212)在收到 PEND_INV_CALCULATE_DONE 时被清零,等于"逻辑层刚算完,立刻允许下一轮功率下发",形成 20 ms 的功率刷新节拍;TASK_INVERTER_CAN_PRIO 也可被 OSTimeDlyResume 主动唤醒(inverter.c:1186)。本文件不直接使用信号量或消息邮箱,全部通过全局标志位 + tick 差值实现"软同步"。

4.10 关键全局变量读写汇总

变量方向触发条件
dsp_meter_calibration_data(本文件定义,:27)写(清标志/清数据)各 ems_send_3xxpack 发送成功后清对应 bit;ems_send_350pack 回读分支写 s16_calib_*_data
s_u32_can_rcv_tick(:28)写收到 0x201 或任一 0x263~0x269 刷新;0x30x 电表帧不刷新
dsp_meter_grid_data / dsp_meter_pv_data(:29~:30)读/写接收解析写入;alphaess_inverter_can_data_check 读出并 memcpy 到 g_t_meter
g_t_alphaess_comm_info.u8_ems_inv_idcode_flag写初始 TRUE,收 0x201 后置 FALSE
g_t_alphaess_comm_info.u32_ems_inv_idcode_tick / u32_ems_send_inv_ctrl_power_tick1写每次成功发送后刷新
g_t_alphaess_comm_info.u8_set_inv_ctrl_mode_flag / u8_set_backup_enable_flag / u8_set_inv_ctrl_arg_flag1写(清)对应帧发送成功后清;由 alphaess_inverter_can_data_check、inverter_set_mppt_percentage(inverter.c:7214)等外部置位
g_t_inv_info.t_run_info.*写接收解析写入电网/备份/逆变器三相电压电流功率、运行模式、电池功率、空开状态
g_t_inv_info.t_run_info.u8_inverter_can_comm_lost_flag写0x201 到达清 FALSE;55 s 超时置 TRUE
g_t_inv_info.t_config_data.u32_ac_rate_* / u32_dc_rate_*读0x192/0x193/0x194 限幅与 ATE 模式取值
g_t_meter.t_grid_info.u8_vave_software_version[9]写0x310 帧写入 8 字节

5. 对外接口

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

符号使用者
alphaess_inverter_comm_can()task_inverter_can(inverter.c:1249)
alphaess_inverter_can_data_check()task_inverter_can(inverter.c:1250)
alphaess_comm_init_can()inverter_init()(inverter.c:90)、alphaess_comm_usart.c:5692、alphaess_upgrade.c:534/583
ems_ate_reset_dsp()task_inverter_can(inverter.c:1255,由 u8_ate_reset_dsp_flag 触发)
parse_alphaess_work_mode()声明于本文件头,实际调用点在本文件 :1191(alphaess_comm_usart.c:1259 中为注释掉的旧调用)
zero_glitch_filter() / zero_glitch_filter_reset()目前仅本文件内使用(全工程搜索无其他调用者),属"导出但未被外部使用"
dsp_meter_calibration_data通过 alphaess_comm_can.h:206 导出,供 HMI/Modbus 等填写校准参数
s_u32_can_rcv_tick / dsp_meter_grid_data / dsp_meter_pv_datastatic,不导出

本文件依赖的外部符号

来源文件使用内容
hardware/can/can1.ccan1_send_standard_pack、get_can1_standard_pack、get_can1_prescaler、can1_mode_init
hardware/can/can0.hCAN_MSG_TYPEDEF_T、CAN_FT_DATA
midware/meter/meter.hMETER_DATA_T、PHASE_A/B/C/T、GRID_METER、PV_METER、meter_get_meter_or_ct、g_t_meter
midware/inverter/inverter.hg_t_inv_info、cWaitMode 等模式宏
midware/inverter/pvcharger.cpvcharger_read_bat_curr/volt/soc、pvcharger_read_bat_relay_state/connect_state、pvcharger_read_bat_max_charge_power/max_dischg_power、pvcharger_read_bat_dischg/charge_cut_off_volt、pvcharger_read_bat_max_dischg/charge_current
midware/inverter/parallel.hparallel_read_ac_total_set_inv_power、parallel_read_ac_three_phase_set_inv_power、parallel_read_ac_*_set_bat_charge/dischg_limit_power、parallel_read_ac_*_set_bat_forced_charge_en
midware/inverter/alphaess/alphaess_comm_usart.hg_t_alphaess_comm_info(ALPHAESS_TABLE_T)
net/inv_download.his_inverter_firm_download_finished()
logic/logic.hg_t_logic_info.t_config_data.u8_system_mode、DC_SYSTEM_MODE
system/board_init/board_init.hget_board_type、BOARD_EMS3_6_3_E、BOARD_EMS3_6_4_E
UCOSIIOSTimeGet、OSTimeDlyHMSM、abs()

6. 依赖关系

依赖

alphaess_comm_can.h、alphaess_comm_usart.h、logic.h、inverter.h、can1.h、inv_download.h、system_config.h、meter.h、board_init.h、bms.h(.c 头部 :13~:22)。

被依赖

7. 备注与风险点

  1. 头文件前向声明不完整(alphaess_comm_can.h:34~52):set_inv_ctrl_mode、send_bat_run_info、send_bat_protect_info、ems_send_34Apack、ems_send_350pack、ems_send_360pack、ems_send_361pack 都没有前向声明,靠"定义先于使用"侥幸编译通过。属版本遗留,建议补齐。
  2. handle_alphaess_inv_set_battery_shutdown_flag 是死声明(alphaess_comm_can.h:213):全工程搜索不到定义,任何引用都会链接失败。
  3. 校准点清除时机可疑:ems_send_340pack 等函数在 CAN_Send_Msg 之后无条件 memset 清零数据段(如 :590),即使发送失败也会清掉待发值,只有标志位在失败时保留 —— 下一轮重发会发出全 0 值。ems_send_34Apack(:863)和 ems_send_361pack 的最后一次 memset(&u16_pgu_gen_l,0,4) 范围也与帧长度不匹配(后者只清 4 字节,前者完全不清)。
  4. ems_send_344pack 清错了标志位(:706):发送 0x344(放电功率校准点 1)后清的是 u8_send_pchg_calibration1_flag(充电功率校准 1,对应 0x348),而 0x344 对应的应是 u8_send_pdischg_calibration1_flag。这会导致 0x344 的 bit 永不清除/0x348 的 bit 被误清。
  5. 三相独立功率未限幅(:244~:246):l_s16_inv_ac_set_a/b/c_real_actpower 直接由 parallel_read_ac_three_phase_set_inv_power() 装入 CAN 帧,没有与 u32_ac_rate_discharge_power 比较,存在越界给定风险。
  6. abs() 与无符号混用:send_ac_ctrl_inv_power1 里 abs(l_s16_ac_set_actpower) 依赖 l_s16_* 为有符号;但 :239 把 u32_ac_rate_discharge_power 赋给 s16 l_s16_ac_set_actpower,额定功率超过 32767 W 会截断。
  7. 链路丢失判据只看 0x201/0x263~0x269(:1308):s_u32_can_rcv_tick 在 0x301~0x310 电表帧分支里没有被刷新,若逆变器只回电表帧而不回运行帧会被误判为丢失。另外注释写"超过15S没有接收到0x269帧数据",与实际(任一运行帧都算)不符。
  8. g_t_meter.t_grid_info.u8_vave_software_version[9] 写入 8 字节(:1286~:1289):下标 8 保留为 0 是合理的,但没有任何长度断言。
  9. send_ems_read_bat_power 的缓存与返回语义耦合:返回值被上层反转为 s_u8_send_bat_parameter_flag(:1123),且 l_u16_bat_pmaxchg 是 u16 而 pvcharger_read_bat_max_charge_power() 返回 s32(pvcharger.h:91),负值或超大值会被截断。
  10. send_bat_run_info 的 SOC 缩放:pvcharger_read_bat_soc()/10 后压进单字节 frame_data[3](:415、:443),无范围钳位,SOC 原始值偏大时会溢出到低 8 位。
  11. u8_inv_parameter_init_flag == TRUE 时完全不下发(:1317~:1320):这是有意设计(串口初始化期间 CAN 只监听),但若串口流程卡死在该状态,CAN 侧将永久失去控制能力,缺少超时兜底。
  12. ems_send_dsp_meter_calibration_data 的循环边界为 i < 13(:980):结构体位域实际定义了 14 个有效标志(bit0~bit13,即 0x0D 也有效),13 恰好覆盖 i = 0..12,因此 bit13(设置并网机发电量 0x361)永远扫不到;case 0x0D: ems_send_361pack(); 是不可达代码。这是明确的差一错误。
  13. 主循环里对 send_cali_data_flag.u16_all 的重复判断(:1063 与 :1118):第二次判断时该值已被 :1067 清零,属冗余分支。
  14. ems_ctrl_inv_backup 未在开头前向声明(:40 有声明,但 set_inv_ctrl_mode 等没有,见第 1 条);INOVANCE_INV_DEBUG_CAN 宏在 set_inv_ctrl_mode 里被使用(:207),与本文件厂商无关,是复制粘贴残留。