set_comm_usart_s5.c
senergys5_data_check_handle4.7 能量统计算法4.8 故障码映射4.9 安规码双向映射4.10 开关机、EPO 与复位4.11 uCOS-II 协作方式4.12 服务器透传寄存器通道5. 对外接口5.1 本文件导出(供外部调用)5.2 本文件依赖的外部函数/变量6. 依赖关系7. 备注与风险点源文件路径:
midware/inverter/set/set_comm_usart_s5.c所属模块:midware/inverter/set(盛能杰 Senergy S5 / S5-II / M10 逆变器串口通信驱动) 代码行数:6282
1. 文件职责
本文件是 EMS 与盛能杰(Senergy,代码里统一缩写为 senergys5 / SET_TYPE)储能逆变器之间的 USART2 串口应用层协议实现。它把 Modbus RTU 帧的组帧、CRC 校验、收发时序、超时重试全部收敛到本文件,向上对 midware/inverter/inverter.c 的 task_inverter 任务暴露少数几个入口(senergys5_inverter_comm、senergy_model_recognition、senergy_comm_init 等)。
在数据流中它处于「EMS 逻辑层 ↔ 逆变器 DSP」的中间环节:一方面把从逆变器读到的实时运行数据(PV 电压电流、并网功率、电池电流、故障字、降载原因、安规参数、版本号)翻译成 g_t_inv_info 里的统一字段供 logic/hmi/net 使用;另一方面把 EMS 侧的控制意图(开关机、UPS 使能、电池加热、安规码、PV 连接类型、孤岛检测、GFCI、缓升缓降速率、并网法规曲线等)写成寄存器下发。
它由 task_inverter(midware/inverter/inverter.c:1090)在每个调度周期同步调用,不是中断驱动:函数内部自己发帧、自己轮询接收、阻塞式等待,因此整个任务的节奏由本文件决定。固件升级路径上,本文件让位给 set_upgrade_s5.c(senergys5_inverter_comm 里检测到升级标志即只跑升级处理)。
2. 关键数据结构与宏
本文件自身定义/主要使用的类型与宏如下(类型本体多在 set_comm_usart_s5.h 与 midware/inverter/inverter.h)。
| 名称 | 类型 | 说明 |
|---|---|---|
SENERGYS5_DAT_PCK_INV_T | 结构体 | 通用串口包:u8_slave_addr / u8_func_code / u8_data[100] / u16_len_data。本文件未直接使用,由头文件导出 |
SENERGYS5_ON_OFF_E | 枚举 | SENERGYS5_ON = 0、SENERGYS5_OFF = 1,对应 0x6001 开机关机寄存器 |
MPPT_CTRL_E | 枚举 | MPPT_DEFAULT = 0 交给 DSP 自控、MPPT_STOP |
SENERGYS5_ABNORMAL_CODE_E | 枚举 | Modbus 异常码 1~4(功能码错、寄存器数过多、地址越界、值越界) |
SENERGYS5_HYBRID_WORK_MODE_E | 枚举 | 逆变器内部工作模式 0x2100:自发自用/馈电优先/分时/备电/电池放电 |
SENERGYS5_PV_PANEL_CONNECT_TYPE | 枚举 | 0x5109 PV 连接类型:独立/并联/自动检测 |
SENERGYS5_INV_PRODUCTION_TYPE_E | 枚举 | 0x1A5A 产品型号:ON_GRID/AC_NO_LOAD/HYBRID_NO_LOAD/HYBRID_LOAD/AC_LOAD。HYBRID_LOAD 是本文件型号识别成功的判据 |
SENERGYS5_INV_WORK_MODE_E | 枚举 | 0x101D 运行模式:INITIAL_MODE 0x00 / STANDBY 0x01 / ONLINE 0x03 / UPS 0x04 / FAULT 0x05 / SHUTDOWN 0x09 |
SENERGY_SERIES_E | 枚举 | SENERGY_S5_SERIES = 0(9600bps)、SENERGY_M_SERIES = 1(57600bps),用于波特率自适应轮询表 |
SET_HEAT_TABLE_T | 结构体 | 电池加热缓存:加热指令、加热继电器、加热电压(0.1V)、加热电流(0.01A);本文件定义 static SET_HEAT_TABLE_T s_t_heat_data |
SENERGYS5_SYSTEM_STATUS_U | 联合体/位域 | 0x104A 系统状态:工作模式 3bit、EPS 继电器、电池状态 2bit、MPPT/PV 异常、市电异常 |
SENERGYS5_LOAD_DE_RATING_U | 联合体/位域 | 0x1036 降载原因:NTC 高温、PV 输入电压、风扇堵转、软起、市电高频/低压/高压、P/Q 指令限载、最大输出电流等 |
SENERGYS5_INV_SELF_TEST_POGRESS_U | 联合体/位域 | 0x5211 意大利安规自检结果:OVP/UVP/OFP/UFP 各两级 + abnormal |
SENERGYS5_ERROR_U | 联合体/位域(64bit) | 故障字 1~4(0x101E/0x101F/0x1020/0x105F),含逆变过流、继电器异常、母线欠压、绝缘异常、电池 OVP/UVP、EPS 短路/过载等 |
SENERGYS5_ERROR_U2 | 联合体/位域(16bit) | 故障字 6(0x2E50 / 0x2F18),核心位 u8_nn_short_error(GRID-EPS 中性线短接) |
SENERGYS5_SYSTEM_SET_ENABLE_FLAG_U | 联合体/位域(32bit) | 4 组「写参数后回读校验」状态字:并网法规基本参数、法规曲线、系统功能 1、系统功能 3。每 bit 表示某项待下发;word.xxx_all(4 个字节)为该组是否全部完成 |
SENERGYS5_INV_CONTROL_INFO_T | 结构体 | 本文件定义的本地控制信息表:混合工作模式、开关机、AC 功率限制百分比、充放电电流、MPPT 启停、DCDC 唤醒计数、复位计数、PV 连接类型、过欠频曲线类型、初始化标志、重连缓升速率 |
SENERGYS5_INV_DEVICE_INFO_T | 结构体 | 本文件定义的本地设备信息表:额定 PV 开路电压/MPPT 路数/标称电压频率/额定额定有功视在功率/产品型号、运行模式、加热状态、降载原因、有功无功视在功率、功率因数、系统状态、自检结果、HS 温度、安规相关状态、故障录波、充放电电流上限、DCDC 步数/状态、AC 最大有功/视在功率 |
SENERGYS5_COMM_INFO_T | 结构体 | 本文件定义的通信任务表:波特率自适应表 adaptive_polling_search[2]、各类查询标志位、调试寄存器缓存 u16_ems_read_inv_debug[6]、各类 tick、u16_query_inv_check_flag、u8_inv_old_version_flag |
SENERGYS5_QUERY_INV_REGISTER_T | 结构体 | 定义在 midware/inverter/inverter.h:1920,本文件定义其实例 senergys_query_inv_register(服务器远程透传读写逆变器寄存器用):u8_start_en / u8_modbus_flag / u8_send_data[10] / u8_rend_data[10] / u8_read_flag |
SENERGYS5_SLAVE_ADDRESS | 宏 | 从机地址 0x01 |
SENERGYS5_FUNC_READ_CODE / WRITE_SINGLE_CODE / WRITE_MULTI_CODE | 宏 | 功能码 0x03 / 0x06 / 0x10 |
SENERGYS5_FUNC_READ_ABNORMAL_CODE / WRITE_SINGLE_ABNORMAL_CODE / WRITE_MULTI_ABNORMAL_CODE | 宏 | 异常响应功能码 0x83 / 0x86 / 0x90 |
SENERGYS5_FUNC_FACTORY_ATE_CODE | 宏 | 厂内 ATE 功能码 0x80 |
SENERGYS5_FUNC_INSIDE_CODE | 宏 | 内部指令功能码 0x90 |
SENERGYS5_INSTRUCT_* | 宏 | ATE 指令码 0x04~0x13(读 DSP 变量/读写 EEPROM/复位/NN/NPE 短接);内部指令码 0x00~0x93(最大充放电电流、截止电压、BMS 状态、控制模式、宽电压使能) |
SENERGYS5_ADDR_* / SENERGYS5_NUM_* | 宏 | 全部寄存器地址与读/写寄存器数量,成对出现在头文件 104~268 行;写寄存器:0x6001 开关机、0x2115 Grid 充电、0x211C EPS、0x6006 清 DSP EEPROM、0x2E06 DSP 重启、0x2E09/0x2F0E/0x2F08 加热、0x2E04 电流校准;读寄存器:0x1001 实时数据 1(0x64/0x4A)、0x2E50 实时数据 2、0x2F00 实时数据 3(0x1C/0x15)、0x1A00 SN/型号、0x1A19 版本、0x1A5A 产品型号、0x5210 自检、0x2F2D 调试、0x2157/0x2E01 系统功能 2/3、0x5000 法规基本参数、0x5030 法规曲线、0x5100 系统功能 1 |
SENERGYS5_INV_RECV_BUFFER_SIZE | 宏 | 接收缓冲 256 字节,配合全局 g_u8_senergys5_inv_recv_buf[SENERGYS5_INV_RECV_BUFFER_SIZE](本文件 34 行定义) |
SENERGYS5_TIME_30MS / 100MS / 250MS / 1S / 1M | 宏 | 30 / 100 / 250 / 1000 / 60000,配合 OSTimeGet() 做周期调度 |
SENERGYS5_MAX_REGISTER_NUM | 宏 | 单帧最多 100 个寄存器(头文件 54 行) |
SENERGYS5_DAT_PCK_INTERVAL_INV | 宏 | 两帧之间最小间隔 100ms,保证从机解析时间 |
MODEL_QUERY_TIMEOUT_CNT | 宏 | 型号查询超时阈值 20 次(约 2s) |
SENERGYS5_INV_RATED_POWER_* | 宏 | 5kW / 5.5kW / 6kW / 9999W / 10kW 额定功率档位(注意 SENERGYS5_INV_RATED_POWER_5_5KW、_6KW、_10KW 定义后未被本文件使用) |
SENERGYS5_INV_BAUDRATE_LOW / HIGH | 宏 | 9600 / 57600,波特率自适应用 |
SENERGYS5_*_DEBUG 系列 | 宏 | 全部为 0,编译期裁剪调试打印 |
本文件的全局对象(第 34~52 行):
u8 g_u8_senergys5_inv_recv_buf[256]— 唯一的接收缓冲,全文件共享。static SET_HEAT_TABLE_T s_t_heat_data— 电池加热参数变化检测缓存。SENERGYS5_COMM_INFO_T s_senergys5_task_tab— 头文件 extern,初始值把波特率自适应表设为{S5系列,9600}、{M系列,57600}。SENERGYS5_INV_DEVICE_INFO_T s_senergys5_device_info_tab、SENERGYS5_INV_CONTROL_INFO_T s_senergys5_control_info_tab、SENERGYS5_ERROR_U s_u_senergys5_error_mapping_table、SENERGYS5_ERROR_U2 s_u_senergys5_error_mapping_table2、SENERGYS5_SYSTEM_SET_ENABLE_FLAG_U s_senergys5_set_info— 均 extern 导出。u8 u8_Safety_charge_flag = 1— 澳规 CEC 默认参数只装载一次的闩锁标志。SENERGYS5_QUERY_INV_REGISTER_T senergys_query_inv_register— 服务器透传寄存器通道。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
senergys5_inv_write_data | static | 发送一帧到 USART2,并用静态 tick 保证两帧间隔 ≥100ms,发送前清接收缓冲 |
senergys5_inv_recv_data | static | 非阻塞读一帧并做 Modbus CRC16 校验,校验失败把长度置 0 |
senergys5_inv_recv_data_set_inv_satey | static | 带超时的阻塞式收帧(写安规码专用,timeout 由调用方给,例如 8000ms) |
senergys5_restart_eeprom_recv_mdsp | static | 复位 EEPROM 专用收帧,超时返回 FALSE,校验失败也返回 TRUE 但长度置 0 |
senergys5_ems_send_query_data_to_inv | static | 组 0x03 读寄存器帧(8 字节)并发送 |
senergys5_ems_send_set_single_data_to_inv | static | 组 0x06 写单个寄存器帧(8 字节)并发送 |
senergy_comm_init | 全局 | 按逆变器子型号(SUB_S5/SUB_S5_II/SUB_M10)做控制标志与控制信息初始化 |
senergy_comm_init_usart | 全局 | 按子型号初始化 USART2 波特率(S5 用 9600,S5-II/M10 用 57600)并清缓冲 |
senergy_comm_status_check | static | 用升级模块的 senergys5_upgrade_recv_mdsp 探测 0xA5 / 0x55 应答,判断 DSP 是否在线 |
senergy_model_recognition | 全局 | 型号识别状态机:查询型号,成功则建链;失败则交替 9600/57600 波特率重试 |
senergy_config_info_init | static | 初始化设备信息表、取电池容量、复位通讯 tick、按子型号决定宽范围使能初值并保存配置 |
senergys5_ems_send_set_multi_data_to_inv | static | 组 0x10 多寄存器写帧(带长度/数量合法性校验)并发送 |
senergys5_ems_send_set_grid_configuration_to_inv | static | 写 0x2115 两寄存器:Grid 充电允许 + AC 最大视在功率 |
senergys5_ems_send_instruct_code_to_inv | static | 组功能码 0x90 的内部指令帧(指令码 + 2 字节值)并发送 |
senergys5_ems_send_ate_code_to_inv | static | 组功能码 0x80 的 ATE 帧(指令码 + 1 字节值)并发送 |
senergy_rate_power_init | static | 按子型号把额定充放电功率写进 g_t_inv_info.t_config_data/t_run_info,S5-II 取 5kW、M10 取 10kW/9999W |
senergy_safety_parameters_init | static | 安规参数初始化:S5/S5-II/M10 置 Safety_Max/SAFESUB_MAX(等待从机回读),其它机型取配置值 |
senergy_bat_protection_init | static | 按电池型号与 BMS 数量查表设置最大充/放电电流(单机 vs 多机两套表),无 BMS 时置 0 |
senergys5_control_flag_init | 全局 | 把所有查询/设置标志位、各 tick、MPPT/UPS/开关机/工作模式/PV 类型等复位到初始状态 |
senergys5_show_abnormal_code | static | 打印 Modbus 异常码含义(调试用) |
senergys5_inv_abnormal_respond_judge | static | 判断收到的功能码是否为对应异常码(0x83/0x86/0x90),是则打印并返回 TRUE |
senergys5_get_workmode_mapping | static | 0x101D 逆变器运行模式 → g_t_inv_info.t_run_info.u8_run_mode(cWaitMode/cCheckMode/cSelfTest/cNormalMode/cUPS/cFaultMode) |
senergys5_get_error_waring_apping_table1 | static | 故障字 1/2 映射到统一故障/告警位;电池就绪或复位计数 <500 时屏蔽母线欠压 |
senergys5_get_error_waring_apping_table2 | static | 故障字 2/3 映射(市电丢失/频率/电压、PV 过压、绝缘、漏流、AFCI 等) |
senergys5_get_error_waring_apping_table3 | static | 故障字 3 剩余位映射(EEPROM 读写失败、ISO 告警、输出功率受限等) |
senergys5_get_error_waring_apping_table4 | static | 故障字 4 映射(电池 OVP/UVP/丢失、N/L 反接、EPS 过压/短路/过载);电池就绪时屏蔽电池类告警 |
senergys5_get_error_waring_apping_table6 | static | 故障字 6 映射(u8_nn_short_error → 中性线反接/短接故障) |
senergys5_inv_system_status_handle | static | 解析并打印 0x104A 系统状态位域(工作模式、EPS 继电器、电池状态、MPPT、市电) |
senergys5_load_de_rating_handle | static | 遍历 0x1036 降载原因位域并逐项打印(仅调试输出,不做逻辑处理) |
senergys5_get_safety_type_inv_to_server | static | 逆变器安规码 → 服务器安规码映射(含澳规子类型 A/B/C/NZ/Vector 细分),变化时置位四个回读标志并保存配置 |
senergys5_get_safety_type_server_to_inv | static | 服务器安规码 + 子码 → 逆变器安规码映射(上一函数的逆向) |
pv_total_pahse_pv_energy_statistics | static | 用 OSTimeGet() 差分对 PV 功率做时间积分,每分钟折算成 0.01kWh 累加 u32_pv_total_phase_energy_input |
inv_total_pahse_inv_energy_statistics | static | 同上,对逆变输出功率积分累加 u32_inv_total_phase_energy_output |
senergys5_query_inv_sn_modelname | static | 读 0x1A00 的 36 个寄存器解析型号名与 SN,按型号串自动识别子型号(SMILE-M10/M5/SMILE5-INV) |
senergys5_query_inv_update_versions_info | 全局 | 通用版本查询,成功返回 TRUE(供 set_upgrade_s5.c:678 升级前校验用) |
senergy_inv_old_version_flag | static | 判断是否老协议版本:S5-II 且 master 版本 ≤401,或任何 SUB_S5 |
senergys5_query_inv_versions_info | static | 读 0x1A19 的 89 个寄存器,解析 master/slave/dcdc 版本、机型参数(PV 开路电压、MPPT 路数、标称电压频率、额定有功/视在功率),版本变化时置位配置状态 |
senergys5_set_inv_ctrl_accuracy_enable | static | 写 0x2E04(DC 控制功率校准使能)为 1 |
senergys5_query_inv_regulation_parameter | static | 读 0x5000 的 48 个寄存器解析并网法规基本参数(并网等待/重连时间、OVP/UVP/OFP/UFP 两级点值时间、恢复值、10min 过压点/时间、缓升速率) |
senergy_set_inv_grid_reconnect_time | static | 写 0x5000 两寄存器(首次并网等待时间 + 重连时间) |
senergys5_set_inv_volt_restore | static | 写 0x5027 两寄存器(过/欠压恢复阈值) |
senergys5_set_inv_ovp10min_value | static | 写 0x5018(十分钟过压保护点) |
senergys5_set_inv_ovp10min_time | static | 写 0x502B(十分钟过压保护时间) |
set_set_bat_heat_cmd | static | 判据:逆变器并网/待机且(电池低 SOC 或强制加热)时置位加热查询/设置标志 |
set_set_bat_heat_cut_volt | static | BMS 加热指令 0xAA 或加热状态为 1 时置电池加热功率标志 |
senergy_get_output_reactive_power_mode | static | 0x5030 无功模式 → g_t_inv_info.t_safty_info.safty_mode_u 各位(固定功率因数/固定无功/有功功率因数/电压无功) |
senergys5_query_inv_regulation_curve | static | 读 0x5030 的 88 个寄存器解析无功模式、功率因数、Q-V 曲线 4 点、P-V 曲线 4 点、OFP3/UFP3/OVP3/UVP3、欠频曲线使能 |
send_bat_heat_to_set_inv | 全局 | 电池加热联锁入口:set_set_bat_heat_cmd() 与 set_set_bat_heat_cut_volt() 都通过才返回 TRUE |
senergys5_query_inv_system_function | static | 读 0x5100 的 48 个寄存器解析安规码、降载百分比、MPPT 阴影管理、PV 连接类型、孤岛检查、低电压穿越、绝缘检测、GFCI、有功/无功控制使能、N 线检测 |
senergys5_query_inv_system_function2 | static | 读 0x2157 解析 NPE 短接状态,写入 t_control_cmd.u8_n_pe_short_flag |
senergys5_query_inv_system_function3 | static | 读 0x2E01 三个寄存器解析 NN 短接检测、强制 bypass、电压频率宽范围状态 |
senergys5_query_inv_debug_data | static | 读 0x2F2D 的 6 个调试寄存器到 u16_ems_read_inv_debug[6] |
senergys5_query_inv_on_off_state | static | 读 0x6001 解析开关机状态;若刚完成 DCDC 升级则强制视为关机 |
senergys5_query_inv_backup_state | static | 读 0x211C 解析 UPS/BACKUP 使能状态 |
senergys5_query_inv_bat_heat_mode_state | static | 读 0x2E09 解析电池加热使能状态 |
senergys5_query_inv_bat_heat_volt | static | 读 0x2F0E 解析加热输出电压并同步到 g_t_bat_info |
senergys5_query_inv_bat_heat_curr | static | 读 0x2F08 解析加热输出电流并同步到 g_t_bat_info |
senergys5_query_inv_bms_curr_calibration | static | 读 0x2E04 解析 BMS 电流校准使能状态 |
senergys5_query_inv_product_model | 全局 | 读 0x1A5A 产品型号;连续 5 次读到 ON_GRID/HYBRID_LOAD 即判定通讯建立 |
senergys5_query_inv_real_time_data1 | static | 读 0x1001(64/74 个寄存器)解析 AC 电流、PV1~PV4 电压电流功率、机内温度、运行模式、故障字 1~3、降载原因、无功/视在功率、功率因数、系统状态、母线电压、电池电流、HS 温度 |
senergys5_query_inv_real_time_data2 | static | 读 0x2E50 解析故障字 6 并做映射 |
senergys5_query_inv_real_time_data3 | static | 读 0x2F00 解析母线电压、DCDC 步数/状态、电池电流、故障字 4/6、check_flag、逆变功率限值、最大充放电电流 |
senergys5_set_inv_grid_charge_enable | static | 写 Grid 充电使能两寄存器,成功后把标志位置 2(进入下一阶段) |
senergys5_set_inv_eps_enable | static | 写 0x211C UPS 使能,成功后置位回读标志 |
senergys5_set_inv_bat_heat_mode_enable | static | 写 0x2E09 电池加热使能,成功后置位回读标志并清设置标志 |
senergys5_set_bms_curr_calibration_enable | static | 写 0x2E04 BMS 电流校准使能(加热期间关校准) |
senergys5_set_inv_active_volt_cure_enable | static | 写 0x511D 有功电压曲线使能,成功后清标志 |
senergy_set_output_reactive_power_mode | static | 服务器无功模式数值(0/64/128/256/2)→ 逆变器无功曲线枚举,无功控制关闭时强制为 0 |
senergys5_set_inv_reactive_cure_mode | static | 写 0x5030 无功模式 |
senergys5_set_inv_reactive_volt_cure | static | 写 0x503C 四寄存器(Q-V 曲线 4 点电压) |
senergys5_set_inv_active_volt_cure | static | 写 0x5049 四寄存器(P-V 曲线 4 点电压,按 ×23/10 反换算) |
senergys5_set_inv_active_apparent_power | static | 写 0x1A4E/0x1A46 两组(AC 最大有功 + 视在功率),成功后推进 Grid 充电使能标志状态机 |
senergys5_set_inv_bypass_enable | static | 写 0x2E02 强制 bypass 使能 |
senergys5_set_inv_open_volt_fac | static | G1:用内部指令 0x93 设置 50/60Hz 宽范围使能(配置 2→下发 1,其它→下发 0) |
senergys5_g2_set_inv_open_volt_fac | static | G2:写 0x2E03 设置宽范围使能,成功后触发安规重下发 |
senergys5_set_pv_connect_type | static | 写 0x5109 PV 连接类型 |
senergys5_set_islanding_check | static | 写 0x510E 孤岛检查(配置取反下发) |
senergys5_set_ofp_curve_enable | static | 写 0x5017 过频曲线使能 |
senergys5_set_ufp_curve_enable | static | 写 0x5080 欠频曲线使能 |
senergys5_set_inv_n_line_detect_enable | static | G1:用 ATE 指令 0x12 设置 N 线检测(1 检测 / 2 不检测) |
senergys5_g2_set_inv_n_line_detect_enable | static | G2:写 0x2E01 bit0 设置 NN 短接检测,保留其它位 |
senergys5_set_inv_npe_short_enable | static | G1:用 ATE 指令 0x13 设置 NPE 继电器(1 闭合 / 2 断开) |
senergy_g2_set_inv_npe_short_enable | static | G2:写 0x2157 设置 NPE 短接,成功后触发功能 2 回读 |
senergys5_restart_dsp_handle | static | G1 重启 DSP:先发关机,延时 500ms,再发 9 字节专有复位帧 1/2,逐级比对固定应答 |
senergyg2_restart_dsp_handle | static | G2 重启 DSP:先关机,待机态下写 0x2E06 并置 CAN 握手标志 |
senergys5_restart_dsp_eeprom_handle | static | G1 清 DSP EEPROM:延时 10s 后发专有帧,最多重试 3 次 |
senergyG2_restart_dsp_eeprom_handle | static | G2 清 DSP EEPROM:先关机→写 0x6006→延时→再开机 |
senergys5_set_inv_on_off_state | static | 写 0x6001 开关机,成功返回 TRUE 并触发状态回读 |
senergys5_set_inv_satey_code | static | 写 0x5101 国家安规码,超时 8000ms;50/60Hz Default 且非 G2 时触发 DSP 复位 |
senergys5_get_self_test_data | 全局 | 意大利安规自检进度机:按 0x5210 进度 0~9 分阶段收集 0x5211 自检结果,10×100ms 后置完成标志 |
senergy_npe_check_handle | static | NPE 短接决策:澳规/接油机/离网 BB 使能则不短接,否则短接 |
senergys5_g2_nn_check_handle | static | G2 NN 短接检测位与配置不一致时置位下发标志 |
senergys5_open_volt_fac_check_handle | static | G1 宽范围/过欠频曲线决策:50/60Hz Default 强制开启,并据并机调频使能决定曲线开关 |
senergys5_g2_open_volt_fac_check_handle | static | G2 同上,但用单寄存器写 |
s_senergys5_CEC_DNSP_set_state | 全局 | 澳规 CEC 默认参数一键装载(有功/无功控制使能、safty_mode=4、UVP 恢复 2160、孤岛检查),并用 u8_Safety_charge_flag 只执行一次 |
senergys5_query_epo_state | static | 读 EPO 关机状态:关机返回 TRUE |
senergys5_data_check_handle | static | 核心决策函数:电池保护重算、速率/安规初始化、能量统计与清零、EPO/开关机比对、安规/PV 类型/孤岛/宽范围/NPE/NN/有功无功曲线逐项差异检测并置位下发标志 |
senergy_set_inv_power_rise_fall_enable | static | 写 0x512B 非首次并网缓升缓降使能 |
senergy_set_inv_power_rise | static | 写 0x5023 非首次并网功率缓升 |
senergy_set_inv_power_fall | static | 写 0x502C 非首次并网功率缓降 |
senergy_set_inv_power_regulation_rate | static | 写 0x5029 首次并网功率缓升 |
senergy_set_inv_power_reconnect_regulation_rate | static | 写 0x5019 重连并网功率缓升 |
senergys5_set_inv_gfci_enable | static | G1:写 0x5118 GFCI 检测使能 |
senergys5_g2_set_inv_gfci_enable | static | G2:写 0x512D,用 bit7 表示不检测 |
senergy_selftest_state | static | 读 0x5210 三个寄存器(自检进度、结果、自检使能) |
senergys5_query_inv_system_function_state | static | 查询调度器:按各标志位依次调用对应查询函数,并处理电池加热与安规自检的周期节拍 |
senergys5_set_inv_system_function_state | static | 设置调度器:按 s_senergys5_set_info 位(及查询完成前提)依次下发设置函数,维护「设置→回读→完成」闭环 |
senergys5_query_inv_register_data | static | 服务器透传通道:按 senergys_query_inv_register 组帧发送并回填应答,区分标准(0x03)与非标准 Modbus |
senergys5_query_inv_run_data | static | 实时数据查询节拍器:数据 1 每 500ms,数据 2 与调试数据每 500ms(仅 G2 且非老版本),数据 3 每 300ms |
senergys5_inverter_comm | 全局 | 任务级总入口:升级态只跑升级处理;正常态做 30s 失联判定 + 型号查询/标志初始化/EPO 复位/数据检查/查询设置调度 |
4. 核心逻辑解析
4.1 通信帧格式与 CRC
全部走 USART2 上的 Modbus RTU,从机地址固定 0x01,CRC 为 GetCrcCode_Modbus()(common/crc.c),低字节在前、高字节在后。
四种上行帧:
| 用途 | 报文结构 | 长度 | 组帧位置 |
|---|---|---|---|
| 读寄存器 | 01 03 ADDR_H ADDR_L NUM_H NUM_L CRC_L CRC_H | 8 | set_comm_usart_s5.c:302 |
| 写单寄存器 | 01 06 ADDR_H ADDR_L VAL_H VAL_L CRC_L CRC_H | 8 | set_comm_usart_s5.c:328 |
| 写多寄存器 | 01 10 ADDR_H ADDR_L NUM_H NUM_L BYTECNT DATA... CRC_L CRC_H | 9+2N | set_comm_usart_s5.c:539 |
| 内部指令 | 01 90 INSTRUCT DATA_H DATA_L CRC_L CRC_H | 7 | set_comm_usart_s5.c:613 |
| ATE 指令 | 01 80 INSTRUCT DATA CRC_L CRC_H | 6 | set_comm_usart_s5.c:647 |
下行的期望应答(各查询函数统一按此解析):
- 读应答:
01 03 BYTECNT DATA... CRC,BYTECNT必须等于请求寄存器数 ×2,否则丢弃。 - 写单/写多应答:回显
01 06/10 ADDR_H ADDR_L ...,函数用回显地址与请求地址比对。 - 异常应答:功能码 OR 0x80(
0x83/0x86/0x90),第 3 字节为异常码,由senergys5_inv_abnormal_respond_judge()识别(set_comm_usart_s5.c:969)。
SENERGYS5_MAX_REGISTER_NUM(100) 是单帧寄存器数上限;senergys5_ems_send_set_multi_data_to_inv 里做了三重校验:register_num <= 100、len_buff <= 200、len_buff == register_num*2,任一不满足则 l_u8_len 保持 0,最终发出一条长度 0 的空帧——这是一个可疑点(见第 7 节)。
4.2 收帧与超时重试机制
有三种收帧原语:
senergys5_inv_recv_data()(第 93 行)——非阻塞。仅当usart2_is_recv_async_data()为真才读;把usart2_get_data_len()上限截到 256,逐字节从异步队列读出,取末两字节做 CRC 比对,不匹配则*len = 0。没有重发逻辑:帧丢了就靠下一次调度周期重来。senergys5_inv_recv_data_set_inv_satey()(第 150 行)——带超时阻塞。用OSTimeGet()差分做超时;未收到数据时OSTimeDlyHMSM(0,0,0,50)让出 CPU 50ms,直到timeout到点。仅用于写安规码(超时 8000ms,因为 DSP 换安规要重新初始化)。senergys5_restart_eeprom_recv_mdsp()(第 230 行)——带超时阻塞 + 三态返回。超时返回 FALSE,收到帧返回 TRUE,CRC 错仍返回 TRUE 但*len = 0,让调用方区分「没回」和「回了但坏包」。
发送侧有全局节流:senergys5_inv_write_data()(第 64 行)用函数内 static u32 s_u32_send_pack_tick,若距上次发送 <100ms(SENERGYS5_DAT_PCK_INTERVAL_INV),就 OSTimeDlyHMSM 补足差额。这保证了从机有足够解析和处理时间,但也意味着本任务在通讯繁忙时会被迫延时。
重试机制不在本文件,而在调度层:某个查询成功后清标志位、某个设置成功后清「待下发」位;失败则标志位保持,下一个任务周期重发。唯一的显式计数重试是 DSP 复位/清 EEPROM 路径的 s_l_u8_error_cnt(上限 10 或 3)。
4.3 启动与型号识别状态机
senergys5_inverter_comm()(第 6199 行)是总入口,由 task_inverter 每周期调用(midware/inverter/inverter.c:1090,且要求 get_passthrough_flag() == FALSE)。其顶层分支:
if (升级标志: inv_upgrade / dcdc_start_upgrade)
→ 清故障字、只跑 senergys5_inv_upgrade_handle(),按 master/slave/dcdc 设 run_mode
else
├─ 30s 无应答 → s8_dcdc_evocation_cnt++,达 5 次置 comm_lost_flag=TRUE、run_mode=cFaultMode
├─ ems_inv_comm_flag == FALSE → senergys5_query_inv_product_model() + senergys5_control_flag_init()
├─ u8_reset_dsp_eeprom == TRUE → G1/G2 各自的清 EEPROM 处理
└─ 正常:data_check_handle → query_inv_system_function_state
→ set_inv_system_function_state
→ query_inv_run_data
型号识别的两条路径在 inverter.c 里分流(midware/inverter/inverter.c:1088-1099):子型号已是 SUB_S5/SUB_S5_II/SUB_M10 就走 senergys5_inverter_comm(),否则先走 senergy_model_recognition()(第 449 行)。
senergy_model_recognition() 逻辑:
- 计数
s_u8_get_model_name_cnt,超过 20 次(MODEL_QUERY_TIMEOUT_CNT)判定 DSP 串口失联,置u8_inverter_comm_lost_flag。 - 调
senergys5_query_inv_product_model();成功条件是「型号 == HYBRID_LOAD」或senergy_comm_status_check()(用升级模块探测 0xA5/0x55 应答)为真。 - 成功 → 复位计数、查 SN/型号名、
senergy_comm_init()+senergy_comm_init_can()建链。 - 失败 →
usart2_init(adaptive_polling_search[s_u8_query_flag].baudrate)切换波特率,延时 30ms,s_u8_query_flag ^= 1在 9600/57600 之间来回切换。
senergys5_query_inv_product_model()(第 3068 行)内部还有一层 5 次确认:只有连续 5 次读到 ON_GRID 或 HYBRID_LOAD,才置 u8_ems_inv_comm_flag = TRUE 并清失联标志,这是抗误判的防抖设计。
SN 解读时还会反向修正子型号(第 1913~1927 行):型号名含 SMILE-M10 → set_inverter_sub(SUB_M10),含 SMILE-M5 → SUB_S5_II,含 SMILE5-INV → SUB_S5,并调 Set_Setup_Config_Status(FALSE) 让上层重存配置。
4.4 查询调度时序
senergys5_query_inv_system_function_state()(第 5550 行)是「按标志位轮询」的调度器,每周期最多执行一个「一次只做一件事」的查询(因为每次查询都会真实收发一帧,耗时以十毫秒计)。它按顺序检查:SN/型号 → 版本信息 → 系统功能 1 → 系统功能 2 → 系统功能 3 → 开关机状态 → BACKUP 状态 → 电池加热组(加热使能/电压/电流/BMS 校准)→ 法规基本参数 → 法规曲线 → 安规自检。
senergys5_query_inv_run_data()(第 6108 行)是周期节拍器,用 SENERGYS5_TIME_100MS = 100 做单位:
| 数据块 | 周期 | 条件 |
|---|---|---|
| 实时数据 1 (0x1001) | 5 × 100ms = 500ms | 总是;老版本读 100 个寄存器,新版本读 74 个 |
| 实时数据 2 (0x2E50) | 3 × 100ms = 300ms | 仅 G2(SUB_S5_II/SUB_M10)且非老版本 |
| 调试数据 (0x2F2D) | 5 × 100ms = 500ms | 同上 |
| 实时数据 3 (0x2F00) | 3 × 100ms = 300ms | 仅 G2;老版本读 0x15 个,新版本读 0x1C 个 |
每处都用 else if (tick > OSTimeGet()) tick = OSTimeGet(); 处理 OSTimeGet() 32 位溢出/回绕,把 tick 拉回当前值避免长时间不查询。这个写法在文件里反复出现(例如 6127、6148、6186 行),是全局统一的溢出保护约定。
u32_ems_inv_comm_tick 是通讯存活心跳:几乎每个成功的收发函数末尾都会 s_senergys5_task_tab.u32_ems_inv_comm_tick = OSTimeGet();,而 senergys5_inverter_comm() 在入口处检查它与当前时间的差值是否 >30s。也就是说,「30 秒内至少成功完成一次任意寄存器读写」即视为通讯正常。
4.5 设置调度与「设置 → 回读 → 完成」闭环
senergys5_set_inv_system_function_state()(第 5656 行)是设置侧调度器。设计模式是:每个设置函数在收到合法应答后清掉对应的 s_senergys5_set_info.bit.xxx_flag,并把相关查询标志位置回 TRUE 做回读确认;若该功能组 4 字节状态字全为 0,则把该项的查询标志再置 TRUE。
举例:安规设置(第 5681 行)成功后 u8_safety_change_flag = TRUE,若 s_senergys5_set_info.word.u8_system_function1_all == FALSE 就把 u8_query_inv_system_function_flag = TRUE 触发整组回读。
分组的 4 个字节(SENERGYS5_SYSTEM_SET_ENABLE_FLAG_U 的 word 视图)分别对应:并网法规基本参数、并网法规曲线参数、系统功能 1、系统功能 3 —— 相当于四把「这一组还有几项没写完」的总闸。只有当某组的整个字节为 0,才认为该组已全部下发完毕并触发回读。
G1/G2 分支在设置侧很密集,例如:
- 宽范围使能:G2 →
senergys5_g2_set_inv_open_volt_fac()(写 0x2E03);G1 →senergys5_set_inv_open_volt_fac()(内部指令 0x93)。 - N 线检测:G2 →
senergys5_g2_set_inv_n_line_detect_enable()(写 0x2E01);G1 →senergys5_set_inv_n_line_detect_enable()(ATE 0x12)。 - NPE 短接:G2 →
senergy_g2_set_inv_npe_short_enable()(写 0x2157);G1 →senergys5_set_inv_npe_short_enable()(ATE 0x13)。 - GFCI:G2 →
senergys5_g2_set_inv_gfci_enable()(0x512D bit7);G1 →senergys5_set_inv_gfci_enable()(0x5118)。 - DSP 重启/清 EEPROM:G2 走寄存器方式,G1 走 9 字节厂内专有帧。
u8_set_inv_grid_charge_enable_flag 是一个三态小状态机(第 5980 行 switch):
| 值 | 动作 | 成功后的迁移 |
|---|---|---|
| 1 | senergys5_set_inv_grid_charge_enable() 写 0x2115(Grid 充电允许 + 最大视在功率) | → 2 |
| 2 | senergys5_set_inv_active_apparent_power(HIGH, ..., 0, 0) 写 0x1A4E 清零 | → 3 |
| 3 | senergys5_set_inv_active_apparent_power(LOW, ..., max_active, max_apparent) 写 0x1A46 放开限制 | → 0,并置版本查询标志 |
这套「先放开、再清零、再按额定值重写」的序列是为了兼容 G1 与 G2 两代逆变器对 AC 功率上限寄存器的不同处理。
4.6 差异检测决策中心:senergys5_data_check_handle
第 5068 行的 senergys5_data_check_handle() 是本文件逻辑最密的部分,它把「配置值」与「从机回读值」逐项比对,把差异转成 s_senergys5_set_info 的置位:
- 电池保护重算:
s_u8_bat_model/s_u8_bat_num变化或u8_set_init_flag为真 → 重跑senergy_bat_protection_init()并标记存 flash。 u16_reset_cnt自增(上限 65500,防止溢出)。这个计数被senergys5_get_error_waring_apping_table1/4当作「开机 500 拍内屏蔽电池/母线告警」的时间窗。- 安规自检一次性初始化:
u8_set_init_flag为真 →senergy_rate_power_init()+senergy_safety_parameters_init()。 - 能量统计:调
pv_total_pahse_pv_energy_statistics()与inv_total_pahse_inv_energy_statistics();u8_clean_energy为真则清零累计值。 - 开关机:前提是未复位、开关机查询与设置标志均为 FALSE、未在清 EEPROM。先读 EPO 状态,与
s_u8_last_epo_state比对,状态跳变才触发开关机(避免每拍重复下发);再看u8_start_stop == 1/2与当前开关机状态是否矛盾。 - 之后依次判:安规码、PV 连接类型、孤岛检查、宽范围/过度欠频曲线、NPE、NN、有功电压曲线使能、无功模式。
- 澳规 CEC 默认值:
u8_safety_change_flag为真时,澳规置actpower/reactpower_control_enable=TRUE、safty_mode=4、uvp_restore=2160并重算速率功率;非澳规则全部清零。 - 最后按
l_u8_save_flash_flag决定是否save_job_post(INVERTER_CONFIG_PAGE)。
4.7 能量统计算法
pv_total_pahse_pv_energy_statistics()(第 1710 行)与 inv_total_pahse_inv_energy_statistics()(第 1791 行)结构对称,后者只是功率源换成 s32_inv_actpower_t 且充放方向语义相反:
1. 若距上次采样 > 5000ms,认为采样中断,功率按 0 处理并更新 tick 后直接返回
2. 计算时间差 l_u32_time_differe:
OSTimeGet() >= tick → 差值
否则(32 位回绕) → (0xFFFFFFFF - tick) + OSTimeGet()
3. 累加 ms 级能量:accum += |P| * dt
4. 累计时间 s_u32Time_sum += dt
5. 当 s_u32Time_sum >= 60000ms(1 分钟):
量纲换算:accum / (s_u32Time_sum * 60) → 得到 0.01kWh 单位
余数保留回 accum(避免截断损失)
累加到 u32_pv_total_phase_energy_input / u32_inv_total_phase_energy_output
s_u32Time_sum 清零
6. 对外暴露给 g_t_inv_info.t_run_info.u32_energy_pv_input / u32_energy_inv_output(除 10 换算成 0.1kWh)
/ 60 这个除以 60 的常数来自量纲:P(W) * dt(ms) = mJ,mJ / (60000ms) = mW,再考虑 0.01kWh 单位需要 /3600…实际上这里是把 W·ms 直接换算成 0.01kWh 的经验系数,属于魔法数字(见第 7 节)。
4.8 故障码映射
逆变器故障字有 4 个 64 位字(SENERGYS5_ERROR_U)+ 1 个 16 位字(SENERGYS5_ERROR_U2),EMS 侧的 g_t_inv_info.t_run_info.u_fault_code / u_warning_code / u_fault_extend1_code 是另一套位定义,两者没有按位对齐,必须逐位搬:
table1:word0/word1→ 输出直流过流、并网继电器、远程关机、过温、GFCI、PV 反接、系统类型错、风扇异常、母线欠压/过压、内部通讯错、软件不兼容、存储错、Boost/逆变异常。table2:word2大部分 +word1部分 → 市电丢失、频率/电压异常、PV 过压/欠压、绝缘、漏流、AFCI、功率限制、内部电源、10min 过压。table3:word3中段 → EEPROM 读写失败、ISO 告警、输出功率受限、logger 丢失。table4:word3+word4→ 电池丢失/OVP/UVP/放电过流、N-L 反接、EPS 过压/短路/过载、内部通讯(EMS↔Master、Master↔DCDC)、DCDC 异常。table6:SENERGYS5_ERROR_U2.u8_nn_short_error→u8_n_n_reverse_lost。
值得注意的是 table1/table4 里的软件屏蔽:bms_get_ready_enable() == TRUE 或 u16_reset_cnt < 500 时,直接把 u8_dc_link_uvp(母线欠压)、u8_bat_ovp/uvp/lost 清 0,防止上电瞬间 BMS 尚未就绪时的误报。
u8_ground_current_detect(G2 的 0x512D)特殊:(value >> 7) & 0x01 == 0 表示检测使能(第 2624 行),与 G1 的 0x5118 语义相反,写回时也必须按位取反(第 5470~5477 行用 &= ~(1<<7) / |= (1<<7) 操作)。
4.9 安规码双向映射
两代逆变器对安规的编号完全不同,因此本文件维护一对互逆映射:
senergys5_get_safety_type_inv_to_server(u16 input)(第 1363 行):逆变器寄存器值 → EMS 安规枚举。输入是 0x5101 读到的u16_regulation_code,输出g_t_inv_info.t_safty_info.u8_safety/u8_safesub。澳规系列(4777.2/.3、NZ、SA、Vector)需要结合g_t_inv_info.t_config_data.u8_safety/u8_safesub做二次细分(因为同一寄存器值可能对应多个子安规)。senergys5_get_safety_type_server_to_inv(u8 u8_safety, u8 u8_safesub)(第 1589 行):EMS 安规枚举 → 逆变器寄存器值,是上一函数的逆向。
映射完成后有两个副作用:若与 t_config_data 不一致则同步并 save_job_post(INVERTER_CONFIG_PAGE);若与函数内 static 缓存的上次值不同,则置位法规参数、法规曲线、系统功能 2/3 四个回读标志(第 1574~1580 行)。
4.10 开关机、EPO 与复位
- 开关机下发:
senergys5_set_inv_on_off_state()写 0x6001,成功返回 TRUE 并置位查询标志。 - EPO(急停):
senergys5_query_epo_state()读inverter_get_shutdown_sta(),INV_SHUTDOWN→ TRUE。data_check_handle只在 EPO 状态跳变时下发开关机。 - 用户开关机:
g_t_inv_info.t_control_cmd.u8_start_stop== 1 要求关机、== 2 要求开机,与u8_inv_on_off_state矛盾时置位设置标志。 - DSP 复位:G1 用
senergys5_restart_dsp_handle(),报文是两条裸 9 字节帧01 80 08 ...,期望应答固定01 80 08 F1 C6 74,用strcmp逐字节比较(注意:g_u8_senergys5_inv_recv_buf是二进制数据,用strcmp比较存在遇 0 截断风险,见第 7 节)。复位前必须先发关机并延时 500ms,注释里写明原因是「防止市电反冲母线电容导致逆变管炸」。 - 复位在
cCheckMode(检查/待机)下被拒绝,G1 直接 return 并计数(>150 次放弃),G2 在待机态才真正执行 0x2E06。
4.11 uCOS-II 协作方式
本文件不使用任何信号量、消息邮箱或互斥量,完全依赖两点:
- 任务串行执行 —— 只有
task_inverter调用这些函数,天然串行。 OSTimeDlyHMSM()主动让出 CPU —— 出现在senergys5_inv_write_data()(补足帧间隔)、senergys5_inv_recv_data_set_inv_satey()/senergys5_restart_eeprom_recv_mdsp()(收帧等待)、以及所有复位/清 EEPROM 路径的固定延时(500ms / 1000ms / 10000ms)。
时间基准统一为 OSTimeGet()(uCOS-II 系统 tick)。所有周期性判断都写成 (OSTimeGet() - tick) > 周期 并配 else if (tick > OSTimeGet()) tick = OSTimeGet(); 做溢出保护。
因为收帧原语是非阻塞的(除两处阻塞变体外),一次完整的「发帧 + 收帧」跨两个调度周期,这也是为什么所有设置函数都需要靠标志位在下一周期重试。
4.12 服务器透传寄存器通道
senergys5_query_inv_register_data()(第 6006 行)是给云端/上位机预留的任意寄存器读写通道(senergys_query_inv_register,定义在 midware/inverter/inverter.h:1930,由 net/server.c:1500 及附近代码写入,net/ems.c:802 读回)。
u8_modbus_flag == 1:标准 Modbus 读,取u8_send_data[0..5]组帧 + 自算 CRC,收帧后校验BYTECNT == register_num*2,回填u8_rend_data[0..7]。u8_modbus_flag == 2:非标准 Modbus,只发 5 字节(不含寄存器数量),收帧后自行重算 CRC(第 6079 行)再比对,同样回填 8 字节。
两种路径都清 u8_start_en = 0 表示本次透传完成。该通道与常规轮询共用 g_u8_senergys5_inv_recv_buf 和 senergys5_inv_write_data() 节流,因此会和实时数据轮询互相抢用串口。
5. 对外接口
5.1 本文件导出(供外部调用)
函数(头文件 set_comm_usart_s5.h:821-829 声明):
| 导出函数 | 调用方 |
|---|---|
senergy_comm_init(void) | midware/inverter/inverter.c:74(inverter_state_init) |
senergy_comm_init_usart(void) | midware/inverter/inverter.c:75 |
senergy_model_recognition(void) | midware/inverter/inverter.c:1094 |
senergys5_inverter_comm(void) | midware/inverter/inverter.c:1090 |
senergys5_control_flag_init(void) | 本文件 senergys5_inverter_comm;也在头文件导出 |
senergys5_get_self_test_data(void) | midware/inverter/inverter.c:302 |
senergys5_query_inv_product_model(void) | 本文件;头文件导出 |
senergys5_query_inv_update_versions_info(u16,u16) | midware/inverter/set/set_upgrade_s5.c:678(另有 set_upgrade_s5.c:28 的 extern 声明) |
send_bat_heat_to_set_inv(void) | 本文件 senergys5_set_inv_system_function_state;工程内其它模块(头文件未声明,靠隐式声明或 extern) |
s_senergys5_CEC_DNSP_set_state(void) | 工程内其它模块(头文件未声明) |
全局变量(头文件 set_comm_usart_s5.h:814-819 声明为 extern):
| 导出变量 | 说明 | 主要使用者 |
|---|---|---|
s_senergys5_task_tab | 通信任务表(标志位、tick、波特率表、调试寄存器) | 本文件、midware/inverter/inverter.c |
s_senergys5_device_info_tab | 设备信息表(实时数据、状态、能力参数) | 本文件、midware/inverter/inverter.c、midware/inverter/set/set_comm_can.c |
s_senergys5_control_info_tab | 控制信息表(工作模式、开关机、限值、计数) | 本文件、set_comm_can.c |
s_u_senergys5_error_mapping_table | 故障字 1~4 原始位 | 本文件、inverter.c |
s_u_senergys5_error_mapping_table2 | 故障字 6 原始位 | 本文件、inverter.c |
s_senergys5_set_info | 4 组待下发/已完成状态字 | 本文件、inverter.c |
其它未在头文件导出的文件级全局:g_u8_senergys5_inv_recv_buf[256]、u8_Safety_charge_flag、senergys_query_inv_register(后者在 midware/inverter/inverter.h:1930 声明)。
注意:头文件 set_comm_usart_s5.h:824 声明了 void senergys5_data_init(void);,但全工程(含本文件)没有任何定义。这是一个悬空声明,从未被调用。
5.2 本文件依赖的外部函数/变量
| 来源模块 | 使用内容 |
|---|---|
midware/inverter/inverter.h(g_t_inv_info) | t_config_data(安规、使能、限值、配置)、t_safty_info(安规参数、曲线、恢复值、GFCI、无功模式)、t_run_info(运行模式、故障字、告警字、PV/AC 实时量、版本、SN、型号名、心跳标志)、t_control_cmd(开关机、UPS 使能、复位、EPO、清能量、NPE)、t_control_parameter(加热数据标志)、t_safty_tset_info[](自检) |
midware/inverter/inverter.c | get_inverter_sub()、get_inverter_type()、set_inverter_sub()、inverter_read_safety()、inverter_read_safety_is_AS4777()、inverter_read_pv_power()、inverter_set_n_pe_short_enable()、inverter_set_actpower_control_enable()、inverter_set_reactpower_control_enable()、inverter_set_safty_mode()、inverter_set_uvp_restore()、inverter_set_islanding_check_enable()、inverter_get_shutdown_sta()、set_inverter_safty_test_info_send_app_flag()、Set_Setup_Config_Status() |
midware/inverter/set/set_upgrade_s5.h | s_senergys5_upgrade_info(u8_set_dcdc_upgrade_on_flag)、senergys5_upgrade_recv_mdsp()、senergys5_get_inv_upgrade_flag()、senergys5_get_dcdc_start_upgrade_flag()、senergys5_get_master_upgrade_flag()、senergys5_get_slave_upgrade_flag()、senergys5_get_dcdc_upgrade_flag()、senergys5_inv_upgrade_handle()、SENERGYS5_INV_REPLY_CODE(0xA5)/REPLY_CODE1(0x55) |
midware/inverter/set/set_comm_can.h | senergy_comm_init_can()、g_t_set_comm_can_info(CAN 握手标志) |
midware/bms/...(bms.h) | g_t_bat_info(电池 SOC 低标志、强制加热、BMS 加热指令、加热电压电流、继电器)、bms_read_num()、bms_get_ready_enable() |
midware/pvcharger(pvcharger 接口) | pvcharger_read_bat_model()、pvcharger_read_bat_dischg_cut_off_volt()、pvcharger_read_bat_charge_cut_off_volt() |
data/(data.h、system_config.h) | g_t_ems_info.u_ems_fault、g_t_system_base_info、save_job_post(INVERTER_CONFIG_PAGE)、energy_read_storage_pv_input()、energy_read_storage_inv_output() |
common/crc.c | GetCrcCode_Modbus() |
hardware 层(usart2.h) | usart2_init()、usart2_clear_buffer()、usart2_send_data()、usart2_is_recv_async_data()、usart2_get_data_len()、usart2_read_data_async() |
logic/(logic.h) | g_t_logic_info.t_config_data.Generator(是否接油机)、parallel_read_freq_dispatch_en()(并机调频使能) |
net/server.h | 服务器透传通道对 senergys_query_inv_register 的写入 |
ate.h | ATE 相关声明 |
math.h | sqrt()(计算视在功率) |
6. 依赖关系
依赖(本文件 include,第 13~29 行):
inverter.h、bms.h、meter.h、logic.h、set_comm_usart_s5.h、set_upgrade_s5.h、server.h、inv_download.h、ems.h、data.h、system_config.h、crc.h、usart2.h、system_aux.h、set_comm_can.h、math.h、ate.h。
被依赖(谁会用到它):
midware/inverter/inverter.c—— 唯一的驱动方,也是g_t_inv_info各字段的主要消费方。midware/inverter/set/set_comm_can.c—— 读s_senergys5_device_info_tab/s_senergys5_control_info_tab做 CAN 侧一致性处理,并实现senergy_comm_init_can()。midware/inverter/set/set_upgrade_s5.c—— 升级前调senergys5_query_inv_update_versions_info()校验版本。net/ems.c、net/server.c—— 通过senergys_query_inv_register透传读写逆变器寄存器。- 头文件
set_comm_usart_s5.h也被set_comm_can.c直接 include。
7. 备注与风险点
strcmp比较二进制帧(第 4373、4383、4489、4500 行):strcmp((char *)g_u8_senergys5_inv_recv_buf, (char *)l_u8_restart_recvbuf)。接收缓冲是二进制,且首字节0x01之后是一串0x80 0x08 ...。strcmp遇到\0会提前结束;本例中固定应答01 80 08 F1 C6 74不含0x00,暂时可工作,但一旦应答里有 0 字节就会出错。应改用memcmp并带长度。- 长度 0 的空帧被发出:
senergys5_ems_send_set_multi_data_to_inv(第 539 行)在参数校验失败时l_u8_len保持 0,随后仍调用senergys5_inv_write_data(l_u8_tx_buf, 0)。虽然usart2_send_data传 0 长度大概率无副作用,但会白白占用 100ms 的帧间隔并刷新发送 tick。 l_u8_tx_buf未完全初始化:同上函数中,若func_code既非0x10也非0x06,l_u8_len为 0,而l_u8_tx_buf[4]/[5]已写入但[6..]未初始化,配合上一条的空帧发送属于未定义内容路径(当前不会真的发送数据,风险低)。u8_set_inv_grid_charge_enable_flag是三态而非布尔,命名以_flag结尾但取值 0/1/2/3,容易被后续维护者当成布尔量误用。senergy_bat_protection_init的电流表是两套并行 switch(bms_read_num()==1与else分支),同一电池型号在两套表里数值不同(如 SMILE_BAT_5P 为 650 vs 1020),且ALPHA_SMILE_M_BAT_13_9P*的注释写「最大放电电流 115A,待接入 M10 后 208A」——注释与数值不符(值为 2300 即 230A)。这是明显的版本遗留代码。SENERGYS5_INV_RATED_POWER_5_5KW、_6KW、_10KW定义了但未被使用;senergy_rate_power_init里SUB_S5_II恒取 5kW,SUB_M10澳规取 9999W 否则 10kW,5.5/6kW 档位形同虚设。- 能量统计的
/ (s_u32Time_sum * 60)系数缺乏说明(第 1759、1760、1764、1765、1840、1841、1845、1846 行)。s_u32Time_sum已是毫秒累计,s_u32_input_temp的单位是W·ms,因此W·ms / (ms * 60)得到的是「平均功率 / 60」,量纲上等价于「1 分钟窗口内的 Wh 数」。但后续+= (s_u32_input / 10)再% 10存留、对外又/10暴露,整条换算链与结构体注释声明的0.01kWh单位对不上(按 1 分钟 Wh 累加,累计值应对应 0.1Wh 而非 0.01kWh)。这段系数来自移植代码,没有任何注释解释 60、10 这两个常数的量纲来源,属于需要重新标定的魔法数字。此外若s_u32Time_sum恰为 0 会除零,虽然进入该分支意味着s_u32Time_sum >= 60000因而实际不触发,但缺少显式保护。 - 两个能量统计函数各有一半是死代码:
pv_total_pahse_pv_energy_statistics累加了s_u32_input并写入 PV 总量,但s_u32_output(PV 反向功率)的累加行被注释掉(第 1766 行)且永远只是% 10后丢弃;inv_total_pahse_inv_energy_statistics恰好相反——s_u32_input(逆变侧反向)的累加行被注释掉(第 1842 行),只有s_u32_output生效。结果是 PV 函数只统计功率为正的方向、逆变函数只统计功率为正的方向,u32_inv_total_phase_energy_input之类的反向电量永远为 0。这是有意简化还是遗漏无法从代码判断,但注释掉的代码应清理。 senergys5_query_inv_real_time_data1的 PV 功率过滤阈值 50 与 7500 是硬编码魔法数字(第 3242~3258 行),低于 50W 归零、高于 7500W 归零。7500W 对 M10(额定 10kW、PV 可到 15kW)可能过严,会把正常的大功率 PV 归零。senergys5_query_inv_regulation_curve的*10/23与*23/10换算(第 2503、2504、2522、2523、3762~3772 行)是 P-V 曲线寄存器的定点换算系数,23 这个魔数无任何注释说明其来源(推测对应 230V 基准)。- 曲线写入的数次
(x /10 *23 >> 8)存在运算顺序隐患:先除后乘再右移,中间结果可能溢出 16 位(u16_p_v_mode_v*最大到 1130,/10*23= 2599,不溢出;但语义上更应写成(u32)运算再截断)。 - G2 分支中的
s_senergys5_task_tab.u8_query_inv_heat_mode_state_flag查询被注释掉(第 2931 行),导致电池加热状态的查询只在设置路径(senergys5_set_inv_bat_heat_mode_enable成功后)被触发一次,且senergys5_query_inv_system_function_state里每 1s 才真正执行一次senergys5_query_inv_bat_heat_mode_state(),其余 2 个查询(电压/电流)与 BMS 校准查询每拍都发——三帧共用一个 1s 节拍但只有一帧受限时,会导致加热电压/电流查询频率远高于状态查询。 - G1/G2 成对函数的命名前缀混用:同一个语义的两代实现,前缀在
senergys5_/senergy_/senergyg2_/senergyG2_之间不一致 —— 例如senergys5_set_inv_npe_short_enable(G1, 4231) 对senergy_g2_set_inv_npe_short_enable(G2, 4276);senergys5_restart_dsp_handle(G1, 4320) 对senergyg2_restart_dsp_handle(G2, 4410);senergys5_restart_dsp_eeprom_handle(G1, 4458) 对senergyG2_restart_dsp_eeprom_handle(G2, 4521)。g2与G2两种大小写同时存在,检索时极易漏项。 - 调试代码用
printf输出大量中间量,且#if (SENERGYS5_INV_DEBUG == 1)包裹(第 3268~3312 行整块)。其中第 3305 行的if((get_inverter_sub() == SUB_S5_II) && (get_inverter_sub() != SUB_M10)) return;是冗余条件:因为SUB_S5_II与SUB_M10是两个不同枚举值,该表达式等价于单纯的get_inverter_sub() == SUB_S5_II,第二个子条件是恒真的废话。实际效果只是 S5_II 机型跳过打印后 3 行调试信息。 u16_inv_ac_max_active_power是 u16 而u32_nominal_ac_active_power是 u32,在senergys5_query_inv_versions_info里直接比较(第 2087 行);若从机上报的额定有功功率超过 65535W,比较必然失败。对 10kW 机型没问题,但类型不一致是隐患。- 无互斥保护:
g_u8_senergys5_inv_recv_buf是文件级共享缓冲,被常规轮询和服务器透传通道共用。若net/server.c在其它任务上下文改senergys_query_inv_register(u8_read_flag/u8_start_en),与本任务的读取之间没有临界区保护,存在竞态。 senergys5_inv_write_data里的OSTimeDlyHMSM会阻塞整个task_inverter:当两帧间隔不足 100ms 时(例如查询与设置在同一周期先后发帧),任务会被强制延时,可能影响同任务内 CAN 侧或并机处理的时间响应。此外该函数的abs(OSTimeGet() - s_u32_send_pack_tick)在 tick 回绕时不正确(abs作用于 32 位无符号相减的结果,回绕时的负差值会得到错误量级)。SENERGYS5_NUM_EPS_ENABLE(0x01) 被当作通用「期望 1 个寄存器」常量复用于电池加热状态、加热电压、加热电流、BMS 校准四处长度校验(第 2924、2971、3010、3051 行)。当前这四处确实都只读 1 个寄存器,因此结果正确,但用错了语义(应各自用SENERGYS5_NUM_BAT_HEATING_ENABLE等),一旦将来某个寄存器数量改变就会静默失效。senergys5_query_inv_system_function3中把u16赋值给u8变量(第 2739 行g_t_inv_info.t_run_info.u8_inv_bypass_enable = (u16)(...)),存在高位截断。senergys5_set_inv_satey_code的括号与換行极具误导性(第 4650~4654 行):if( (A || B) && C && D )虽然括号配平,但后半个条件另起一行、缩进与整体不齐,容易误读为独立的第二个判断。- 一批调试宏在头文件中全部为 0,但 .c 里保留了
#if ((SENERGYS5_INV_DEBUG == 1) || (SENERGYS5_INV_SYSTEM_FUNCTION == 1))的双条件写法,说明历史上曾被临时置 1 调试过,属未清理的遗留开关。 senergys5_get_workmode_mapping把SHUTDOWN_MODE映射为cWaitMode(第 1042~1043 行),与INITIAL_MODE相同;而 30s 通讯丢失走的是cFaultMode。停机态与初始态在 EMS 逻辑层不可区分,可能影响上层对逆变器状态的判断。