goodwe_comm.h
GOODWE_TABLE_T 的字段分组与并发语义4.5 故障/告警位域的排布约束5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/inverter/goodwe/goodwe_comm.h所属模块:midware/inverter/goodwe(古瑞瓦特 GoodWe 逆变器通信) 代码行数:417
1. 文件职责
本头文件是 GoodWe(古瑞瓦特)逆变器 Modbus-RTU 通信模块的对外契约,集中定义 EMS 与逆变器交互所需的全部寄存器地址常量、协议功能码、型号/安规枚举和故障/告警位域联合体。它把逆变器寄存器手册里的地址表原样搬进代码,使 goodwe_comm.c 的每个查询/下发函数都能用语义化宏(如 ADDR_REGISTER_QUERY_INV_RUNNING_DATA)代替裸数字。文件中定义的 GOODWE_TABLE_T 是模块唯一的运行时状态表,通过 extern GOODWE_TABLE_T g_t_goodwe_comm_info; 暴露给 inverter.c 等上层模块做标志位设置与状态读取。同时它只导出 5 个函数原型,是 inverter.c 调用本模块的全部入口面。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
GOODWE_TABLE_T | 结构体 | 模块全局状态表(goodwe_comm.h:350)。15 个 u8_*_flag 触发标志 + 8 个 u32_*_tick 周期时间戳 + 若干 u16 回读缓存 + u8_inv_device_type[10] + u8_inv_model_sub + 两个联合体实例 |
GOODWE_ERROR_U | 联合体(位域) | u32_all 与 4 字节 32 个故障位共用(goodwe_comm.h:263);byte4(bit24~31)在源码中被整段注释,实际不可用 |
GOODWE_WARNNING_U | 联合体(位域) | u16_all 与 2 字节 16 个告警位共用(goodwe_comm.h:321) |
GOODWE_POWER_MODE_E | 枚举 | 写入 47511 的功率模式字:AUTO=0x0001、CHARGE_PV、DISCHARGE_PV、IMPORT_AC、EXPORT_AC、CONSERVE、OFF_GRID、BATTERY_STANBY、BUY_POWER、SELL_POWER、CHARGE_BAT、DISCHARGE_BAT、STOP=0x00FF |
GOODWE_LED_DISPLAY_E | 枚举 | LED 语义:LED_OFF=0x01、LED_ON、LED_FLASH_1X、LED_FLASH_2X、LED_FLASH_4X(goodwe_comm.h:164) |
GOODWE_OPERATION_MODE_E | 枚举 | 逆变器上报运行态:WAIT_E=0、ON_GRID_E、OFF_GRID_E、FAULT_E、FLASH_E、CHECK_E(goodwe_comm.h:173) |
GOODWE_SAFETY_COUNTRY_E | 枚举 | GoodWe 安规国家码,GW_ITALY=0 顺序递增到 GW_AUSTRALIA_P_C,另有跳跃值 GW_UL1741=0x61、GW_PR_240VAC=0x77(goodwe_comm.h:183) |
GOODWE_INV_DEBUG | 宏 | (0),总调试开关,控制本模块所有 printf |
MAX_REGISTER_NUM | 宏 | 100,单帧读写寄存器数上限,ems_send_ctrl_data_to_inv 的入参校验上界 |
INV_RECV_BUFFER_SIZE | 宏 | 256,UART4 收帧缓冲长度 |
GOODWE_DAT_PCK_INTERVAL_INV | 宏 | 100(ms),两次 Modbus 发送的最小间隔 |
GOODWE_SLAVE_ADDRESS | 宏 | 0xF7,Modbus 从站地址 |
FUNC_READ_HOLDING_REGISTER | 宏 | 0x03 读保持寄存器 |
FUNC_WRITE_SIGNAL_HOLDING_REGISTER | 宏 | 0x06 写单个保持寄存器 |
FUNC_WRITE_MULTIPLE_HOLDING_REGISTER | 宏 | 0x10 写多个保持寄存器 |
ALPHA_SAFETY / GOODWE_SAFETY | 宏 | 0 / 1,parse_goodwe_safety() 的方向选择 |
INV_TYPE_ALPHA | 宏 | 10,写入 47011 把系统类型标为 Alpha |
GW_ABP_SOC_PROTECT | 宏 | 95,离网充电截止 SOC 上限 |
ON / OFF | 宏 | 1 / 0 |
ADDR_REGISTER_QUERY_INV_DEVICE_INFO + NUM_QUERY_INV_DEVICE_INFO | 宏 | 35000 + 21,设备信息区 |
ADDR_REGISTER_QUERY_INV_RUNNING_DATA + NUM_QUERY_INV_RUNNING_DATA | 宏 | 35100 + 122,运行数据区(本模块最大的单次读取) |
ADDR_REGISTER_QUERY_INV_ENERGY_DATA + NUM_QUERY_INV_ENERGY_DATA | 宏 | 35191 + 21,能量数据区(本版本未被使用) |
ADDR_REGISTER_QUERY_INV_ARC_MODULE + NUM_QUERY_INV_ARC_MODULE | 宏 | 36067 + 2,ARC 模块版本 |
ADDR_REGISTER_CLEAR_ENERGY_INV + NUM_CLEAR_ENERGY_INV | 宏 | 45217 + 1,清除电量统计 |
ADDR_REGISTER_RESET_INV | 宏 | 45220,复位逆变器(写 0x01 0x69) |
ADDR_REGISTER_RESET_SPS | 宏 | 45221,复位 SPS(本版本未被使用) |
ADDR_REGISTER_SET_INV_SAFETY + NUM_SET_INV_SAFETY | 宏 | 45244 + 1,安规设置 |
ADDR_REGISTER_SET_ISO_LIMIT + NUM_SET_ISO_LIMIT | 宏 | 45245 + 1,ISO 绝缘阻抗限值 |
ADDR_REGISTER_SET_LVRT + NUM_SET_LVRT | 宏 | 45246 + 1,低电压穿越(本版本未被使用) |
ADDR_REGISTER_QUERY_DETAILED_MSG + NUM_QUERY_DETAILED_MSG | 宏 | 45250 + 12,详细报文(本版本未被使用) |
ADDR_REGISTER_BACKUP_ENABLE + NUM_BACKUP_ENABLE | 宏 | 45252 + 1,备份/备电使能 |
ADDR_REGISTER_SET_OFFGRID_AUTOSTART + NUM_SET_OFFGRID_AUTOSTART | 宏 | 45253 + 1,离网自启动 |
ADDR_REGISTER_SET_AFCI_ENABLE | 宏 | 45270,AFCI(电弧故障)使能 |
ADDR_REGISTER_SET_ONGRID_DELAY + NUM_SET_ONGRID_DELAY | 宏 | 45276 + 1,并网延时 |
ADDR_REGISTER_SET_MICRO_GRID_ENABLE | 宏 | 45280,微网(GridForming)使能(本版本未被使用,改为写电表配置) |
ADDR_REGISTER_READ_BAT_PARA + NUM_READ_BAT_PARA | 宏 | 45350 + 9,电池参数 |
ADDR_RESITER_SET_MICRO_GRID_SOC_PROTECT + NUM_SET_MICRO_GRID_SOC_PROTECT | 宏 | 45387 + 1,离网充电截止 SOC 保护 |
ADDR_RESITER_SAFETY_DATA + NUM_READ_SAFETY_DATA | 宏 | 45400 + 28,安规阈值区 1 |
ADDR_RESITER_SET_POWER_FACTOR + NUM_SET_POWER_FACTOR | 宏 | 45443 + 1,功率因数(本版本未被使用) |
ADDR_RESITER_SET_MICRO_GRID_FREQ_PROTECT + NUM_SET_MICRO_GRID_FREQ_PROTECT | 宏 | 45446 + 1,微网频率保护 |
ADDR_RESITER_SET_QU_CURVE + NUM_SET_QU_CURVE | 宏 | 45456 + 1,Q-U 曲线(本版本未被使用) |
ADDR_RESITER_SAFETY_DATA2 + NUM_READ_SAFETY_DATA2 | 宏 | 45495 + 4,安规阈值区 2(OVP3/UVP3) |
ADDR_REGISTER_LED_TEST + NUM_LED_TEST | 宏 | 47008 + 1,LED 测试(本版本未被使用) |
ADDR_REGSITER_INV_SYSTEM_TYPE + NUM_INV_SYSTEM_TYPE | 宏 | 47011 + 1,逆变器系统类型 |
ADDR_REGISTER_WIFI_LED / ADDR_REGISTER_COM_LED | 宏 | 47013 / 47014,WIFI、通信指示灯 |
ADDR_REGISTER_SOC1_LED + NUM_SOC_LED | 宏 | 47020 + 4,4 段 SOC 指示灯 |
ADDR_REGISTER_BAT_LED / ADDR_REGISTER_SYS_LED / ADDR_REGISTER_FAULT_LED | 宏 | 47024 / 47025 / 47026,电池、系统、故障指示灯 |
ADDR_REGISTER_LED_CONTORL + NUM_LED_CONTROL | 宏 | 47028 + 1,LED 控制解锁(写 0xA5 0x67) |
ADDR_REGISTER_STOP_SOC_PROTECT + NUM_STOP_SOC_PROTECT | 宏 | 47500 + 2,停机 SOC 保护(本版本未被使用) |
ADDR_REGISTER_CTRL_INV_FEEDIN + NUM_CTRL_INV_FEEDIN | 宏 | 47509 + 2,馈电使能 + 馈电功率 |
ADDR_REGISTER_SET_INV_POWER_MODE + NUM_SET_INV_POWER_MODE | 宏 | 47511 + 2,功率模式字 + 功率值(最核心的控制寄存器) |
ADDR_REGISTER_SET_BAT_PROTOCOL + NUM_SET_BAT_PROTOCOL | 宏 | 47514 + 1,电池协议(写 0x011F 锂电协议) |
ADDR_REGISTER_SEND_BMS_DATA + NUM_SEND_BMS_DATA | 宏 | 47900 + 16,EMS 向逆变器下发 BMS 汇总数据(32 字节) |
ADDR_REGISTER_EBMS_COMM_LOSS_ENABLE + NUM_EBMS_COMM_LOSS_ENABLE | 宏 | 47916 + 1,EBMS 通信丢失告警使能 |
3. 函数清单
本文件为头文件,不含函数定义,共声明 5 个对外函数原型(goodwe_comm.h:412-416):
| 函数名 | 作用域 | 说明 |
|---|---|---|
set_inv_feedin | 全局(导出声明) | 下发馈电使能与馈电功率(u8 status, s16 power) |
set_inv_wait_mode | 全局(导出声明) | 使逆变器进入待机,返回是否收到应答 |
goodwe_comm_init | 全局(导出声明) | 复位触发标志并恢复安规缓存 |
goodwe_inverter_comm | 全局(导出声明) | 通信主调度入口(含升级分支) |
goodwe_inverter_data_check | 全局(导出声明) | 一致性维护与配置落盘触发 |
模块导出的全局变量 1 个:
| 名称 | 类型 | 说明 |
|---|---|---|
g_t_goodwe_comm_info | GOODWE_TABLE_T | goodwe_comm.h:410 声明 extern,实例定义在 goodwe_comm.c:30 |
4. 核心逻辑解析
头文件本身无执行逻辑,本节说明它承载的三类「隐性协议约定」——这些约定决定了 goodwe_comm.c 的行为,改动时必须同步。
4.1 寄存器地址表的组织规律
地址表按 GoodWe 逆变器的 Modbus 地址手册分段,可归为 5 个区段:
| 区段 | 地址范围 | 用途 |
|---|---|---|
| 输入/只读信息区 | 35000 ~ 36067 | 设备信息、运行数据、能量统计、ARC 版本,由 0x03 读取 |
| 系统/安规配置区 | 45217 ~ 45495 | 清能量、复位、安规、ISO、AFCI、微网保护、电池参数、安规阈值 |
| LED 区 | 47008 ~ 47028 | 6 类指示灯 + 控制解锁字 |
| 控制区 | 47500 ~ 47514 | 馈电、功率模式、电池协议 |
| EMS→逆变器数据区 | 47900 ~ 47916 | BMS 汇总数据推送、EBMS 通信丢失使能 |
命名上有两类拼写残留需注意:读类宏用 ADDR_REGISTER_*,而一部分写类宏用 ADDR_RESITER_*(RESITER 为 REGISTER 的笔误,见 goodwe_comm.h:83、86、92、95、98),以及 ADDR_REGISTER_LED_CONTORL(control 拼错,goodwe_comm.h:125)。这些错拼已成为跨文件引用的既成事实,重命名会影响 goodwe_comm.c 与文档一致性。
4.2 功能码与「读写方向」约定
| 功能码 | 值 | 帧长 | 使用场景 |
|---|---|---|---|
FUNC_READ_HOLDING_REGISTER | 0x03 | 8 字节请求 | 全部 query_* 函数 |
FUNC_WRITE_SIGNAL_HOLDING_REGISTER | 0x06 | 8 字节请求 | 在 ems_send_ctrl_data_to_inv 中支持,但本模块所有调用都走 0x10,0x06 分支实际未被使用 |
FUNC_WRITE_MULTIPLE_HOLDING_REGISTER | 0x10 | 13+N 字节请求 | 全部 set_* 函数,即使只写 1 个寄存器也走 0x10 |
这一点值得注意:写单个寄存器时协议上本应可省 4 字节(0x10 需额外的寄存器数 2 字节 + 字节计数 1 字节 + 首部差异),本模块统一使用 0x10,是对逆变器固件行为的适配(该型号对 0x06 支持不完整)。
4.3 三个枚举的语义边界
GOODWE_POWER_MODE_E:只有AUTO(0x0001)、OFF_GRID(0x06)、CHARGE_BAT(0x0B)、DISCHARGE_BAT(0x0C)、STOP(0x00FF) 被实际使用;其余CHARGE_PV、DISCHARGE_PV、IMPORT_AC、EXPORT_AC、CONSERVE、BATTERY_STANBY、BUY_POWER、SELL_POWER在本版本无调用点,属预留。注意STOP是跳跃值0x00FF而非紧邻递增,因此不能依赖枚举顺序做范围判断。GOODWE_LED_DISPLAY_E:从0x01起编号(而非 0),代码中直接以数值下发,因此「灯灭」对应 1、「灯亮」对应 2,与直觉的 0/1 相反,容易误读。GOODWE_OPERATION_MODE_E:FLASH_E(4) 未被parse_goodwe_work_mode()处理,会落到default返回0xFF,调用方inverter.c把它当作未知模式;这是「逆变器固件新版本上报态」与「EMS 未同步适配」之间的空档。GOODWE_SAFETY_COUNTRY_E:序号 0~71 连续,然后GW_UL1741=0x61(97)、GW_PR_240VAC=0x77(119)。中间 72~96 与 98~118 的地址空洞意味着该枚举不是 GoodWe 寄存器值的完整映射——写入 45244 的值必须是 GoodWe 侧认的编号,parse_goodwe_safety(GOODWE_SAFETY, ...)的switch只覆盖了goodwe_comm.c:198-294列出的分支,未列出的安规不会下发成功。
4.4 GOODWE_TABLE_T 的字段分组与并发语义
结构体虽为一个整体,但字段按用途分为三层,三种访问模式必须清楚区分:
- 触发标志层(
u8_*_flag,共 15 个):由外部模块(inverter.c、HMI、服务器命令)置TRUE,由goodwe_comm.c中的执行函数在收到有效应答后清除。这是一种单生产者-单消费者的「命令邮箱」,但因为读-改-写不加锁,task_inverter与 HMI/网络任务同时访问时存在丢失更新(lost update)的理论竞态。inverter.c:4056直接写g_t_goodwe_comm_info.u8_set_iso_limit_flag = TRUE;就是这种跨任务写入。 - 时间戳层(
u32_*_tick,共 8 个):只有task_inverter读写,无跨任务共享问题;但所有时间戳都依赖OSTimeGet()的毫秒回绕,因此goodwe_inverter_comm对每处比较都补了「记录值 > 当前值则重置」的分支。u32_ems_inv_comm_tick是唯一被多个函数写入的心跳字段。 - 数据缓存层(
u16_*/u8_*数组、两个联合体):由query_*函数写入、由inverter.c/net读取。u8_inv_device_type[10]是定长 10 字节的无 NUL 保证数组,外部若按 C 字符串使用有越界读风险。
4.5 故障/告警位域的排布约束
GOODWE_ERROR_U 的位域按 little-endian 位序排列:u8_gfci_device_check_failure 是 u32_all 的 bit0,u8_pv_over_voltage 是 bit15,u8_internal_communication_failure 是 bit31。这要求编译器把位域从 LSB 开始分配(ARM/Keil 默认满足),换编译器或加 --pcc 类选项会整体错位。
同时 byte4(bit24~31)在头文件里被整段注释(goodwe_comm.h:310-317),原意是 PV1/PV2/BAT 过流、Bus 过压、R/S/T 逆变过流、电池继电器故障共 8 位;注释掉后 u32_all 仍然完整接收 32 位数据,只是这 8 位不做任何语义解释。parse_inv_err_msg() 因此丢掉了这批故障信息。
GOODWE_WARNNING_U 是 16 位,u16_all 由 goodwe_comm.c:676 一次性填入,但没有任何一位被逐个解析,仅整字转存到 u16_dsp_debug[4]。也就是说这些告警名目前只是文档用途。
5. 对外接口
本文件导出
- 5 个函数原型:
set_inv_feedin、set_inv_wait_mode、goodwe_comm_init、goodwe_inverter_comm、goodwe_inverter_data_check。 - 1 个全局变量:
extern GOODWE_TABLE_T g_t_goodwe_comm_info;。 - 全部寄存器宏、功能码、枚举、联合体(供
goodwe_comm.c与goodwe_upgrade.c使用;本模块外无引用)。
本文件引用的外部头文件
includes.h(goodwe_comm.h:13):项目总头,提供u8/u16/u32/s16/s32类型定义(typedef来自UCOSII/common基础定义)。goodwe_upgrade.h(goodwe_comm.h:14):引入升级模块,使goodwe_comm.c能直接调用升级状态机;同时goodwe_upgrade.h反过来包含includes.h,形成平级依赖而非循环。
6. 依赖关系
依赖:includes.h(基础类型)、goodwe_upgrade.h(升级接口)。头文件使用 __GOODWE_COMM_H__ 作为 include guard(goodwe_comm.h:9-10)。
被依赖:
midware/inverter/goodwe/goodwe_comm.c:13—— 唯一实现者,使用全部宏、枚举与结构体。midware/inverter/goodwe/goodwe_upgrade.c:19—— 使用goodwe_comm.h导出的set_inv_wait_mode()、set_inv_feedin(),并通过GOODWE_SLAVE_ADDRESS等宏与主通信共用总线。midware/inverter/inverter.c:21—— include 本头文件以访问g_t_goodwe_comm_info(L4056 置 ISO 标志)和三个调度函数(L96、L1125、L1126)。- 由于
inverter.c在INVERTER_TYPE == GOODWE_TYPE编译分支下才引用 GoodWe 实现,本头文件在其它机型固件配置中虽被 include(inverter.c:21无条件包含)但不产生调用。
7. 备注与风险点
- 注释头与实际文件名/内容不符(
goodwe_comm.h:2):@file :mdsp_com.h、@brief为空,说明本文件是从mdsp_com.h复制改造而来,头部注释未同步。 - 大量未使用的寄存器宏:
ADDR_REGISTER_QUERY_INV_ENERGY_DATA、ADDR_REGISTER_RESET_SPS、ADDR_REGISTER_SET_LVRT、ADDR_REGISTER_QUERY_DETAILED_MSG、ADDR_REGISTER_SET_MICRO_GRID_ENABLE、ADDR_RESITER_SET_POWER_FACTOR、ADDR_RESITER_SET_QU_CURVE、ADDR_REGISTER_LED_TEST、ADDR_REGISTER_STOP_SOC_PROTECT在本版本goodwe_comm.c中均无调用点。保留它们可以认为是为后续机型适配预留,也让「哪些能力真的实现了」变得难以判断。 ON/OFF宏的全局污染(goodwe_comm.h:33-34):在头文件中定义如此通用的名字,任何 include 本头文件的翻译单元都会受影响,一旦与其它模块(如UCOSII的OS_OFF、HAL 的位定义)冲突会出现难以定位的编译错误。工程内目前靠命名差异规避。GOODWE_INV_DEBUG与GOODWE_INV_UPGRADE_DEBUG双开关:通信调试在goodwe_comm.h:17,升级调试在goodwe_upgrade.h:14,两者独立;goodwe_comm.c中还存在#if (GOODWE_INV_DEBUG)与#if (GOODWE_INV_DEBUG == 1)两种写法混用,把宏改为其它真值会导致部分调试段失效。u8_inv_device_type[10]没有长度/终止符约束:从寄存器memcpy10 字节原始数据后直接当作型号字符串用(goodwe_comm.c:579用%s打印),若逆变器返回的内容不含\0会越界读。GOODWE_ERROR_U.byte4被注释(goodwe_comm.h:310-317):这批故障位(PV1/PV2/BAT 过流、Bus 过压、R/S/T 逆变过流、电池继电器故障)在 EMS 侧永久丢失,服务器看不到。若 GoodWe 固件确实会上报这些位,属于功能缺失而非有意裁剪。INV_TYPE_ALPHA = 10与GOODWE_SAFETY_COUNTRY_E中的GW_AUSTRALIAN(9) 等值无关联:两个枚举都是裸数字,容易与安规码混淆;实际作用不同(前者写 47011 系统类型,后者写 45244 安规),代码里没有任何注释区分。MAX_REGISTER_NUM = 100与实际调用规模不匹配:本模块所有写操作最多 16 个寄存器,MAX_REGISTER_NUM的上界形同虚设,反而放大了goodwe_comm.c中 128 字节栈缓冲被越界的可能(详见goodwe_comm.c备注第 1、2 条)。- 错拼的宏名:
ADDR_RESITER_*(5 处)与ADDR_REGISTER_LED_CONTORL会长期污染代码搜索与自动补全;重构时建议保留旧名做别名而非直接改名。 STOP = 0x00FF使枚举失去可递推性:任何「模式值 ≤ 某枚举」的判断都不成立,必须逐值switch;当前代码遵守了这一约定,但缺少注释警示。