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

ginlong_comm_usart.c

源文件路径: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_infoGINLONG_TABLE_T(全局定义,ginlong_comm_usart.c:31)锦浪通道唯一状态表,聚合标志位表、运行数据、控制命令、安规参数四大块;全工程通过 ginlong_comm_usart.h:927 的 extern 引用
g_u8_inv_recv_bufu8[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_datas_u32_send_pack_tick发送节流基准 tick
query_inv_product_models_u8_querry_tick型号查询成功计数,>5 才允许写回配置
query_inv_rundata_1s_u8_safety_tick安规不一致计数,>5 才写回配置
set_inv_grid_parameter_tunings_u8_status三步状态机(NO_RESPONSE_IDLE→ACTPOWER_RESPONSE→REACPOWER_RESPONSE)
query_safety_regulations_info / query_safety_regulations_info_2 / query_inv_pu_mode_active_response_3taus_u8_first_query首次查询才把逆变器值同步到 EMS
ginlong_query_italy_self_inspection_enables_u8_cnt全检使能未就绪重试计数,>3 放弃
set_inv_isolation_limit_values_u8_set_flag写入失败重试计数,>10 放弃并接受逆变器现值
ems_send_bms_protect_paramter_to_invs_u32_dc_set_power_tick最近一次非零功率设定时刻(0 电流保底放电用)
ginlong_inverter_data_checks_u8_inspect_backup_flag、s_u32_inv_status_check_tick、s_u8_force_charge_flag、s_u8_safety_changebackup 迟滞状态、Wait 超时计时、强制充电状态、上一次安规值
deal_bat_upgrades_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_ginlongstatic组 8 字节 0x03/0x04 读帧(从机地址+功能码+起始地址+数量+CRC)并发送(:273)
ems_send_ctrl_data_to_ginlongstatic组 0x06(8 字节)/0x10(7+2N+2 字节) 写帧并发送,含入参上界校验(:299)
exchange_safetystatic大安规代码双向映射(EMS 标准码 ↔ 锦浪码),按机型分 T10 与新平台两套(:341)
exchange_safetysubstatic子安规(澳 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_tuningstatic仅 T10 且安规为 VDE4105/PEA/MEA/TOR 时返回 TRUE(:1645)
set_inv_grid_parameter_tuningstatic写 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_IDLENO_RESPONSE_MODE(0)→ ACTPOWER_RESPONSE(:1779-1782)
ACTPOWER_RESPONSEVOLT_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_tick900 msems_send_bms_protect_paramter_to_inv():5209-5213
u32_query_run_data_1_tick2*TIME_1S = 2 squery_inv_rundata_1():5189-5193
u32_query_run_data_2_tick2*TIME_1S = 2 squery_inv_rundata_2():5199-5203
u32_query_bat_protect_parameter_tick9000 msquery_ems_send_bms_protect_paramter_to_inv():5179-5183
u32_query_safety_regulation_data_tick5*60*1000 = 5 min仅重置 tick 与标志(无实际发帧):5168-5173
u32_ems_inv_comm_tick30 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_flagginlong_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_setginlong_inverter_data_check 按 ups_enable + 故障/升级/电池连接状态赋值,带 s_u8_inspect_backup_flag 迟滞set_inv_backup_bat_enable、ems_send_bms_protect_paramter_to_invUPS 状态变化
g_t_inv_info.t_run_info.u8_run_modedeal_system_mode、deal_inv_fault、升级分支全工程状态解析
g_t_inv_info.t_run_info.u_fault_code / u_fault_extend1_codedeal_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_verquery_inv_product_model() 经 set_inverter_sub()ginlong_data_init() 按型号填默认值初次识别或换机
g_t_inv_info.t_config_data.u8_safety / u8_safesubquery_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_resetginlong_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 有功设定

  1. 上层置 u8_change_grid_parameter_tuning_flag(或 ginlong_data_init() 因 allow_grid_parameter_tuning() 为真而置,:139-144)。
  2. 主循环先执行 query_safety_regulations_info() 把 u8_query_safety_config_flag 清掉(:1878)。
  3. 下一轮进入 set_inv_grid_parameter_tuning(),读 t_safty_info.s32_... 各值 → 越界保护(无功 >6000 归 0、有功 >11000 归 0、功率因数绝对值不在 [800,1000] 归 1000,:1734-1747)→ 组 5 寄存器 0x10 帧发 43050。
  4. 校验回显地址 ADDR_SET_WORK_MODE 后推进状态机并刷新心跳。
  5. 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_initinverter.c:82(逆变器上电初始化)
ginlong_inverter_comminverter.c:1105(GINLONG_TYPE 轮询分支)
ginlong_inverter_data_checkinverter.c:1106
ginlong_get_self_test_datainverter.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.husart2_clear_buffer、usart2_send_data、usart2_read_data_async、usart2_get_data_len、usart2_is_recv_async_data
common/crc.hGetCrcCode_Modbus
midware/inverter/inverter.h/.cg_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.hginlong_upgrade_dsp_handle、ginlong_upgrade_arm_handle、ginlong_read_upgrade_flag_master、ginlong_read_upgrade_flag_slave
midware/bms/bms.hbms_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.hlogic_read_ups_reserve_soc、logic_read_dc_set_power(仅调试打印用)
data/data.h、data/system_config.hSYSTEM_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.hinclude 引入(升级链路)
标准库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 缓冲区越界风险(高)

  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。若逆变器返回超长帧或串口粘包/噪声导致长度异常,会越过全局缓冲越界写,破坏相邻全局变量。
  2. 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 组帧缺陷

  1. 入参校验失败时仍会发帧(:309-331)。若 register_num/len_buff 不满足条件,if 块整体跳过,l_u8_len 保持 0,函数仍执行 ginlong_write_data(l_u8_tx_buf, 0, ...) —— 发出一帧"长度 0"的垃圾数据(内容为未初始化的栈缓冲)。应改为直接 return。
  2. 函数名拼写与语义不符: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_3tau vs ADDR_INV_PU_MODE_ACTIVE_RESPONSE_3TAU 命名不一致。宏名 u16_ongrid_fault 单复数混用。
  3. 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 数据解析缺陷

  1. 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],说明是拷贝后漏改。
  2. 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 倍或产生错值。
  3. 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 决定是否下发 —— 该字段从未被赋值(恒为初值),导致下发判断失去意义。
  4. .h 中 s16_qu_mode_lag_reactpower_value 被声明为 u16(ginlong_comm_usart.h:750),而代码按有符号负数使用(set_inv_safety_config_data 写入 -4360)。类型与语义不符,依赖隐式截断。
  5. inv_on_off_command() 的 u8 l_u8_tx_buf[3] 只填 2 字节(:1062、:1075-1076),实际只用前 2 个,属冗余但无害。
  6. set_inv_grid_parameter_tuning() 寄存器数量与注释矛盾:写 ADDR_SET_WORK_MODE(43050) 共 NUM_SET_WORK_MODE(5) 个寄存器,但注释写 "解析数据 43117"(:1771),是从电池保护参数处拷贝来的残留注释。

7.4 并发与实时性

  1. 全程无互斥。所有状态都放在全局变量里由逆变器轮询任务单线程读写,但 g_t_ginlong_info.* 被 HMI/服务器/并机任务直接读写。u16 及以上的标志(如 u16_query_mppt_start_stop_flag、u16_set_mppt_start_stop_flag)在多任务环境下存在读改写竞态。
  2. 同步阻塞拖慢轮询: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 的电池保护参数下发)全部停摆。
  3. 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 死代码与遗留

  1. 超长注释代码块: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)。
  2. 主循环里的空转分支::5168-5173 的 u32_query_safety_regulation_data_tick 5 分钟分支只是重置 tick 和标志,并不调用任何查询函数(真正的安规查询由 u8_query_safety_config_flag 驱动),属遗留的无效调度。
  3. 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),所以"设置"通、"回读校验"不通。
  4. deal_bat_upgrade() 重复调用 ginlong_recv_data()(:4413):函数末尾又执行一次接收(用于读取 inv_on_off_command() 的响应),但该响应已在 :4352 被读过,第二次读到的内容不确定;且 l_u16_inv_recv_num 被复用,语义混乱。
  5. 头文件声明不完整: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() 在前、定义在后)。
  6. 大量魔法数字: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 门限)等均无宏或注释说明来源。
  7. s_u8_safety_change 初值 5(:4567):用 5 而非合法安规码作为"未初始化"哨兵值,与 inverter_read_safety() 首次比较时必然不等,会在第一次进入 u8_ems_inv_comm_flag 分支时无条件置 u8_set_bit0_scan_enalbe = TRUE(:4816-4820)——属有意为之的"首次强制同步",但用魔数表达,可读性差。
  8. s32_inv_apparentpower_t 作除数无零保护(:1412),逆变器视在功率为 0 时(如待机)会产生除零异常(Cortex-M4 上整数除零默认返回 0 或触发 UsageFault,取决于编译器运行时)。