ginlong_comm_usart.c
static 变量(隐性状态)3. 函数清单4. 核心逻辑解析4.1 Modbus-RTU 帧格式与组帧4.2 主状态机:ginlong_inverter_comm() 三段式分支(ginlong_comm_usart.c:4861-5220)4.3 子状态机:set_inv_grid_parameter_tuning()(:1679-1819)4.4 关键算法与计算公式4.5 定时/调度时序4.6 关键全局变量的读写关系4.7 典型业务流程:下发一个 AC 有功设定5. 对外接口5.1 本文件导出、供外部调用的函数(头文件正式声明 4 个:ginlong_comm_usart.h:930-933)5.2 本文件导出但未在头文件声明的 64 个全局函数5.3 本文件导出的全局变量5.4 本文件调用的外部函数/变量6. 依赖关系6.1 依赖(本文件 include / 使用的外部模块)6.2 被依赖(哪些文件会用到它)7. 备注与风险点7.1 缓冲区越界风险(高)7.2 组帧缺陷7.3 数据解析缺陷7.4 并发与实时性7.5 死代码与遗留源文件路径:
midware/inverter/ginlong/ginlong_comm_usart.c所属模块:midware / inverter / ginlong(锦浪 Solis 逆变器 Modbus-RTU 串口通信 + 固件升级) 代码行数:5221
1. 文件职责
本文件是 EMS 与锦浪(Ginlong/Solis)储能逆变器之间 Modbus-RTU 串口主通道 的完整实现:对上提供 ginlong_inverter_comm() 这一"周期入口",对下通过 usart2_* 驱动在物理串口上收发 Modbus 报文。
它在数据流中处于"EMS 应用层 ↔ 逆变器设备层"的唯一桥接位置。被 midware/inverter/inverter.c 的逆变器轮询任务驱动:inverter.c:82 上电调 ginlong_comm_init() 做通道初始化,inverter.c:1105/inverter.c:1106 在每轮 GINLONG_TYPE 分支里依次调用 ginlong_inverter_comm()(收发调度)与 ginlong_inverter_data_check()(数据一致性/防超额限幅),inverter.c:314 还会调 ginlong_get_self_test_data() 汇总意大利安规自检结果。
数据方向有两个:一是读,把逆变器输入/保持寄存器解析成 EMS 统一运行数据 g_t_inv_info.t_run_info、安规参数 g_t_inv_info.t_safty_info、型号 g_t_inv_info.t_config_data.u8_inv_model_sub;二是写,把 EMS 调度结果(AC 有功设定、电池保护参数、backup 使能、安规/工作模式/保护点)折算成寄存器值下发。
整个文件由"标志位驱动":上层(HMI、服务器、并机、BMS、logic 调度)只往 g_t_ginlong_info.t_ginlong_comm_info 里的约 50 个 u8/u16 标志置位,本文件在 ginlong_inverter_comm() 中逐个消费标志、组帧、收发,实现了上下层异步解耦。另有一条独立的固件升级分支(ginlong_upgrade_dsp_handle() / ginlong_upgrade_arm_handle())。
2. 关键数据结构与宏
2.1 本文件定义的全局对象
| 名称 | 类型 | 说明 |
|---|---|---|
g_t_ginlong_info | GINLONG_TABLE_T(全局定义,ginlong_comm_usart.c:31) | 锦浪通道唯一状态表,聚合标志位表、运行数据、控制命令、安规参数四大块;全工程通过 ginlong_comm_usart.h:927 的 extern 引用 |
g_u8_inv_recv_buf | u8[GINLONG_INV_RECV_BUFFER_SIZE] = u8[256](ginlong_comm_usart.c:32) | 所有收发函数共用的接收缓冲;所有 ginlong_recv_data() 的入参都是它 |
GINLONG_TABLE_T | 结构体(ginlong_comm_usart.h:917-925) | 顶层容器:t_ginlong_comm_info / t_ginlong_rundata / t_control_cmd / t_ginlong_debug / t_ginlong_safety_info |
GINLONG_COMM_INFO_T | 结构体(ginlong_comm_usart.h:846-909) | 核心"待办标志表":u8_ems_inv_comm_flag(通信建立)、u32_ems_inv_comm_tick(通信心跳)、约 50 个 u8_set_*/u8_query_* 请求标志、4 个调度周期 tick |
GINLONG_RUNDATA_TABLE_T | 结构体(ginlong_comm_usart.h:807-844) | 逆变器侧运行缓存:型号子码 u8_inv_model_sub、硬件版本 u8_hw_ver[10]、backup 状态、4 个故障位域联合体、频率减载模式、低压穿越模式、特殊功能控制字 |
GINLONG_CONTROL_TABLE_T | 结构体(ginlong_comm_usart.h:794-805) | 控制命令:u8_backup_set、u8_charge_limit_enable、意大利一键全检使能/查询标志 |
GINLONG_SAFETY_INFO_T | 结构体(ginlong_comm_usart.h:691-792) | 安规参数全量镜像(约 100 个 u16):电压/频率一二级保护点与时间、PU/QU 曲线点、功率斜率、频率减载参数、GFCI/ISO 限值、意大利自检结果 |
GINLONG_DEBUG_TABLE_T | 结构体(ginlong_comm_usart.h:911-915) | 调试缓存 s32_ginlong_debug_1,本文件中相关代码已全部注释 |
GINLONG_SPECIAL_FUNC_CONTROL_U | 联合体(ginlong_comm_usart.h:610-637) | 43302 特殊功能控制字:bit0 u8_boost_control、bit1 u8_dc_component_control、bit3 u8_afci_self_check、bit9 u8_mppt_enable(MPPT 开关)、bit12 阴影扫描(本文件按位裸操作,无独立位域名) |
GINLONG_SPECIAL_SEETING_U | 联合体(ginlong_comm_usart.h:640-659) | 43249 安规特殊设置字:bit1 u8_igfollow(电网追踪)等 |
GINLONG_ONGRID_FAULT_STATUS_U | 联合体(ginlong_comm_usart.h:493-521) | 33116 并网侧故障位域(u8_grid_ovp/u8_grid_uvp/u8_grid_ofp/u8_grid_ufp/u8_gird_unbalance 等) |
GINLONG_BACKUP_FAULT_STATUS_U | 联合体(ginlong_comm_usart.h:524-550) | 33117 backup 侧故障位域(u8_backup_overload/u8_grid_backup_overload/u8_backup_ovp) |
GINLONG_INV_FAULT1_STATUS_U / GINLONG_INV_FAULT2_STATUS_U | 联合体(ginlong_comm_usart.h:552-608) | 33119/33120 机器侧故障位域(母线过压、IGBT 过流、绝缘、GFCI、过温、DSP 通讯丢失等) |
ITALY_SAFTY_TEST_RESULT_U | 联合体(ginlong_comm_usart.h:661-689) | 意大利三相自检逐项结果位域(u8_test_ovp*_result 系列) |
2.2 枚举(本文件大量作 switch/比较使用)
| 名称 | 类型 | 说明 |
|---|---|---|
SAFETY_RESPONSE_MODE | 枚举(.h:212-217) | NO_RESPONSE_IDLE / ACTPOWER_RESPONSE / REACPOWER_RESPONSE,用于 set_inv_grid_parameter_tuning() 的三步状态机 |
GINLONG_WORK_MODE_E | 枚举(.h:219-229) | NO_RESPONSE_MODE / VOLT_WATT_DEFAULT / VOLT_VAR / FIXED_POWER_FACTOR / FIX_REACTIVE_POWER / POWER_PF,写入 43050 的工作模式值 |
SAFETY_E | 枚举(.h:231-239) | GINLONG_SAFETY / STANDARD_SAFETY / GINLONG_SAFETYSUB / STANDARD_SAFETYSUB,指定 exchange_safety() 的转换方向 |
FREQ_LOAD_SHED_MODE_E | 枚举(.h:241-267) | NO_REQUIRE / AUSTRALIA_REQUIRE / ITALY_REQUIRE / AUSTRIA_REQUIRE … 频率减载模式码 |
MPPT_SWITCH_CTRL_E | 枚举(.h:206-210) | MPPT_SWITCH_ON_DEFAULT = 0 / MPPT_SWITCH_STOP,与 43302 bit9 对照 |
GINLONG_SPECIAL_SETTING_E | 枚举(.h:198-204) | SPECIAL_SETTING_NONE / IGFOLLOWE_FLAG / FRT_ENABLE_FLAG,区分 43249 的两种修改意图 |
ON_OFF_E | 枚举(.h:192-196) | OFF / ON |
SUB_*(SUB_T10/SUB_S5/SUB_B5/SUB_S6/SUB_S3_6/SUB_S4_6 等) | 枚举(定义于 midware/inverter/inverter.h:306-330) | 逆变器机型子码,本文件几乎所有分支都按它区分行为 |
2.3 重要宏常量
| 名称 | 类型 | 说明 |
|---|---|---|
GINLONG_INV_DEBUG | 宏(.h:33,值为 0) | 调试打印总开关;本文件内所有 printf/for(int i...) 均被它包裹 |
GINLONG_MAX_REGISTER_NUM | 宏(.h:35,100) | 单次多寄存器写允许的最大寄存器数,ems_send_ctrl_data_to_ginlong() 用作入参上界校验 |
GINLONG_INV_RECV_BUFFER_SIZE | 宏(.h:36,256) | g_u8_inv_recv_buf 长度 |
GINLONG_DAT_PCK_INTERVAL_INV | 宏(.h:38,700 ms) | 两条报文之间的最小间隔,同时也是收帧等待超时(ginlong_write_data / ginlong_recv_data 共用) |
GINLONG_SLAVE_ADDRESS | 宏(.h:40,1) | Modbus 从机地址(逆变器) |
GINLONG_FUNC_READ_HOLDING_REGISTER / _READ_INPUT_REGISTER / _WRITE_SIGNAL_HOLDING_REGISTER / _WRITE_MULTIPLE_HOLDING_REGISTER | 宏 | 功能码 0x03 / 0x04 / 0x06 / 0x10 |
TIME_100MS / TIME_1S | 宏(.h:19-20,100 / 1000) | 节拍换算常量,本文件用 2*TIME_1S 表示 2 秒轮询周期 |
GL_VERSION_INVHARD / GL_VERSION_INVHARD_V2 | 宏 | "V1.01" / "V1.02",硬件版本字符串 |
GINLONG_T10_SUB / GINLONG_T10_SUB_V2 / GINLONG_S5_SUB / GINLONG_S5_SUB_V2 / GINLONG_B5_SUB / GINLONG_S6_SUB / GINLONG_S3_6_SUB / GINLONG_S4_6_SUB | 宏 | 33000 寄存器返回的机型子码(0xBF / 0xF0 / 0x3133 / 0x3137 / 0x4132 / 0x3138 / 0x3135 / 0x3136) |
ADDR_REGISTER_RUNDATA_1 / NUM_RUNDATA_1 | 宏(.h:60-61,33029 / 93) | 运行数据块 1 |
ADDR_REGISTER_RUNDATA_2 / NUM_RUNDATA_2 | 宏(.h:63-64,33132 / 81) | 运行数据块 2 |
ADDR_REGISTER_PRODUCT_MODEL / NUM_INV_PRODUCT_MODEL | 宏(.h:48-49,33000 / 20) | 产品型号/版本/SN |
ADDR_SAFETY_REGULATIONS_INFO / NUM_SAFETY_REGULATIONS_INFO | 宏(.h:85-86,43050 / 60) | 安规参数读取区 43050–43109 |
ADDR_INV_PU_MODE_V1_SET / NUM_INV_SAFETY_MODE_SET1 / NUM_INV_PU_MODE_V1_SET | 宏(.h:131-133,43200 / 21 / 50) | 安规工作模式参数区(读 50 个、写 21 个) |
ADDR_REGISTER_BMS_PROTECT_PARAMETER / NUM_BMS_PROTECT_PARAMETER / NUM_BMS_PROTECT_PARAMETER2 | 宏(.h:112-114,43111 / 15 / 12) | 电池保护参数区(T10 写 15 个寄存器,新平台写 12 个) |
ADDR_INV_ON_OFF_COMMAND / ADDR_INV_SPECIAL_SETTING_MPPT_SWITCH / ADDR_INV_ISOLATION_LIMIT_VALUE / ADDR_INV_LOW_VOLTAGE_CROSS / ADDR_INV_PU_MODE_ACTIVE_RESPONSE_3TAU / ADDR_INV_ZERO_CURRENT_VOLTAGE | 宏 | 43007 / 43302 / 43246 / 43330 / 43403 / 43443 等单寄存器地址 |
ADDR_ITALY_SELF_INSPECTION_RESULTS / ADDR_REGISTER_QUERY_ITALY_TEST_DATA_1 / ADDR_REGISTER_QUERY_ITALY_TEST_DATA_2 | 宏 | 33334/3、33223/20、33470/40,意大利自检三组数据 |
完整寄存器宏清单(约 70 组
ADDR_*/NUM_*)见ginlong_comm_usart.h:48-188,本文件只按名称引用,不重新定义。
2.4 本文件内的函数级 static 变量(隐性状态)
| 所属函数 | 变量 | 说明 |
|---|---|---|
ginlong_write_data | s_u32_send_pack_tick | 发送节流基准 tick |
query_inv_product_model | s_u8_querry_tick | 型号查询成功计数,>5 才允许写回配置 |
query_inv_rundata_1 | s_u8_safety_tick | 安规不一致计数,>5 才写回配置 |
set_inv_grid_parameter_tuning | s_u8_status | 三步状态机(NO_RESPONSE_IDLE→ACTPOWER_RESPONSE→REACPOWER_RESPONSE) |
query_safety_regulations_info / query_safety_regulations_info_2 / query_inv_pu_mode_active_response_3tau | s_u8_first_query | 首次查询才把逆变器值同步到 EMS |
ginlong_query_italy_self_inspection_enable | s_u8_cnt | 全检使能未就绪重试计数,>3 放弃 |
set_inv_isolation_limit_value | s_u8_set_flag | 写入失败重试计数,>10 放弃并接受逆变器现值 |
ems_send_bms_protect_paramter_to_inv | s_u32_dc_set_power_tick | 最近一次非零功率设定时刻(0 电流保底放电用) |
ginlong_inverter_data_check | s_u8_inspect_backup_flag、s_u32_inv_status_check_tick、s_u8_force_charge_flag、s_u8_safety_change | backup 迟滞状态、Wait 超时计时、强制充电状态、上一次安规值 |
deal_bat_upgrade | s_u8_status_cnt | 逆变器已停机确认计数,≥10 判定可升级 |
3. 函数清单
全部函数共 68 个(其中 static 4 个:ems_send_query_data_to_inv_ginlong、ems_send_ctrl_data_to_ginlong、exchange_safety、exchange_safetysub,另有前置声明 allow_grid_parameter_tuning 于 ginlong_comm_usart.c:37)。
| 函数名 | 作用域 | 说明(行号) |
|---|---|---|
ginlong_data_init | 全局 | 按机型子码填默认额定功率/电池参数,并置一批初始请求标志(:44) |
ginlong_write_data | 全局 | 发送节流(距上次发送 <700 ms 则 OSTimeDlyHMSM 补齐)后清缓冲并调用 usart2_send_data(:164) |
ginlong_recv_data | 全局 | 轮询等待异步接收,长度≥2 后读空 FIFO,校验 Modbus CRC,成功返回长度否则返回 0(:201) |
ems_send_query_data_to_inv_ginlong | static | 组 8 字节 0x03/0x04 读帧(从机地址+功能码+起始地址+数量+CRC)并发送(:273) |
ems_send_ctrl_data_to_ginlong | static | 组 0x06(8 字节)/0x10(7+2N+2 字节) 写帧并发送,含入参上界校验(:299) |
exchange_safety | static | 大安规代码双向映射(EMS 标准码 ↔ 锦浪码),按机型分 T10 与新平台两套(:341) |
exchange_safetysub | static | 子安规(澳 A/B/C/NZ)双向映射,仅 AS4777 系使用(:763) |
deal_system_mode | 全局 | 由 33095 system_status 与 33121 inv_status 推导 g_t_inv_info.t_run_info.u8_run_mode(:889) |
deal_inv_fault | 全局 | 把 4 个锦浪故障位域翻译成 EMS 统一 u_fault_code/u_fault_extend1_code,并置 cFaultMode(:970) |
inv_on_off_command | 全局 | 向 43007 写 0xBE(开)/0xDE(关)(:1059) |
ginlong_restart | 全局 | 关机 → 延时 500 ms → 开机,实现逆变器重启(:1109) |
query_inv_product_model | 全局 | 读 33000 起 20 个寄存器,解析子码/主从版本/SN,稳定后写回配置并落盘(:1126) |
set_inv_rtc_time | 全局 | 读 43000–43005 与 SYSTEM_DATETIME 比对,不一致则写回 6 个寄存器(:1284) |
query_inv_rundata_1 | 全局 | 读 33029 起 93 个寄存器,解析 PV/母线/电网/功率/安规/温度/系统状态与故障(:1357) |
query_inv_rundata_2 | 全局 | 读 33132 起 81 个寄存器,解析电池/backup 三相/发电量/backup 状态(:1528) |
allow_grid_parameter_tuning | static | 仅 T10 且安规为 VDE4105/PEA/MEA/TOR 时返回 TRUE(:1645) |
set_inv_grid_parameter_tuning | static | 写 43050 起 5 个寄存器(工作模式/无功/有功/功率因数),带三步状态机(:1679) |
query_safety_test_data | 全局 | 读 33221 起 22 个寄存器(安规自检原始数据,仅刷新心跳)(:1828) |
query_safety_regulations_info | 全局 | 读 43050 起 60 个寄存器,解析安规参数并(首查/上传标志时)同步到 t_safty_info(:1861) |
query_ems_send_bms_protect_paramter_to_inv | 全局 | 读 43111 起写指令寄存器(回读校验用,仅刷新心跳)(:2017) |
query_safety_regulations_info_2 | 全局 | 读 43200 起 50 个寄存器,解析 PU/QU/斜率/减载/GFCI/ISO/LVRT 等安规参数(:2068) |
query_inv_freq_load_shed_mode | 全局 | 读 43226 频率减载模式(:2214) |
query_inv_mppt_switch | 全局 | 读 43302 特殊功能控制字(:2253) |
query_low_voltage_cross | 全局 | 读 43330 低压穿越模式(:2290) |
query_inv_pu_mode_active_response_3tau | 全局 | 读 43403 P(U) 有功响应 3Tau(:2328) |
ems_send_bms_protect_paramter_to_inv | 全局 | 把并机/调度侧电池功率折算成电流,写 43111 电池保护参数区(0x10)(:2369) |
set_inv_three_unbalance_mode | 全局 | 读后写 43191 三相不平衡使能,使能时置重启标志(:2576) |
set_inv_igfollow | 全局 | 写 43249 特殊设置字中的 u8_igfollow 位(:2645) |
set_inv_freq_load_shed_mode | 全局 | 写 43226 频率减载模式(参数化入口)(:2697) |
set_inv_power_change_slpoe_limit | 全局 | 写 43222 功率变化斜率限制(:2727) |
set_inv_fault_recover_protect_value | 全局 | 写 43082 起 8 个寄存器(故障恢复点 + 首次并网点)(:2766) |
set_inv_reconnect_time | 全局 | 写 43106–43107 上电启动时间与故障恢复时间(:2820) |
set_inv_safety_config_data | 全局 | 写 43200 起 21 个寄存器,下发 PU/QU 曲线与功率因数点(:2861) |
set_inv_pu_v3_value | 全局 | 写 43202 P(U) 模式 V3 电压点(:2950) |
set_inv_pu_v4_value | 全局 | 写 43203 P(U) 模式 V4 电压点(:2988) |
set_inv_qu_v1_value | 全局 | 写 43208 Q(U) 模式 V1 电压点(:3026) |
set_inv_qu_v2_value | 全局 | 写 43209 Q(U) 模式 V2 电压点(:3064) |
set_inv_qu_v3_value | 全局 | 写 43210 Q(U) 模式 V3 电压点(:3102) |
set_inv_qu_v4_value | 全局 | 写 43211 Q(U) 模式 V4 电压点(:3140) |
set_inv_freq_derating_protect_value | 全局 | 写 43227 过频减载起始点(:3178) |
set_inv_freq_derating_protect_rate_value | 全局 | 写 43228 过频减载斜率(:3218) |
set_inv_pu_3tau_value | 全局 | 写 43403 P(U) 有功响应时间常数(:3256) |
send_inv_mppt_start_stop_switch | 全局 | 按 MPPT 百分比改 43302 的 bit9 后写回(:3297) |
ginlong_set_bit0_scan_enalbe | 全局 | 改 43302 bit0(TOR 安规)与 bit12(阴影扫描)后写回(:3338) |
ginlong_set_ovp_value | 全局 | 写 43090 起 8 个寄存器(一二级过欠压保护点及时间)(:3396) |
ginlong_set_ofp_value | 全局 | 写 43098 起 8 个寄存器(一二级过欠频保护点及时间)(:3451) |
ginlong_set_inv_ovp10min_value | 全局 | 写 43108–43109 十分钟过压保护(:3507) |
ginlong_set_inv_zero_current_voltage | 全局 | 写 43443 零电流模式电压(使能 8000 / 关闭 500)(:3547) |
ginlong_query_inv_zero_current_voltage | 全局 | 读 43443 零电流模式电压(:3588) |
set_low_voltage_cross_mode | 全局 | 写 43330 低压穿越模式(参数由调用方给出)(:3625) |
set_low_voltage_cross_mode_2 | 全局 | 写 43330:开 = 0x5A、关 = 0xA5(FRT 开关)(:3654) |
set_inv_italy_self_inspection | 全局 | 向 43079 写 2 触发意大利一键全检(:3711) |
ginlong_query_italy_self_inspection_enable | 全局 | 读 43079 一键全检使能值,未就绪则重试并置自检数据查询标志(:3742) |
query_italy_self_inspection_results | 全局 | 读 33334–33336 三相自检结论(:3792) |
query_italy_self_inspection_value1 | 全局 | 读 33223 起 20 个寄存器(L1 相逐项自检数据)(:3830) |
query_italy_self_inspection_value2 | 全局 | 读 33470 起 40 个寄存器(L2/L3 相逐项自检数据)(:3895) |
ginlong_get_self_test_data | 全局 | 触发三组自检查询并把三相原始数据汇总成 t_safty_tset_info[0..2](:3998) |
set_inv_isolation_limit_value | 全局 | 读比较后写 43246 绝缘阻抗限值(:4054) |
set_inv_ac_actpower | 全局 | 写 43052 AC 有功功率设定(:4122) |
clean_energy_statistics | 全局 | 向 43055 写 1 清除发电量统计(:4233) |
clean_overload_flag | 全局 | 向 43312 写 0xAA55 清除过载标志(:4265) |
set_safety | 全局 | 经 exchange_safety() 换算后写 43068 安规代码,成功后延时 5 s 重读运行数据(:4298) |
deal_bat_upgrade | 全局 | 电池升级期间读 43007 并据状态联动开关机,确认停机 ≥10 次后放行升级(:4341) |
set_inv_backup_bat_enable | 全局 | 读 43111 比较后写 43111 起 4 个寄存器(backup/bat 使能)(:4428) |
ginlong_query_inv_on_off_status | 全局 | 读 43007 返回 2(开机)/1(关机)/0(未知)(:4496) |
ginlong_comm_init | 全局 | 通道初始化:默认开机、backup 使能、低压穿越与 MPPT 查询标志置位、MPPT 100%(:4539) |
ginlong_inverter_data_check | 全局 | 数据一致性检查与限幅:电池保护参数取小、SOC 截止放电归零、backup 迟滞、安规/减载/阴影扫描/重启等标志派生(:4556) |
ginlong_inverter_comm | 全局 | 周期主调度:升级分支 → 失联检测 → 约 50 个标志位分派与时基轮询(:4861) |
4. 核心逻辑解析
4.1 Modbus-RTU 帧格式与组帧
读请求(0x03/0x04,8 字节) —— ems_send_query_data_to_inv_ginlong()(ginlong_comm_usart.c:273-290):
[0] 从机地址 = GINLONG_SLAVE_ADDRESS (0x01)
[1] 功能码 = 0x03 或 0x04
[2] 起始地址高 [3] 起始地址低
[4] 寄存器数量高 [5] 寄存器数量低
[6] CRC16 低字节 [7] CRC16 高字节 // 注释明确 "高位在后面"
CRC 由 GetCrcCode_Modbus()(common/crc.h:17)对前 6 字节计算,低字节在前(ginlong_comm_usart.c:285-287)。
写请求(0x06 单寄存器,8 字节 / 0x10 多寄存器,7+2N+2 字节) —— ems_send_ctrl_data_to_ginlong()(ginlong_comm_usart.c:299-332):
公共头:[0]=从机地址 [1]=功能码 [2..3]=起始地址
0x06 : [4..5] = 数据(由 memcpy(l_u8_tx_buf+4, tx_buff, 2) 覆盖) → l_u8_len = 6
0x10 : [4..5] = 寄存器数量 [6] = 字节数(2N) [7..7+2N-1] = 数据 → l_u8_len = 7 + 2N
尾部 : [l_u8_len] = CRC 低字节, [l_u8_len+1] = CRC 高字节
入参校验条件(:309):register_num <= 100 且 len_buff <= 200 且 len_buff == register_num * 2 三者同时成立才组帧。
收帧与校验 —— ginlong_recv_data()(ginlong_comm_usart.c:201-263):先确认 usart2_is_recv_async_data() 为真,然后循环等到 usart2_get_data_len() >= 2,超时基准 GINLONG_DAT_PCK_INTERVAL_INV(700 ms) 到点即返回长度 0;随后按帧长度逐字节 usart2_read_data_async() 读入 buf,用帧尾两字节做小端 CRC 拼接与 GetCrcCode_Modbus(buf, len-2) 比对:一致则 *len = l_u16_len,否则 *len = 0。
发送节流 —— ginlong_write_data()(ginlong_comm_usart.c:164-193):用 static u32 s_u32_send_pack_tick 记录上次发送时刻,若 OSTimeGet() - tick < 700 则 OSTimeDlyHMSM(0,0,0, 700 - elapsed) 阻塞补齐,再 usart2_clear_buffer() → usart2_send_data()。这是整个通道的 1.43 帧/秒硬上限:每轮 ginlong_inverter_comm() 里凡是实际发帧的分支都会各占用一个 700 ms 槽位,因此 u8_ems_inv_comm_flag 建立后每轮只会真正执行 1~2 个请求,其余标志留到下一轮。
响应判定约定:本文件不使用 Modbus 异常码语义,而是每个请求函数自行做"四要素"校验(除写函数外普遍一致):
(GINLONG_SLAVE_ADDRESS == buf[0]) && (期望功能码 == buf[1]) && (期望字节数 == buf[2]) && (recv_num > 0)
写函数(0x06/0x10)则用 (ADDR_xxx == (buf[2] << 8 | buf[3])) 校验寄存器回显地址。所有成功分支都会做 u32_ems_inv_comm_tick = OSTimeGet()(通信心跳),部分还会清掉对应的 u8_set_*_flag / u8_query_*_flag。
超时与重试:没有显式重试计数,重试由"请求标志未被清掉 + 主循环每轮重新进入该分支"自然形成。少数函数自加退避:set_inv_isolation_limit_value() 连续失败 >10 次后接受逆变器现值并放弃(:4079-4086);ginlong_query_italy_self_inspection_enable() 未就绪 >3 次后放弃并停掉后续查询(:3769-3774)。
4.2 主状态机:ginlong_inverter_comm() 三段式分支(ginlong_comm_usart.c:4861-5220)
if (is_inverter_firm_download_finished()) // ① 固件下载完成
→ 刷新心跳 / ginlong_data_init() / 清失联标记
→ 按 ginlong_read_upgrade_flag_master()/slave() 置 cUpdateMaster / cUpdateSlave
→ ginlong_upgrade_dsp_handle(); u8_ems_inv_comm_flag = FALSE
else if (Get_Status_Is_Updated_From_Server(UPDT_INV3)) // ② 服务器下发 ARM 升级
→ 刷新心跳 / ginlong_data_init() / 清失联标记 / 置 cUpdateArm
→ u8_ems_inv_comm_flag = FALSE; ginlong_upgrade_arm_handle()
else // ③ 正常运行
├─ 失联判定:|u32_ems_inv_comm_tick - OSTimeGet()| > 30000 ms
│ → u8_run_mode = cWaitMode; u8_ems_inv_comm_flag = FALSE;
│ u8_inverter_comm_lost_flag = TRUE; ginlong_data_init()
├─ tick 追平(大数回绕/时钟复位):tick > OSTimeGet() 时直接改写为当前值
├─ u8_ems_inv_comm_flag == FALSE(未建立通信)
│ → query_inv_product_model(); ginlong_data_init(); // 只做型号识别
└─ u8_ems_inv_comm_flag == TRUE(正常数据交换)
→ 先按标志位分派 30+ 个"请求型"函数(每个都可能占用 700 ms)
→ 再按 5 个时基 tick 做周期轮询
通信建立状态机(隐含在 query_inv_product_model() + 主循环之间):
u8_ems_inv_comm_flag = FALSE(ginlong_comm_init() 置) → 每轮只调 query_inv_product_model() → 解析出合法子码后 s_u8_querry_tick++;累计 >5 次(:1213)且子码与 g_t_inv_info.t_config_data.u8_inv_model_sub/硬件版本不一致时,调 set_inverter_sub() + memcpy 版本号 + save_job_post(INVERTER_CONFIG_PAGE) 落盘,然后 u8_inverter_comm_lost_flag = FALSE; u8_ems_inv_comm_flag = TRUE; Set_Setup_Config_Status(FALSE)(:1262-1265);若子码一致则直接置 u8_ems_inv_comm_flag = TRUE(:1267-1273)。
4.3 子状态机:set_inv_grid_parameter_tuning()(:1679-1819)
三步递进,用 static u8 s_u8_status(SAFETY_RESPONSE_MODE)推进,每步都靠"写 43050 成功"作为迁移条件:
| 当前状态 | 写 43050 的工作模式值 | 成功后的迁移 |
|---|---|---|
NO_RESPONSE_IDLE | NO_RESPONSE_MODE(0) | → ACTPOWER_RESPONSE(:1779-1782) |
ACTPOWER_RESPONSE | VOLT_WATT_DEFAULT(1) | 置 u8_allow_pu_set_flag = TRUE;若 PU V3/V4 标志都为 FALSE,则清 u8_allow_pu_set_flag 并 → REACPOWER_RESPONSE(:1783-1793) |
REACPOWER_RESPONSE | 按 t_safty_info.safty_mode_u.BIT 选 VOLT_VAR/FIXED_POWER_FACTOR/FIX_REACTIVE_POWER/POWER_PF,默认 VOLT_VAR(:1705-1726) | 置 u8_allow_qu_set_flag = TRUE;若 QU V1~V4 标志全为 FALSE,则清 u8_change_grid_parameter_tuning_flag 并回到 NO_RESPONSE_IDLE,同时重查安规配置(:1794-1810) |
若 allow_grid_parameter_tuning() 为假(非 T10 或安规不在白名单),直接清 u8_change_grid_parameter_tuning_flag(:1812-1815)。此状态机与主循环的门控条件 u8_query_safety_config_flag == FALSE && u8_change_grid_parameter_tuning_flag != FALSE(:4986-4990)耦合——必须先读完 43050-43109 才允许改。
4.4 关键算法与计算公式
(1) 型号识别(query_inv_product_model,:1156-1202):
l_u16_sub_temp = (buf[3] << 8) | buf[4](33000)→ 与 8 个子码宏比对映射为 SUB_* 枚举;主/从 DSP 版本由 buf[5]&0x0F、buf[6]、buf[7]&0x0F、buf[8] 格式化:snprintf(..., "V%X.%02X", buf[5] & 0x0F, buf[6]);SN 为 memcpy(u8_inv_sn, &buf[11], 15)。
(2) 功率因数与相电流(query_inv_rundata_1,:1412-1428):
s8_run_pf_value = (s32_inv_actpower_t * 10000) / s32_inv_apparentpower_t / 100 // 限幅 [-100, 100] → -1.00~1.00
s16_grid_cur_x = (s32_inv_actpower_x * 100) / grid_volt_x // 分相功率/电压 → 电流
注意 s32_inv_apparentpower_t 作为被除数,若为 0 会除零。
(3) 安规换算(:1435-1445):exchange_safety(sub, GINLONG_SAFETY, 33092 原始值) → u8_safety;若结果为 AS4777_2 再调 exchange_safetysub() 取子安规,否则 SAFESUB_DEFAULT。反向写回(set_safety,:4304-4309)用 STANDARD_SAFETY / STANDARD_SAFETYSUB 方向。
(4) PU/QU 曲线精度换算(对称的一对公式):
| 方向 | 公式 | 位置 |
|---|---|---|
| 读(逆变器 → EMS) | u16_p_v_mode_vX = (u16_pu_mode_vX_set * 10) / 23 | :2131-2140 |
| 写(EMS → 逆变器) | reg = (u16_p_v_mode_vX * 23) / 10 | :2956、:2994、:3032、:3070、:3108、:3146 |
| 响应时间 | 读 /30,写 *30 | :2136、:3263 |
| 功率百分比 | 读 u16_p_pf_mode_p2 = reg / 100,写 *100 | :2142、:2904 |
| 斜率 | 读 u16_rev_machine_rate = reg / 10,写 *10 | :2144、:2733 |
(5) 电池功率→电流折算与限幅(ems_send_bms_protect_paramter_to_inv,:2377-2472):
l_s16_dc_set_current = parallel_read_dc_total_set_bat_power() * 10 / u16_bat_volt
if (current > u16_dischg_max_rate_cur) current = u16_dischg_max_rate_cur;
if (current < -u16_chg_max_rate_cur) current = -u16_chg_max_rate_cur;
若 u8_ems_inv_comm_flag == FALSE → current = 0(通信未建立不下发)
方向字:current > 0 → [8..9] = 0x0001(放电),current < 0 → [8..9] = 0x0000(充电),取绝对值入 [10..11]
零电流特判:若 current == 0 且「充电满功率=0 且 放电满功率≠0 且 距上次非零设定 < 60 s」→ 保底放电流 3(`:2460-2465`)
(6) 防超额限幅(ginlong_inverter_data_check,:4571-4698):四路参数取"电池能力与逆变器能力中的较小/较保守者":
放电截止电压 = max(电池放电截止电压, config.u16_dischg_cut_rate_volt)(T10 强制 2000,其余强制 440)
充电截止电压 = min(电池充电截止电压, config.u16_chg_cut_rate_volt)(T10 强制 2880,其余强制 550;S5/B5 直接取 config)
放电最大电流 = min(电池放电电流, config.u16_dischg_max_rate_cur)
充电最大电流 = min(pvcharger_read_bat_max_charge_current(), config.u16_chg_max_rate_cur)
若 SOC <= logic_read_ups_reserve_soc()*10 且 cNormalMode → 放电最大电流归零
结果只在四值与 t_run_info 现值不一致时才写入。
(7) 失联/心跳:u32_ems_inv_comm_tick 是唯一心跳源,由每一个成功解析的响应刷新;>30 s 未刷新即判定失联(:4890)。同时多处做 if (tick > OSTimeGet()) tick = OSTimeGet(); 的"倒挂修正"(:4898、:5174、:5184 等),说明设计上预期 OSTimeGet() 可能回绕。
4.5 定时/调度时序
ginlong_write_data() 的 700 ms 节流是唯一的物理时间约束,主循环里所有轮询周期的真实分辨率都以它为颗粒(单位 ms):
| 时基变量 | 周期 | 触发函数 | 位置 |
|---|---|---|---|
u32_send_bms_protect_parameter_to_inv_tick | 900 ms | ems_send_bms_protect_paramter_to_inv() | :5209-5213 |
u32_query_run_data_1_tick | 2*TIME_1S = 2 s | query_inv_rundata_1() | :5189-5193 |
u32_query_run_data_2_tick | 2*TIME_1S = 2 s | query_inv_rundata_2() | :5199-5203 |
u32_query_bat_protect_parameter_tick | 9000 ms | query_ems_send_bms_protect_paramter_to_inv() | :5179-5183 |
u32_query_safety_regulation_data_tick | 5*60*1000 = 5 min | 仅重置 tick 与标志(无实际发帧) | :5168-5173 |
u32_ems_inv_comm_tick | 30 s 超时 | 失联判定 | :4890 |
另有阻塞式延时:ginlong_write_data 的发送节流 OSTimeDlyHMSM(0,0,0,n)、query_inv_product_model() 入口 OSTimeDlyHMSM(0,0,1,500)(1.5 s,:1132)、ginlong_restart() 的 500 ms(:1112)、set_safety() 成功后的 5 s(:4327)。这些都在逆变器轮询任务上下文里同步阻塞,会拖慢同任务内其他调度。
任务/同步机制:本文件不使用信号量或消息邮箱,全部同步都基于 uCOS-II 的全局节拍 OSTimeGet() + OSTimeDlyHMSM() 轮询;跨任务通信完全靠共享全局变量(标志位 + 数据缓存)。因此没有任何互斥保护。
4.6 关键全局变量的读写关系
| 变量 | 本文件写入 | 本文件读取 | 触发条件 |
|---|---|---|---|
g_t_ginlong_info.t_ginlong_comm_info.u8_ems_inv_comm_flag | ginlong_data_init() 置 TRUE、失联/升级时置 FALSE、query_inv_product_model() 型号确认后置 TRUE | 主循环门控、ems_send_bms_protect_paramter_to_inv() 决定是否限流为 0 | 通信建立/失联 |
g_t_ginlong_info.t_ginlong_comm_info.u32_ems_inv_comm_tick | 每个成功响应 | 失联判定、倒挂修正 | 每次收发成功 |
g_t_ginlong_info.t_ginlong_rundata.* | query_inv_rundata_*、query_safety_regulations_info_2、query_inv_mppt_switch 等 | deal_system_mode、deal_inv_fault、ginlong_inverter_data_check | 轮询解析 |
g_t_ginlong_info.t_ginlong_safety_info.* | 各类 query_* 与 set_* 回读 | set_* 的"相同则跳过"比较、ginlong_inverter_data_check | 安规查询/下发 |
g_t_ginlong_info.t_control_cmd.u8_backup_set | ginlong_inverter_data_check 按 ups_enable + 故障/升级/电池连接状态赋值,带 s_u8_inspect_backup_flag 迟滞 | set_inv_backup_bat_enable、ems_send_bms_protect_paramter_to_inv | UPS 状态变化 |
g_t_inv_info.t_run_info.u8_run_mode | deal_system_mode、deal_inv_fault、升级分支 | 全工程 | 状态解析 |
g_t_inv_info.t_run_info.u_fault_code / u_fault_extend1_code | deal_inv_fault() 全量翻译 | 上层告警 | 故障位域变化 |
g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag | 失联置 TRUE / 型号确认与升级分支置 FALSE | 上层 | 失联/恢复 |
g_t_inv_info.t_config_data.u8_inv_model_sub / u8_hw_ver | query_inv_product_model() 经 set_inverter_sub() | ginlong_data_init() 按型号填默认值 | 初次识别或换机 |
g_t_inv_info.t_config_data.u8_safety / u8_safesub | query_inv_rundata_1() 连续 5 次不一致后写回(:1447-1458) | set_safety()、allow_grid_parameter_tuning() | 逆变器安规被改 |
g_t_inv_info.t_run_info.u16_dischg_cut_rate_volt 等 4 个限值 | ginlong_inverter_data_check() | ems_send_bms_protect_paramter_to_inv() | 电池/配置变化 |
g_t_inv_info.t_control_cmd.u8_reset | ginlong_inverter_data_check() 消费后清 FALSE | 同函数 | 复位请求 |
g_t_inv_info.t_safty_info.* | 各 query_safety_*(首查或上传标志时同步)、query_italy_self_inspection_value1/2 | 各 set_* 下发 | 安规查询 |
g_t_inv_info.t_safty_tset_info[0..2] | ginlong_get_self_test_data() | HMI/服务器 | 自检完成 |
4.7 典型业务流程:下发一个 AC 有功设定
- 上层置
u8_change_grid_parameter_tuning_flag(或ginlong_data_init()因allow_grid_parameter_tuning()为真而置,:139-144)。 - 主循环先执行
query_safety_regulations_info()把u8_query_safety_config_flag清掉(:1878)。 - 下一轮进入
set_inv_grid_parameter_tuning(),读t_safty_info.s32_...各值 → 越界保护(无功 >6000 归 0、有功 >11000 归 0、功率因数绝对值不在 [800,1000] 归 1000,:1734-1747)→ 组 5 寄存器0x10帧发 43050。 - 校验回显地址
ADDR_SET_WORK_MODE后推进状态机并刷新心跳。 ginlong_inverter_data_check()发现t_safty_info.u16_inv_ac_power_setting != 10000且开了三相不平衡且机型为 T10 时,直接调set_inv_ac_actpower(10000)并把u8_query_safety_config_flag重新置 TRUE(:4783-4787),形成"设置后必须回读校验"的闭环。
5. 对外接口
5.1 本文件导出、供外部调用的函数(头文件正式声明 4 个:ginlong_comm_usart.h:930-933)
| 函数 | 调用方 |
|---|---|
ginlong_comm_init | inverter.c:82(逆变器上电初始化) |
ginlong_inverter_comm | inverter.c:1105(GINLONG_TYPE 轮询分支) |
ginlong_inverter_data_check | inverter.c:1106 |
ginlong_get_self_test_data | inverter.c:314(意大利自检汇总处) |
5.2 本文件导出但未在头文件声明的 64 个全局函数
ginlong_data_init、ginlong_write_data、ginlong_recv_data、deal_system_mode、deal_inv_fault、inv_on_off_command、ginlong_restart、query_inv_product_model、set_inv_rtc_time、query_inv_rundata_1、query_inv_rundata_2、query_safety_test_data、query_safety_regulations_info、query_ems_send_bms_protect_paramter_to_inv、query_safety_regulations_info_2、query_inv_freq_load_shed_mode、query_inv_mppt_switch、query_low_voltage_cross、query_inv_pu_mode_active_response_3tau、ems_send_bms_protect_paramter_to_inv、set_inv_three_unbalance_mode、set_inv_igfollow、set_inv_freq_load_shed_mode、set_inv_power_change_slpoe_limit、set_inv_fault_recover_protect_value、set_inv_reconnect_time、set_inv_safety_config_data、set_inv_pu_v3_value、set_inv_pu_v4_value、set_inv_qu_v1_value、set_inv_qu_v2_value、set_inv_qu_v3_value、set_inv_qu_v4_value、set_inv_freq_derating_protect_value、set_inv_freq_derating_protect_rate_value、set_inv_pu_3tau_value、send_inv_mppt_start_stop_switch、ginlong_set_bit0_scan_enalbe、ginlong_set_ovp_value、ginlong_set_ofp_value、ginlong_set_inv_ovp10min_value、ginlong_set_inv_zero_current_voltage、ginlong_query_inv_zero_current_voltage、set_low_voltage_cross_mode、set_low_voltage_cross_mode_2、set_inv_italy_self_inspection、ginlong_query_italy_self_inspection_enable、query_italy_self_inspection_results、query_italy_self_inspection_value1、query_italy_self_inspection_value2、set_inv_isolation_limit_value、set_inv_ac_actpower、clean_energy_statistics、clean_overload_flag、set_safety、deal_bat_upgrade、set_inv_backup_bat_enable、ginlong_query_inv_on_off_status。
(实测工程内对上述函数的跨文件调用只有 ginlong_comm_usart.c 自身;grep -rn 未在 ginlong_comm_usart.c 之外找到调用点,因此这批函数实质是"外部可见但仅内部使用"的死接口,详见第 7 节。)
5.3 本文件导出的全局变量
| 名称 | 去向 |
|---|---|
g_t_ginlong_info | 经 ginlong_comm_usart.h:927 的 extern 暴露给全工程;实际读写方集中在 ginlong_comm_usart.c 与 midware/inverter/ginlong/ginlong_comm_can.c、HMI/服务器上报链路 |
g_u8_inv_recv_buf | 无 extern 声明,等效文件私有(仅本文件使用) |
5.4 本文件调用的外部函数/变量
| 来源模块 | 使用的符号 |
|---|---|
hardware/usart2/usart2.h | usart2_clear_buffer、usart2_send_data、usart2_read_data_async、usart2_get_data_len、usart2_is_recv_async_data |
common/crc.h | GetCrcCode_Modbus |
midware/inverter/inverter.h/.c | g_t_inv_info(t_run_info/t_config_data/t_safty_info/t_control_cmd/t_safty_tset_info)、set_inverter_sub、get_inverter_sub、inverter_read_safety、inverter_read_run_mode、inverter_read_grid_freq、inverter_read_bat_volt、inverter_get_igfollow_mode、inverter_set_igfollow_mode、inverter_get_three_unbalance_mode、is_inverter_firm_download_finished、IsUpgradeBMS、get_inverter_control_bat_disable_flag、set_inverter_control_bat_disable_flag、cWaitMode/cNormalMode/cByPassMode/cUPS/cFaultMode/cUpdateMaster/cUpdateSlave/cUpdateArm |
midware/inverter/ginlong/ginlong_upgrade.h | ginlong_upgrade_dsp_handle、ginlong_upgrade_arm_handle、ginlong_read_upgrade_flag_master、ginlong_read_upgrade_flag_slave |
midware/bms/bms.h | bms_get_ready_enable |
midware/pvcharger(pvcharger.h) | pvcharger_read_bat_curr、pvcharger_read_bat_soc、pvcharger_read_bat_relay_state、pvcharger_read_bat_max_charge_current、pvcharger_read_bat_max_charge_power、pvcharger_read_bat_max_dischg_power、pvcharger_read_bat_dischg_cut_off_volt、pvcharger_read_bat_charge_cut_off_volt、pvcharger_read_bat_connect_state |
midware/inverter/parallel(parallel.h) | parallel_read_dc_total_set_bat_power、parallel_read_freq_dispatch_en |
logic/logic.h | logic_read_ups_reserve_soc、logic_read_dc_set_power(仅调试打印用) |
data/data.h、data/system_config.h | SYSTEM_DATETIME、save_job_post、INVERTER_CONFIG_PAGE、Set_Setup_Config_Status、Get_Status_Is_Updated_From_Server、UPDT_INV3、system_aux 相关 |
net/ems(ems.h)、net/inv_download.h、net/bms_download.h | include 引入(升级链路) |
| 标准库 | memcpy、strcmp、snprintf、abs |
6. 依赖关系
6.1 依赖(本文件 include / 使用的外部模块)
ginlong_comm_usart.h 本通道的类型、宏、extern g_t_ginlong_info
ginlong_upgrade.h DSP / ARM 固件升级流程与升级标志查询
inverter.h g_t_inv_info、机型枚举 SUB_*、运行模式枚举 c*Mode、逆变器读写访问器
bms.h bms_get_ready_enable(判定能否进 cNormalMode)
meter.h 电表(include 链)
usart2.h 物理串口收发(实测头文件在 hardware/usart2/usart2.h)
crc.h GetCrcCode_Modbus(Modbus CRC16)
data.h / system_config.h g_t_config_data、SYSTEM_DATETIME、save_job_post 掉电保存
logic.h logic_read_ups_reserve_soc / logic_read_dc_set_power
inv_download.h / bms_download.h / ems.h / system_aux.h 升级与系统辅助
6.2 被依赖(哪些文件会用到它)
| 文件 | 使用方式 |
|---|---|
midware/inverter/inverter.c | #include 后调 ginlong_comm_init()(:82)、ginlong_inverter_comm()(:1105)、ginlong_inverter_data_check()(:1106)、ginlong_get_self_test_data()(:314) |
midware/inverter/inverter.h 的 include 链 / 各 HMI、服务器上报、并机、BMS 模块 | 通过 ginlong_comm_usart.h 的 extern GINLONG_TABLE_T g_t_ginlong_info 读写锦浪通道状态与标志位 |
midware/inverter/ginlong/ginlong_upgrade.c | 与本文件互相引用:本文件调 ginlong_upgrade_*_handle(),升级流程回写 u8_ems_inv_comm_flag、u8_run_mode |
| 并机 / 数据处理模块 | 置 u8_set_* 标志、读 t_ginlong_rundata(解耦式依赖) |
7. 备注与风险点
7.1 缓冲区越界风险(高)
ginlong_recv_data()无长度上界校验(ginlong_comm_usart.c:214-232)。l_u16_len = usart2_get_data_len()最大可达USART2_BUFFER_LEN(512)(hardware/usart2/usart2.h:6),而目标缓冲g_u8_inv_recv_buf只有 256 字节,循环*(buf + l_u16_index) = data没有任何if (index >= sizeof(buf)) break。若逆变器返回超长帧或串口粘包/噪声导致长度异常,会越过全局缓冲越界写,破坏相邻全局变量。ems_send_ctrl_data_to_ginlong()的发送缓冲上界不足(:301、:309)。栈上缓冲为u8 l_u8_tx_buf[128],但校验只要求len_buff <= GINLONG_MAX_REGISTER_NUM * 2(=200),此时memcpy(l_u8_tx_buf + 7, tx_buff, len_buff)会写到下标 206,越界 79 字节。当前调用点最大NUM_INV_SAFETY_MODE_SET1=21(42 字节)尚在 128 内,但接口本身不设防。GINLONG_MAX_REGISTER_NUM(100) 与缓冲(128) 之间存在明显不一致。
7.2 组帧缺陷
- 入参校验失败时仍会发帧(
:309-331)。若register_num/len_buff不满足条件,if块整体跳过,l_u8_len保持 0,函数仍执行ginlong_write_data(l_u8_tx_buf, 0, ...)—— 发出一帧"长度 0"的垃圾数据(内容为未初始化的栈缓冲)。应改为直接 return。 - 函数名拼写与语义不符:
ems_send_bms_protect_paramter_to_inv(paramter拼写错误,:2369)、ginlong_set_bit0_scan_enalbe(enalbe,:3338)、set_inv_power_change_slpoe_limit(slpoe,:2727)、query_inv_pu_mode_active_response_3tauvsADDR_INV_PU_MODE_ACTIVE_RESPONSE_3TAU命名不一致。宏名u16_ongrid_fault单复数混用。 ginlong_set_ofp_value()拷贝粘贴错误(:3469-3480):43098–43105 的 8 个寄存器本应分别写 ofp1/ufp1 的保护点与时间共 4 组,但代码把u16_ofp1_value/u16_ofp1_time写了两遍([0..3]与[4..7]),u16_ofp2_value/u16_ofp2_time完全没用上,随后[8..15]又写 ufp1。从ginlong_set_ovp_value()(:3412-3427,ovp1/ovp2/uvp1/uvp2 四组各一)的对称结构看,这里应为 ofp1/ofp2/ufp1/ufp2,属实际功能缺陷(二级过频点无法设置)。
7.3 数据解析缺陷
query_italy_self_inspection_value1()/value2()的字节偏移错位(:3859、:3926):u16_ufp1_value = (buf[23] << 8) | buf[23]—— 高字节与低字节用了同一个下标 23,应为buf[23]与buf[24]。同类写法在query_italy_self_inspection_value1(:3859)与value2的 L2(:3926)各出现一次,而 L3 段(:3944)却是正确的buf[63]/buf[64],说明是拷贝后漏改。u16_pu_mode_*_time系列的运算优先级错误(:1902、:1904、:1907等):(u16)((buf[89] << 8) | buf[90] *10)——*10只作用于buf[90],而:1900、:1901却是((buf[85] << 8) | buf[86]) * 10。同一批寄存器里两种写法混用,时间值会差 10 倍或产生错值。query_safety_regulations_info()中u16_ovp10min_time被整行注释掉(:1920、:1951、:2005),但ginlong_set_inv_ovp10min_value()(:3513)仍在比较t_safty_info.u16_ovp10min_time == t_ginlong_safety_info.u16_ovp10min_time决定是否下发 —— 该字段从未被赋值(恒为初值),导致下发判断失去意义。.h中s16_qu_mode_lag_reactpower_value被声明为u16(ginlong_comm_usart.h:750),而代码按有符号负数使用(set_inv_safety_config_data写入-4360)。类型与语义不符,依赖隐式截断。inv_on_off_command()的u8 l_u8_tx_buf[3]只填 2 字节(:1062、:1075-1076),实际只用前 2 个,属冗余但无害。set_inv_grid_parameter_tuning()寄存器数量与注释矛盾:写ADDR_SET_WORK_MODE(43050) 共NUM_SET_WORK_MODE(5) 个寄存器,但注释写 "解析数据 43117"(:1771),是从电池保护参数处拷贝来的残留注释。
7.4 并发与实时性
- 全程无互斥。所有状态都放在全局变量里由逆变器轮询任务单线程读写,但
g_t_ginlong_info.*被 HMI/服务器/并机任务直接读写。u16及以上的标志(如u16_query_mppt_start_stop_flag、u16_set_mppt_start_stop_flag)在多任务环境下存在读改写竞态。 - 同步阻塞拖慢轮询:
ginlong_write_data()内的OSTimeDlyHMSM(最多 700 ms/帧)、ginlong_restart()的 500 ms、set_safety()的 5 s、query_inv_product_model()的 1.5 s,全部在逆变器轮询任务里同步阻塞。set_safety()一次阻塞 5 s,期间同任务的其他调度(含 900 ms 的电池保护参数下发)全部停摆。 query_inv_product_model()的s_u8_querry_tick只增不清零的边界:若型号一直不匹配,s_u8_querry_tick会持续累加(u8上限 255),但两个出口分支都会s_u8_querry_tick = 0,实际不会溢出;不过"型号不匹配但子码非 0"时会在:1267直接进入u8_ems_inv_comm_flag = TRUE,跳过配置落盘,可能让u8_inv_model_sub保持旧值。
7.5 死代码与遗留
- 超长注释代码块:
ems_send_three_ac_actpower_to_inv()整函数被注释(:4158-4224,约 67 行);ginlong_inverter_data_check()中弱电网 98% 充电限制逻辑(:4590-4598)、S5 低 SOC 保护(:4666-4686)与其配套的u8_backup_set修正(:4770-4774)也被注释,但s_u8_soc_low_backup_disable_flag变量声明保留(:4563)。 - 主循环里的空转分支:
:5168-5173的u32_query_safety_regulation_data_tick5 分钟分支只是重置 tick 和标志,并不调用任何查询函数(真正的安规查询由u8_query_safety_config_flag驱动),属遗留的无效调度。 ginlong_query_inv_zero_current_voltage()在主循环被注释(:5112-5115),导致set_inv_zero_current_mode_flag置位后置出的u8_query_zero_current_mode_flag(:3577)永远无人消费。但ginlong_set_inv_zero_current_voltage()本身仍在循环里(:4981-4984),所以"设置"通、"回读校验"不通。deal_bat_upgrade()重复调用ginlong_recv_data()(:4413):函数末尾又执行一次接收(用于读取inv_on_off_command()的响应),但该响应已在:4352被读过,第二次读到的内容不确定;且l_u16_inv_recv_num被复用,语义混乱。- 头文件声明不完整:
ginlong_comm_usart.h只声明 4 个函数(:930-933),其余 64 个全局函数靠隐式声明或调用方自行extern。本文件内ems_send_query_data_to_inv_ginlong等 4 个static函数有正确的static声明,但allow_grid_parameter_tuning还需在:37手工前置声明(因为ginlong_data_init()在前、定义在后)。 - 大量魔法数字:
200/2880(T10 截止电压)、440/550(其他机型)、8000/500(零电流电压)、0x5A/0xA5(FRT 开关)、0xAA/0x55(清过载)、0xBE/0xDE(开关机)、2300/4999(backup 电压/频率)、4360/-4360/-950/-900(TOR 安规无功/功率因数)、阈值6000/11000/800/1000、15(力充电 SOC 门限)等均无宏或注释说明来源。 s_u8_safety_change初值5(:4567):用5而非合法安规码作为"未初始化"哨兵值,与inverter_read_safety()首次比较时必然不等,会在第一次进入u8_ems_inv_comm_flag分支时无条件置u8_set_bit0_scan_enalbe = TRUE(:4816-4820)——属有意为之的"首次强制同步",但用魔数表达,可读性差。s32_inv_apparentpower_t作除数无零保护(:1412),逆变器视在功率为 0 时(如待机)会产生除零异常(Cortex-M4 上整数除零默认返回 0 或触发 UsageFault,取决于编译器运行时)。