EMS 故障分册(E0001 ~ E0094)
面向现场售后:报
E00xx时查这里。共 30 个故障位。 需求号:REQ-001(K7)| 源码基准:V0.11.676(git04e72700)| 定稿 2026-10-15 源码只读,全部结论来自逐行阅读,未修改任何.c/.h。
先看这 6 条(现场最容易踩的坑)
- 有 7 个码在本版本固件里永远不会报 ——
E0001E0008E0009E0012E0013E0015E0016。
看到这 7 个码说明固件版本与本分册不一致,别在当前代码里找它。详见 §二。
- 有 3 个码一旦出现就是永久锁存,只有断电/复位才消 ——
E0014(Flash)、E0031(失联 12 h)、E0033(异常强制关机)。
代码里没有清除路径。看到「重启一次就没了」不要以为修好了。详见 §三 第 3 条。
- 机械不卖电很可能不是独立问题 —— 在英国 G100(G98/G99)安规下,
E0004 E0005 E0006 E0007 E0010 E0011 里任一个挂着,系统会被强制切成「禁止馈网」
(logic.c:390-395 聚合 → logic.c:506-511 切成 STATE3_FOBID_FEEDIN,原因码 OVER_STATE3_COM_FAULT)。
现场反馈"不卖电"时先查这 6 个码。
- "丢通讯"类故障门槛都在 15 s ~ 30 s,瞬断不会报;反过来报出来就说明已经断很久了。
而且离网/UPS、电网频率 < 45 Hz、断点续传/升级中这三种工况下,一部分码被故意设计成不报。 不报 ≠ 没坏。
E0004(逆变器失联)报出来时,电量类数据会同时变 0 —— 这是inverter.c:132-189主动清零一大片
运行数据的连带现象,不是第二个故障。
- 屏幕上的码是 5 位数字:故障码 7 显示成
E00007,不是本分册写的E0007(去掉补零即得)。
而且行首有没有 NO 是关键: E00007 NO. 1 表示故障正在发生,
NO E00007 NO. 1 表示该故障已恢复。排查时只看没有 NO 的行。
注:行尾 NO. 后的数字对 EMS 码不是设备号 —— EMS 写日志时硬编码为 1
(data/fault_warn_log/fault_warn_log.c:556/560),只有 BMS 上报的码才按电池序号变化(:603)。
格式实现在 hmi/ui/ui_history_error_log.c:139-165。
本分册的每条结论都标注了
文件:行。提取完成后我又独立复核了其中最强结论(3 个锁存位的无清除路径、 G100 禁止馈网的聚合关系、以及 alphaess/锦浪/科陆 三家逆变器的失联阈值与置位点),全部与原文一致。 后来还从源码原文补出了第 6 条(屏幕显示格式),该格式不影响任何故障位判定,只影响现场读屏。
零、先看懂这张位图(现场必须先懂这个)
EMS 主控把自己判定的故障压在一个 32 位位图 EMS_FAULT_U 里(data/data.h:67-114),
只有 30 个位定义了含义(bit0 ~ bit29),高两位 bit30/bit31 是占位。
- 置位/清位函数:
ems_set_fault_bit(u8 index, u8 value),data/data.c:338-348
(index 就是 bit 位置,不是故障码值;value 非 0 置 1,为 0 清 0)
- 也有 3 个位是绕过函数直接写位域的:bit9、bit25、bit26
- 全工程共 42 处写位图的地方,落在 23 个位上
- 位图变化时,
data/fault_warn_log/fault_warn_log.c:516-566负责把它翻译成E00xx故障码
并写入故障日志(对照表在 fault_warn_log.c:518-549)
一个关键结论:位图没有任何"整体清零"的地方。 全工程搜不到 u_ems_fault.u32_all = 0
或对 g_t_ems_info 的 memset。所以只要某位置了 1 而代码里没有对应的清 0 分支,这个故障就会一直挂着,
直到主控断电或复位。本文件下面标注为「单向锁存」的三个位(bit13/bit17/bit19)就属于这种情况。
故障位恢复的响应速度取决于扫描它的任务周期,本文件逐个标出:
| 任务 | 文件:行 | 周期 | 负责的位 |
|---|---|---|---|
task_inverter | midware/inverter/inverter.c:1188 | 80 ms | bit3, bit16, bit22 |
task_inverter_can | midware/inverter/inverter.c:1285 | 30 ms | bit10, bit20, bit21 |
task_bms | midware/bms/bms.c:2933 | 100 ms | bit6, bit17, bit19, bit26, bit28 |
task_meter | midware/meter/meter.c:5881 | 300 ms | bit4, bit5, bit25, bit29 |
task_system_data | data/data.c:263(信号量超时 500) | ≈500 ms | bit2 |
task_system_aux | midware/auxiliary/system_aux.c:2092 | 400 ms | bit1 |
task_interface_parse | midware/interface/interface.c:230 | 500 ms | bit24 |
task_sampling | midware/meter/ct.c:844(ADC-DMA 信号量触发) | 随采样中断 | bit18 |
alphaess_inverter_comm_usart | 由 task_inverter 调,inverter.c:1116 | 80 ms | bit23 |
位图的一个下游影响(现场会看到"另一个症状"):logic/logic.c:390-395 把
E0004/E0005/E0006/E0007/E0010/E0011 这 6 个位或在一起,只要有一个为 1,
在 英国 G100 安规(u8_safety = G98/G99) 配置下就会把系统状态强制切成
STATE3_FOBID_FEEDIN(禁止馈网),状态原因 OVER_STATE3_COM_FAULT(logic.c:506-511)。
所以现场可能遇到「报一个通讯类故障 + 系统不给卖了」的组合,那不是两个独立问题。
一、23 个"活位"逐条
bit 0 | E0001 | EMS_FAULT_NET_FAULT | u8_net_fault
死位,永不报。 见文末「死位」一节。
bit 1 | E0002 | EMS_FAULT_RTC_FAULT | u8_rtc_fault
- 置位点:
midware/auxiliary/system_aux.c:355(置 1);midware/auxiliary/system_aux.c:351(清 0)
两者都在 rtc_refresh()(system_aux.c:341-380)内,由 task_system_aux 每轮调用一次(system_aux.c:2073,周期 400 ms)
- 触发条件:读 RTC 芯片 PCF8563 失败。判据就是
PCF8563_Get_DateTime_Other()(hardware/pcf8563/pcf8563.c:1415)
的返回值不等于 0:
- 返回
-1:传入指针为空(正常调用不会走这里)- 返回
-2:连读 3 次、每次读 3 遍,得到的时间还是无效值(PCF8563_Check_Time_Is_Valid()校验不过) - 返回
0:读到有效时间
- 返回
- 清除条件:下一轮读取返回 0 就立刻清 0(每 400 ms 重试一次,属于自恢复故障)
- 消抖/延时:函数级无门槛;但底层做了「一次调用读 3 遍 + 3 轮重试 + 时间有效性校验」,
相当于读失败要有相当强的证据
- 上游数据来源:RTC 芯片本身。返回 -2 时代码还会做一件事:把上一次读到的好时间
temp_tm_old
回写给 RTC(pcf8563.c:1474-1476:PCF8563_SetDateTime() + return -2),
也就是芯片时间跑飞时主控会尝试纠回
- 源码摘录:
l_s8_rtc_state = PCF8563_Get_DateTime_Other(&l_t_time); if(l_s8_rtc_state == 0) { ems_set_fault_bit(1,FALSE); } else { ems_set_fault_bit(1,TRUE); }
现场注意:E0002 通常意味着 RTC 芯片通讯不良/晶振停振/电池没电,不是主控算错时间。
bit 2 | E0003 | EMS_FAULT_EEPROM_FAULT | u8_eeprom_fault
- 置位点:
data/data.c:232(置 1);清除点:data/data.c:228(清 0)
都在 eeprom_fault_handle()(data/data.c:211-242)内,由 task_system_data 每轮调用(data/data.c:308,≈500 ms)
- 触发条件:下面 4 个 32 位标志位的"或"不为 0 就置 1(
data/data.c:219-220):s_u32_eeprom_hardware_read_fault_flag——底层 Flash 读失败
(hardware/flash/feeprom.c:46/77/84/95/100)
s_u32_eeprom_hardware_write_fault_flag——底层 Flash 擦/写失败
(hardware/flash/feeprom.c:163/194/250/269)
s_u32_eeprom_software_read_fault_flag——某个配置页读出后 CRC/内容不对
(data/system_config/system_config.c 各 *_restore() 里大量调用,如 :1236/1286/1432/1596...)
s_u32_eeprom_software_write_fault_flag——某个配置页写回失败
(system_config.c:120/166/246/281/.../3141)
每个标志位用 bit(页号 - SYSTEM_STARTE_PAGE) 记是哪一页出的问题(fault_warn_log.c:2077-2089)
- 清除条件:4 个标志的"或"归 0 时清 0(
data/data.c:226-229) - 消抖/延时:无。任何一次 Flash 失败都会立刻报出来
- 上游数据来源:Flash 驱动层与各配置页的读写函数(上面 1~4 已列出)
- 源码摘录:
u32 l_u32_eeprom_fault_flag = (l_u32_eeprom_hardware_read_fault_flag | l_u32_eeprom_hardware_write_fault_flag | l_u32_eeprom_software_read_fault_flag | l_u32_eeprom_software_write_fault_flag); if(s_u32_eeprom_fault_flag != l_u32_eeprom_fault_flag) { s_u32_eeprom_fault_flag = l_u32_eeprom_fault_flag; if(l_u32_eeprom_fault_flag == 0x00000000) ems_set_fault_bit(2,FALSE); else { ems_set_fault_bit(2,TRUE); set_info_status(EEPROM_FAULT); } } - 附带的自愈逻辑:
clear_eeprom_fault_temp()(data/data.c:187-202)会遍历 32 个页,
如果某页只有"读失败"而没有"写失败",就认为它是临时坏值,主动把"读失败"标志清掉。 现场含义:偶尔闪一次的 E0003 会自动消失;一直挂着的 E0003 意味着某页真的写不进去了。
bit 3 | E0004 | EMS_FAULT_INV_LOST | u8_inv_lost
- 置位点:
midware/inverter/inverter.c:119,在inverter_lost_handle()(inverter.c:115-190)内,
由 task_inverter 调用(inverter.c:1138),周期 80 ms
- 触发条件:直接跟随标志
g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag
—— 该标志为 TRUE 就置 1,为 FALSE 就清 0(无条件跟随,无时长消抖)
- 清除条件:
u8_inverter_comm_lost_flag被通讯任务改回 FALSE(下一轮 80 ms 内就清掉) - 消抖/延时:不在这一层,在决定
u8_inverter_comm_lost_flag的地方(见下) - 上游数据来源(逆变器机型不同,判据不同,都是"多久没收到有效帧"):
机型 置 TRUE(丢失) 清 FALSE(恢复) AlphaESS(贝瓦) alphaess_comm_usart.c:5459-5463:距上次成功通讯 > 30 s;若正在升级或刚升级完 120 s 内放宽到 30+90=120 s。置位同时重初始化串口alphaess_comm_usart.c:1031-1032:收到合法的逆变器设备信息回帧锦浪 GINLONG ginlong_comm_usart.c:4890-4894:距上次成功 > 30 sginlong_comm_usart.c:4868、:4882古瑞瓦特 GOODWE goodwe_comm.c:2485-2487:距上次成功 > 30 sgoodwe_comm.c:2473、:515汇川 INOVANCE inovance_comm_usart.c:2598-2600:距上次成功 >INOVANCE_EMS_INV_COMM_LOST_TIME(30 s,inovance_comm_usart.h:26)inovance_comm_usart.c:2308盛能杰 SET(S5/M10 等) 不走这个标志: set_comm_usart_s5.c用的是自己文件内的u8_ems_inv_comm_flag— 科陆 KELONG 没有置 TRUE 的地方,只有一处清 0: kelong_comm.c:1371—
→ 现场含义:E0004 只对贝瓦 / 锦浪 / 古瑞瓦特 / 汇川这 4 类逆变器有意义, 盛能杰、科陆机型下这个码不会因为"逆变器失联"而报(盛能杰另有 E0010 走内部错误位)
- 置位瞬间的副作用(现场必看):
inverter.c:132-189会把 PV 电压/电流/功率、电网三相、逆变器三相、
备电三相、电池电压/电流/功率等一大片运行数据全部清零,并把运行模式写成 cFaultMode。
所以报 E0004 时,App/屏幕上电量类数据同时变 0 是正常的连带现象,不是两个故障
- 源码摘录:
static void inverter_lost_handle(void) { static u8 s_u8_pcs_lost_flag = FALSE; ems_set_fault_bit(3,g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag); ...if (abs(g_t_alphaess_comm_info.u32_ems_inv_comm_tick - OSTimeGet()) > 30 * 1000 + l_u32_ugrade_finish_tick) { g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag = TRUE; alphaess_comm_init(); }
bit 4 | E0005 | EMS_FAULT_GRID_METER_LOST | u8_grid_meter_lost
- 置位点:
midware/meter/meter.c:4567;清除点:midware/meter/meter.c:4571
都在 meter_fault_handle()(meter.c:4539-4598)内,由 task_meter 调用(meter.c:5775),周期 300 ms
- 触发条件:直接跟随
g_t_meter.t_grid_info.u8_comm_lost_err(电网侧电表通讯丢失标志) - 清除条件:该标志被清 0 即恢复
- 消抖/延时:不在这一层,在自己算标志的 4 条路径里:
- P1 无线通信棒路线(电网侧电表类型 =
P1_SAMPLING):
- P1 无线通信棒路线(电网侧电表类型 =
距上次读到 P1 数据 > 3000 ms 就置位,且明确排除下面三种情况不报:
正在做固件升级、断点续传中、逆变器处于 cUPS 模式(midware/ethernt/wireless/wireless_driver.c:621-627);
恢复正常则 wireless_driver.c:632 清 0
- 首次搜索不到电表:
grid_meter_detect()(meter.c:605-647),连续 30 次找不到才进入判断
(每次搜索间隔 ≥ 1000 ms,宏 METER_DETECT_INTERVAL_TIME,meter.h:98 → 约 30 s),
且还要同时满足:非"离网未接市电"、且不是 cUPS 模式、且电网频率 ≥ 4500(45.00 Hz);
否则即使搜不到也不报(meter.c:626-640)
- 本来在通讯、后来断了:
grid_meter_read_data()(meter.c:3075)里的连续失败计数u8_lost_cnt
超阈值(meter.c:3622-3636):
- 若逆变器"软件限制"或"硬件限制"使能 →
u8_lost_cnt >= 3(很快)- 否则 →
u8_lost_cnt > 15(宏METER_LOST_MAX_CNT,meter.h:95)
- 否则 →
然后走和路径 2 一样的并网/离网判断(meter.c:3652-3666)
- 无线 CT/电表:
wireless_meter_handle_lost()(meter.c:5456-5476),判断条件同路径 2
- 上游数据来源:电表通讯任务(
meter_handle()→meter_data_read());CT 直连板另有一条
meter.c:5806-5811,每 15 s 刷新一次数据,把丢失标志强制清 0
- 源码摘录:
if (g_t_meter.t_grid_info.u8_comm_lost_err == TRUE) { ems_set_fault_bit(4,TRUE); } else { ems_set_fault_bit(4,FALSE); }if(!((inverter_read_run_mode() == cUPS) || (inverter_read_grid_freq() < 4500))) //电表接并网侧报故障条件 { g_t_meter.t_grid_info.u8_comm_lost_err = TRUE; }
现场注意:离网(UPS/无市电)时不报电表丢失,这是设计如此,不是漏报。
bit 5 | E0006 | EMS_FAULT_PV_METER_LOST | u8_pv_meter_lost
- 置位点:
midware/meter/meter.c:4576;清除点:midware/meter/meter.c:4580(同meter_fault_handle()) - 触发条件:直接跟随
g_t_meter.t_pv_info.u8_comm_lost_err - 清除条件:该标志清 0 即恢复
- 消抖/延时:同上,在自己的判据层:
pv_meter_detect()(meter.c:958-1006):连续 30 次搜不到(间隔 ≥1 s → 约 30 s)后判断pv_meter_read_data()(meter.c:3680):u8_lost_cnt > 15(METER_LOST_MAX_CNT)→meter.c:4062-4095- 并网/离网判断和电网侧不一样(
meter.c:978-999、meter.c:4076-4089):- 若配置了
u8_gridforming_en(构网使能)→ 只看备电频率:< 4500(45.00 Hz)不报,≥ 45 Hz 才报 - 否则 → 与电网侧相同:非
cUPS且电网频率 ≥ 45.00 Hz 才报
- 若配置了
- 上游数据来源:电表通讯任务;CT 直连板路径
meter.c:5854-5859(15 s 刷新则清 0),
未配 PV 电表时 meter.c:5866 强制清 0
- 源码摘录:
if (g_t_meter.t_pv_info.u8_comm_lost_err == TRUE) { ems_set_fault_bit(5,TRUE); } else { ems_set_fault_bit(5,FALSE); }
现场注意:PV 侧是否报错,首先看有没有配"构网使能"。同一块表、同样断线,两种配置下 一个报 E0006、一个不报。
bit 6 | E0007 | EMS_FAULT_BMS_LOST | u8_bms_lost
- 置位点:
midware/bms/bms.c:62;清除点:midware/bms/bms.c:66
都在 bms_comm_detect()(bms.c:56-69)内,由 task_bms 调用(bms.c:2756),周期 100 ms
- 另有 2 个强制清 0 点:
bms.c:2750(配置为"系统未接入电池"时)、bms.c:2760(电池固件升级期间) - 触发条件(三层同时成立):
- 开机满 60 s 才开始检测(
bms.c:58,OSTimeGet() > 60*1000)——
- 开机满 60 s 才开始检测(
注意:开机 60 s 内这个函数既不置位也不清零,位图保持原状
g_t_bat_info.t_run_info.u8_connect == FALSE(电池通讯连接标志)pvcharger_read_machine_mode() != PVCHARGR_FROM_E(不是"被外部 PV 充电器主机接管"的模式)
- 清除条件:以上任一不成立即清 0(电池连上、或切到外部 PV 充电器模式、或进入升级/未接电池配置)
- 消抖/延时:开机 60 s 静默期;连接丢失本身有 10 s 门槛(见上游)
- 上游数据来源:
u8_connect在task_bms里被写:- 置 TRUE:收到并成功解析一帧 Alpha 电池 CAN 报文(
g_t_bat_info.t_run_info.u8_connect = TRUE,bms.c:2863) - 置 FALSE:距上次收到有效帧 > 10 s(
bms.c:2899-2905),同时调用bms_lost_handle()
- 置 TRUE:收到并成功解析一帧 Alpha 电池 CAN 报文(
清掉整块电池运行数据(bms.c:1133-1141)
- 源码摘录:
static void bms_comm_detect(void) { if (OSTimeGet() > 60 * 1000) //开机后60s开始检测BMS丢失错误 { if ((g_t_bat_info.t_run_info.u8_connect == FALSE) && (pvcharger_read_machine_mode() != PVCHARGR_FROM_E)) { ems_set_fault_bit(6, TRUE); } else { ems_set_fault_bit(6, FALSE); } } }
现场注意:上电 60 s 内不会报 E0007。刚上电就找售后说"电池没了"要先等一分钟。
bit 7 | E0008 | EMS_FAULT_BAT_VOLT_LOW | u8_bat_volt_low
死位,永不报。 见文末「死位」一节。
bit 8 | E0009 | EMS_FAULT_BACKUP_OVERLOAD | backup_overload
死位,永不报。 见文末「死位」一节。
bit 9 | E0010 | EMS_FAULT_INV_SLAVE_LOST | inv_slave_lost
- 置位点:
midware/inverter/set/set_comm_usart_s5.c:1077(直接写位域,不走ems_set_fault_bit)
在 senergys5_get_error_waring_apping_table1()(set_comm_usart_s5.c:1059-1083)内,
该函数由 senergys5_query_inv_real_time_data1()(set_comm_usart_s5.c:3141)在解析逆变器实时数据后调用
(调用点 set_comm_usart_s5.c:3217)
- 触发条件:把盛能杰(SET)逆变器自己上报的"内部通讯错误"位原样抄过来
—— s_u_senergys5_error_mapping_table.bit.u8_internal_comm_error。
该位的原始来源是逆变器寄存器 0x101E(Error message 1)的 bit2
(set_comm_usart_s5.c:3189 读入 word0;位定义 set_comm_usart_s5.h:527,
注释原文:Internal communication error(master to slave))
- 清除条件:逆变器自己的这一位归 0(逆变器自恢复);或 DSP 重启握手成功后由代码强制写 0
(set_comm_usart_s5.c:4390,在 senergys5_restart_dsp_handle() 内,set_comm_usart_s5.c:4320)
- 消抖/延时:EMS 侧无。完全跟随逆变器上报
- 上游数据来源:盛能杰逆变器内部主从 DSP 之间的通讯;EMS 只做映射。
EMS 侧没有独立判据 —— 这一位是纯镜像
- 源码摘录:
g_t_ems_info.u_ems_fault.bit.inv_slave_lost = s_u_senergys5_error_mapping_table.bit.u8_internal_comm_error;if(abs(s_u32_can_rcv_tick - OSTimeGet()) >= 15000) // (另一处:通讯丢失后会重试 DSP 重启) ... g_t_ems_info.u_ems_fault.bit.inv_slave_lost = 0; // 重启 DSP 握手成功
现场注意:报 E0010 时,故障根源在逆变器内部(主控 DSP 与从 DSP 之间),不在 EMS。 换 EMS 主板解决不了,要查逆变器。
bit 10 | E0011 | EMS_FAULT_INV_MASTER_LOST | inv_master_lost
- 置位点:
midware/inverter/inverter.c:203;清除点:midware/inverter/inverter.c:207
都在 inverter_lost_handle_can()(inverter.c:199-221)内,由 task_inverter_can 调用(inverter.c:1270),周期 30 ms
- 触发条件:跟随
g_t_inv_info.t_run_info.u8_inverter_can_comm_lost_flag == TRUE - 清除条件:该标志为 FALSE 即清 0
- 消抖/延时:不在这一层。各机型 CAN 判据:
机型 置 TRUE 清 FALSE AlphaESS alphaess_comm_can.c:1308-1314:距上次收到 0x269 帧 ≥ 15 s 先重初始化 CAN;≥ 55 s 才置丢失alphaess_comm_can.c:1134:收到 IDCODE 帧盛能杰 SET set_comm_can.c:808-816:距上次成功 > 60 sset_comm_can.c:866:进入正常分支即清汇川 INOVANCE inovance_comm_can.c:780-783:≥INOVANCE_EMS_INV_CAN_COMM_LOST_TIME(15 s,inovance_comm_can.h:17)inovance_comm_can.c:656汇川(串口侧共用) — inovance_comm_usart.c:2309 - 源码摘录:
if (g_t_inv_info.t_run_info.u8_inverter_can_comm_lost_flag == TRUE) { ems_set_fault_bit(10, TRUE); } else { ems_set_fault_bit(10, FALSE); return; }
现场注意:E0011 是 CAN 通道丢失,E0004 是 串口通道丢失,两者可能同时报(两条链路都断)。
bit 11 | E0012 | EMS_FAULT_PARALLEL_COMM_ERROR | parallel_comm_error
死位,永不报。 见文末「死位」一节。
bit 12 | E0013 | EMS_FAULT_PARALLEL_MODE_DIFFER | parallel_mode_differ
死位,永不报。 见文末「死位」一节。
bit 13 | E0014 | EMS_FAULT_FLASH_FAULT | u8_flash_fault
- 置位点:
data/system_config/system_config.c:2530。全工程只此一处,且只有置 TRUE,没有清 FALSE - 触发条件:
system_backup_info_restore() == FALSE(system_config.c:2528-2531),
即系统备份参数区读不出来。该函数(system_config.c:2326-2361)的逻辑是:
从 SYSTEM_DATA_BACKUP_PAGE 连读 3 次,每次都要能读出、且 CRC 校验通过;
3 次都失败就调 system_backup_info_init() 重建备份并返回 FALSE
- 清除条件:代码中不存在清除路径 → 单向锁存。 一旦置位,只能靠断电/复位清掉
- 消抖/延时:无(但本身上层已做了 3 次重读)
- 上游数据来源:Flash 备份区(备份 SN、逆变器型号等系统参数的冗余副本)
- 源码摘录:
if(system_backup_info_restore() == FALSE) { ems_set_fault_bit(13,TRUE); } else { ... }
现场注意:E0014 出现即代表主控 Flash 里的系统备份区已损坏并被重建, 重建后 SN、逆变器型号等参数可能回到默认值,必须人工核对 SN 与机型配置。 而且这个故障会一直挂着直到重启,看到"重启后 E0014 消失"是预期行为。
bit 14 | E0015 | EMS_FAULT_SDRAM_ERROR | u8_sdram_error
死位,永不报。 见文末「死位」一节。
bit 15 | E0016 | EMS_FAULT_EXTENSION_CAN_ERROR | u8_extension_can_error
死位,永不报。 见文末「死位」一节。
bit 16 | E0017 | EMS_FAULT_INV_TYPE_NOT_SPECIFIED | inv_type_not_specified
- 置位点:
midware/inverter/inverter.c:1071,在task_inverter(inverter.c:1045)主循环里,周期 80 ms - 触发条件:
g_t_system_base_info.u16_oem_flag != OEM_HHE(非 HHE 贴牌)并且下面三条之一成立:get_inverter_type() == INVERTER_NULL—— 运行时配置的逆变器型号为"未指定"(值 0) ← 这是现场实际会碰到的那条INVERTER_TYPE == INVERTER_NULL—— 编译期宏(get_inverter_type() != INVERTER_TYPE) && (INVERTER_TYPE != INVERTER_MAX)
—— 配的机型与固件编译的机型不一致,且固件不是"通吃"版本
- 清除条件:条件不成立即清 0(即把机型配对之后 80 ms 内自动消失)
- 消抖/延时:无
- 上游数据来源:
get_inverter_type()(inverter.c:1340-1348)返回
g_t_inv_info.t_config_data.u8_inv_model,即储存在 Flash 里的机型配置;
≥ INVERTER_MAX(7) 的非法值会被当场改写成 INVERTER_NULL
- 源码摘录:
if(g_t_system_base_info.u16_oem_flag != OEM_HHE) { ems_set_fault_bit(16,(get_inverter_type() == INVERTER_NULL) || (INVERTER_TYPE == INVERTER_NULL) || ((get_inverter_type() != INVERTER_TYPE) && (INVERTER_TYPE != INVERTER_MAX))); }
现场注意:这是配置类故障,不是硬件故障。出现 E0017 先查机型配置有没有丢, 不要去拆线。⚠️ 另一个必须知道的坑见本文件末「不确定的地方」第 1 条。
bit 17 | E0031 | EMS_FAULT_INV_LOST_BAT_SHUTDOWN | u8_inv_lost_bat_shutdown
- 置位点:
midware/bms/bms.c:1258,在force_bat_shutdown_handle()(bms.c:1199-1270)内,
由 task_bms 调用(bms.c:2890),周期 100 ms
- 触发条件(两件事同时成立,且继续计时):
- 逆变器通讯丢失:
g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag == TRUE - 出厂标志还在:
g_t_system_base_info.u8_factory_flag == TRUE
- 逆变器通讯丢失:
(该标志只在 system_config.c:108 的 system_sn_init() 里被置 TRUE,
并作为 FactoryFlag 参数注册给服务器,可由服务器下发改写 —— net/server.c:3944。实际含义是"还没走完出厂配置")
且从第一次检测到通讯丢失起连续 12 小时(bms.c:1256,12 * 60 * 60 * 1000)才置 1
- 清除条件:代码里唯一的清 0 点
bms.c:1244位于bit17 == FALSE的 else 分支
(bms.c:1237-1245),也就是说:只有本来就是 0 的时候才写 0,一旦置 1 就再也清不掉 → 单向锁存。
只能靠断电/复位
- 消抖/延时:12 小时(极长,实际是"逆变器彻底联系不上半天后强制电池关机"的兜底保护)
- 上游数据来源:
u8_inverter_comm_lost_flag(同 bit3 的上游,各机型 30 s 级判据) - 源码摘录:
if((g_t_inv_info.t_run_info.u8_inverter_comm_lost_flag == TRUE) && (g_t_system_base_info.u8_factory_flag == TRUE)) { if (s_u8_inv_lost_flag == FALSE) { s_u8_inv_lost_flag = TRUE; s_u32_inverter_comm_lost_tick = OSTimeGet(); } else { if((OSTimeGet() - s_u32_inverter_comm_lost_tick) > 12 * 60 * 60 * 1000) { ems_set_fault_bit(17, TRUE); }
现场注意:通讯短暂恢复过不要以为没事 —— 只有持续 12 小时不间断才置位, 而一旦置位就永久锁存。看到 E0031 说明这台机器至少失联过半天。
bit 18 | E0032 | EMS_FAULT_SAMPLING_ANOMALY | sampling_anomaly
- 置位点:
midware/meter/ct.c:1099(置 1);清除点:ct.c:1103、ct.c:1108(清 0)
都在 task_sampling(ct.c:844)的采样处理里,由 ADC-DMA 完成中断喂信号量触发
- 触发条件(外条件全部成立,并且持续 > 2000 ms):
l_u16_dma_data_len < 150(本次 DMA 采样点数不足 —— 也可能是 CT A 相电压过低)
或 g_t_ct.t_grid_ct_info.t_ct_data.u16_ct_volt_a < 300
- 且
g_t_ct.t_grid_ct_info.t_ct_data.u16_ct_frequent >= 4200(电网频率 ≥ 42.00 Hz,即确实接了市电)- 且板型不是
BOARD_EMS3_6_3_E/BOARD_EMS3_6_4_E(这两种板不做这个判断)
- 且板型不是
(判断整体在 ct.c:1095-1110)
- 清除条件:条件不成立,或成立时间不足 2 s → 立即清 0
- 消抖/延时:2 s 持续
实现细节:s_u32_sampling_anomaly_tick(ct.c:37)只在条件不成立时才刷新(ct.c:1109),
所以 abs(当前 - tick) > 2000 实际测的就是"这个异常已经连续持续多久"
- 上游数据来源:CT 采样原始数据(DMA 点数、电网 A 相电压、电网频率),
采样率由 ct_sampling_init() 里 timer1_init(100, 167) 决定(ct.c:66,注释写"默认 100 Hz 100 个点")
- 源码摘录:
//采样异常检测 if (((l_u16_dma_data_len < 150) || (g_t_ct.t_grid_ct_info.t_ct_data.u16_ct_volt_a < 300)) && (g_t_ct.t_grid_ct_info.t_ct_data.u16_ct_frequent >= 4200) && (get_board_type()!= BOARD_EMS3_6_3_E) && (get_board_type()!= BOARD_EMS3_6_4_E)) { if (abs(OSTimeGet() - s_u32_sampling_anomaly_tick) > 2000) { ems_set_fault_bit(18, TRUE); } else { ems_set_fault_bit(18, FALSE); } } else { ems_set_fault_bit(18, FALSE); s_u32_sampling_anomaly_tick = OSTimeGet(); }
现场注意:E0032 是"接了市电但 CT 侧采不到像样的电压/点数"。
代码里 300 这个数是 CT 内部量纲的原始判据,源码没有标注单位,
不要直接理解成 300 V。先查 CT 接线、CT 插头、CT 方向。
bit 19 | E0033 | EMS_FAULT_INV_SATUSE_ABNORMAL_BAT_SHUTDOWN | inv_satuse_abnormal_bat_shutdown
- 置位点:
midware/bms/bms.c:1228,在force_bat_shutdown_handle()内(同 bit17 所在的函数) - 触发条件:以下全部同时成立,并且连续保持 ≥ 2 s(
bms.c:1212-1231):inverter_get_bat_shutdown_flag() == TRUE—— 机型是 AlphaESS 或古瑞瓦特(inverter.c:2898-2904)inverter_get_bat_breaker_statuse() == 2(逆变器报告电池断路器状态 = 2)inverter_read_bus_volt() < 230(母线电压低)inverter_get_battery_awaken_pv_start_status() == FALSE("PV 唤醒电池启动"未成立)inverter_read_grid_freq() < 4500(电网频率 < 45.00 Hz,即电网也掉了)(g_t_bat_info.t_run_info.u8_relay_status & 0x01) == 0x01(BMS 上报的继电器状态字节 bit0 = 1)
置位的同时会执行 bms_set_shutdown(TRUE) 强制电池关机
- 清除条件:代码中没有清除点 → 单向锁存。只能靠断电/复位
- 消抖/延时:2 s 连续。实现上:只要上面 6 条有一条不成立,
s_u32_battery_shut_down_tick 就被刷新(bms.c:1222),计时重新开始
- 上游数据来源:逆变器遥测(断路器状态、母线电压、电网频率、唤醒状态)+ BMS 上报的继电器状态
(u8_relay_status 来自电池通讯,赋值见 midware/bms/alpha/battery_comm.c:1318)
- 源码摘录:
if( inverter_get_bat_breaker_statuse() == 2 && inverter_read_bus_volt() < 230 && inverter_get_battery_awaken_pv_start_status() == FALSE && (inverter_read_grid_freq() < 4500) && (g_t_bat_info.t_run_info.u8_relay_status & 0x01) == 0x01 ) { u8_start_tick_flag = TRUE; } ... if( abs(s_u32_battery_shut_down_tick - OSTimeGet()) >= 2*1000 && u8_start_tick_flag == TRUE ) { u8_start_tick_flag = FALSE; ems_set_fault_bit(19, TRUE); bms_set_shutdown(TRUE); return; }
现场注意:E0033 描述的是"电网掉了 + 母线没电 + 电池没有自己站起来 + 电池继电器还是合着的" 这种异常工况,属于保护性判定。一旦报出就是永久锁存,必须断电才能清。
bit 20 | E0034 | EMS_FAULT_PARALLEL_HOST_LOST | parallel_host_lost
方向:本机是并机系统中的"从机",报"找不到主机"
- 置位点/清除点:
midware/inverter/parallel.c:1014,在parallel_data_handle()(parallel.c末尾,:975-1016)内,
由 task_inverter_can 调用(inverter.c:1276),周期 30 ms
- 触发条件:
g_t_from_machine_networking_info.u8_parallel_lost_flag != FALSE - 清除条件:该标志清 0 即恢复
- 消抖/延时:不在这一层。上游心跳判据(两种通讯方式都是 15 s):
- RS485/SCI:
parallel_sci.c:1546-1555——距上次收到组网帧 ≥ 15 s,
- RS485/SCI:
置位时还会把 u8_parallel_addr 清 0、u8_parallel_addr_en 置 FALSE、清空运行数据
(意味着要重新组网才能恢复)
- CAN:
parallel_can.c:861-869——同为 15 s- 清 0:收到主机写多寄存器响应帧时
parallel_sci.c:1388-1391;CAN 侧parallel_can.c:787
- 清 0:收到主机写多寄存器响应帧时
- 源码摘录:
ems_set_fault_bit(20,(g_t_from_machine_networking_info.u8_parallel_lost_flag != FALSE)); ems_set_fault_bit(21,(l_u8_from_machine_lost != FALSE));//心跳检测 if(abs(g_t_from_machine_networking_info.u32_rcv_tick - OSTimeGet()) >= 15000) // 超过15S没有接收到组网帧数据 { if (g_t_from_machine_networking_info.u8_parallel_lost_flag == FALSE) { g_t_from_machine_networking_info.u8_parallel_lost_flag = TRUE; g_t_from_machine_networking_info.u8_parallel_addr_en = FALSE; g_t_from_machine_networking_info.u8_parallel_addr = 0; memset(&g_t_from_machine_running_info,0x00,sizeof(PARALLEL_FROM_MACHINE_RUNNING_INFO_T)); } }
现场注意:15 s 是一个比较宽松的门槛,短暂插拔通讯线不会立刻报。
bit 21 | E0035 | EMS_FAULT_PARALLEL_FORM_LOST | parallel_form_lost
方向:本机是"主机",报"有从机掉了"
- 置位点/清除点:
midware/inverter/parallel.c:1015(同一个parallel_data_handle()) - 触发条件:只要
i = 1 ~ PARALLEL_MAX_NUM-1中有任意一台从机的
g_t_host_machine_networking_info[i].u8_parallel_lost_flag != FALSE,本机即报 E0035
(循环在 parallel.c:1006-1012,用局部变量 l_u8_from_machine_lost 汇总)
- 清除条件:所有已启用的从机的丢失标志都回到 FALSE
- 消抖/延时:不在这一层。上游心跳判据:
- SCI(主机侧):
parallel_sci.c:648-661——该从机u8_parallel_addr_en != FALSE且距上次收到帧 ≥ 15 s,
- SCI(主机侧):
置位同时把 u8_parallel_addr 清 0
- CAN(主机侧):
parallel_can.c:493-497——同为 15 s- 清 0:CAN 侧
parallel_can.c:164(收到某从机入网帧)、SCI 侧parallel_sci.c:153(同上)
- 清 0:CAN 侧
- 源码摘录:
for (size_t i = 1; i < PARALLEL_MAX_NUM; i++) { if (g_t_host_machine_networking_info[i].u8_parallel_lost_flag != FALSE) { l_u8_from_machine_lost = TRUE; } } ems_set_fault_bit(20,(g_t_from_machine_networking_info.u8_parallel_lost_flag != FALSE)); ems_set_fault_bit(21,(l_u8_from_machine_lost != FALSE));
现场注意:E0035 只告诉你"有从机丢了",不告诉你是哪一台。 哪一台要去看各自的从机地址/运行数据是否为零。
bit 22 | E0036 | EMS_FAULT_PV_CHARGER_HOST_LOST | pv_charger_host_lost
- 置位点/清除点:
midware/inverter/pvcharger.c:249,在inverter_pvcharger_info_handle()
(pvcharger.c:159-250)内,由 task_inverter 调用(inverter.c:1168),周期 80 ms
- 触发条件:
s_u8_pvcharger_comm_flag == FALSE - 清除条件:该标志回 TRUE 即清 0
- 消抖/延时:上游是 15 s(见下)
- 上游数据来源(
s_u8_pvcharger_comm_flag,pvcharger.c:29,是文件内静态变量):- 非"从机模式"时恒为 TRUE(
pvcharger.c:161-166):只要u8_pv_charger_mode != PVCHARGR_FROM_E,
- 非"从机模式"时恒为 TRUE(
每轮都直接写 TRUE 并刷新时间戳 —— 也就是说这个故障只在 PV 充电器被当成从机、 由外部主机(Host)通过逆变器来管的时候才可能报
- 从机模式(
== PVCHARGR_FROM_E)且距上次收到帧 ≥ 15000 ms(pvcharger.c:169-176)→ 置 FALSE
并 memset 清空整块 PV 充电器数据
- 收到帧即置 TRUE 并刷新时间戳(
pvcharger.c:146-150,pvcharger_from_machine_recv_sync()) - 源码摘录:
if (g_t_inv_info.t_config_data.u8_pv_charger_mode != PVCHARGR_FROM_E) { pvcharger_local_info_handle(); s_u8_pvcharger_comm_flag = TRUE; s_u32_pvcharger_recv_tick = OSTimeGet(); } else { if(abs(s_u32_pvcharger_recv_tick - OSTimeGet()) >= 15000) // 超过15S没有接收到帧数据 { if (s_u8_pvcharger_comm_flag != FALSE) { s_u8_pvcharger_comm_flag = FALSE; memset(&g_modbus_pvchargr_info,0x00,sizeof(PVCHARGR_INFO_T)); } } ... ems_set_fault_bit(22,(s_u8_pvcharger_comm_flag == FALSE));
现场注意:普通单机不会报 E0036。报了先确认 PV 充电器模式配置。
bit 23 | E0040 | EMS_FAULT_INV_INITIALIZATION_FAILED | inv_initialization_failed
- 置位点:
midware/inverter/alphaess/alphaess_comm_usart.c:5117;清除点:alphaess_comm_usart.c:5113
都在 alphaess_inverter_comm_usart()(alphaess_comm_usart.c:5090)内,
由 task_inverter 调用(inverter.c:1116),周期 80 ms
- 触发条件(两件事同时成立):
g_t_alphaess_comm_info.u8_inv_parameter_init_flag == TRUE(首次上电参数初始化还没做完,
该标志在 alphaess_comm_usart.c:119 等处置 TRUE)
- 距初始化开始
> 3000 ms(alphaess_comm_usart.c:5106)- 此时调用
system_start_first_setting()(alphaess_comm_usart.c:3685-3716)仍然返回 FALSE
- 此时调用
该函数依次做三件事,任一失败即 FALSE:
query_inv_function_enable_data_alphaess() → query_inv_safety_actpower_setting() →
query_inv_quiescent_volt_reactpower_support()
- 清除条件:
system_start_first_setting()返回 TRUE 的同一刻写入清 0,
并把 u8_inv_parameter_init_flag 置 FALSE(alphaess_comm_usart.c:5108-5113)
- 消抖/延时:3 s。而且注意:置位分支里带
return(alphaess_comm_usart.c:5118),
初始化没成功之前,后面的通讯流程整段被跳过 —— 这是个"卡住"的故障
- 上游数据来源:AlphaESS 逆变器的参数配置读写(功能使能、安规功率设置、无功支撑参数)
- 源码摘录:
if (g_t_alphaess_comm_info.u8_inv_parameter_init_flag == TRUE) { if ((OSTimeGet() - g_t_alphaess_comm_info.u32_query_inv_parameter_init_flag_tick) > 3000) { if(system_start_first_setting()) { query_inv_pe_loss_check_flag(); query_inv_pu_mode_active_response_3tau(); g_t_alphaess_comm_info.u8_inv_parameter_init_flag = FALSE; ems_set_fault_bit(23,FALSE); } else { ems_set_fault_bit(23,TRUE); return; } } else { return; } }
现场注意:E0040 通常伴随逆变器整机不受控/数据不刷新(因为 return 把后续流程跳过了)。 根因一般是逆变器侧参数写不进去(通讯质量、逆变器本身未就绪、安规参数不匹配)。
bit 24 | E0041 | EMS_FAULT_EPO_TRIGGER | epo_trigger
- 置位点:
midware/interface/interface.c:162、interface.c:170;清除点:interface.c:194
都在 epo_control_handle()(interface.c:139-202)内,由 task_interface_parse 调用(interface.c:225),周期 500 ms
- 触发条件(EPO = Emergency Power Off,急停干接点):
- 路径 A(
interface.c:158-165):EPO 功能已在配置里使能(epo_read_config_enable() == TRUE)
- 路径 A(
且 EPO 端子信号有效(epo_read_signal_status() == TRUE)
→ 置 1 + epo_set_protect_enable(TRUE) + 进入 EPO_SEND_SHUTDOWN 状态
- 路径 B(
interface.c:168-176):EPO 保护已经使能过,但端子信号又恢复了
→ 依然置 1(此时是为了配合"重新开机"流程)
- 清除条件:状态机走到
EPO_SEND_STARTUP,执行完inverter_startup()之后写 0
(interface.c:191-197)。也就是说只有"EPO 恢复 → 重新开机成功"这一条路能清掉
- 消抖/延时:
- 开机后 30 s 内完全不动作(
interface.c:150,(OSTimeGet() - s_u32_tick_cond_start) <= 30*1000直接 return)
- 开机后 30 s 内完全不动作(
—— 这段只在 ALPHAESS_TYPE 编译分支下存在
- 状态机本身 500 ms 轮询一次;
EPO_SEND_SHUTDOWN会OSTimeDlyHMSM(0,0,0,500)并同时关逆变器和电池 - 上游数据来源:干接点输入(
epo_read_signal_status())与配置项(epo_read_config_enable()) - 源码摘录:
case EPO_IDLE: if(TRUE == epo_read_config_enable()) { if(TRUE == epo_read_signal_status()) { ems_set_fault_bit(24,TRUE); epo_set_protect_enable(TRUE); s_u8_epo_status = EPO_SEND_SHUTDOWN; } else { if((TRUE == epo_read_protect_enable()) && (FALSE == epo_read_signal_status())) { ems_set_fault_bit(24,TRUE);
现场注意:E0041 = 有人按了急停 / 急停回路断开了。这是人为或接线问题,不是设备故障。 复位方式:把急停恢复,等系统自动走"重新开机"流程;开机成功才清码。
bit 25 | E0042 | EMS_FAULT_WIFI_P1_PAIRING_FAULT | u8_wifi_P1_pairing_fault
- 置位/清除点(直接写位域,不走
ems_set_fault_bit):
midware/meter/meter.c:7047(写 1)、meter.c:7043(写 0),函数是
meter_set_grid_wifi_P1_fault()(meter.c:7039-7049)
- 触发条件(全部在
midware/ethernt/wireless/wireless_driver.c的 P1 数据读取流程里):wireless_driver.c:480:电网侧电表确实配成 P1(meter_get_meter_or_ct(GRID_METER) == P1_SAMPLING),
但通信棒不支持 P1 —— 需同时满足
u8_wifi_support_P1_falg == TRUE 且 无线类型是 E_LINKER_WIFI 或 E_LINKER_4G。
任一不满足 → 置 1 并 return(注释写明"WIFI 不适配 P1 此时再发 P1 的 AT 指令会影响设备上线")
wireless_driver.c:596-605:读 P1 的 AT 指令连续失败。
计数 s_u8_timeout_count 在 <= 15 时递增,超过 15(即第 17 次仍失败)→ 置 1
- 清除条件(三条):
wireless_driver.c:468:电网侧电表不是 P1 → 清 0wireless_driver.c:593:AT 指令读取 + 解析成功(l_u8_res == TRUE)→ 清 0,
同时把 s_u8_timeout_count 归 0
- 未匹配的其他情况不写(保持原值)
- 消抖/延时:16~17 次连续超时;单次读取之间有节流(距上次读取 < 800 ms 或距上次 UART 动作 < 30 ms
或 UART 被占用时直接 return,wireless_driver.c:484)
- 上游数据来源:通信棒(eLinker WiFi/4G 模块)的 AT 指令交互 —— 这是
主控与通信棒之间的配对/适配问题,不是电表本身坏
- 源码摘录:
if((g_t_wireless_info.u8_wifi_support_P1_falg == TRUE)&&((g_t_wireless_info.u8_wireless_type == E_LINKER_WIFI)||(g_t_wireless_info.u8_wireless_type == E_LINKER_4G))) {} else{ meter_set_grid_wifi_P1_fault(1); // WIFI不适配P1告警 return;// WIFI不适配P1 此时再发P1的AT指令会影响设备上线 }else {//通信棒不适配 P1查询指令 if(s_u8_timeout_count <= 15) { s_u8_timeout_count++; } else { meter_set_grid_wifi_P1_fault(1); } }
现场注意:报 E0042 时电表数据也不会上来(因为通信棒压根读不到 P1)。 先确认通信棒型号/固件是否支持 P1,再怀疑电表。
bit 26 | E0079 | EMS_FAULT_BAT_HEAT_FAULT | u8_bat_heat_fault
- 置位/清除点(直接写位域):
data/fault_warn_log/fault_warn_log.c:2155(写 1)、
fault_warn_log.c:2151(写 0),函数是 set_bat_heat_fault_flag()(fault_warn_log.c:2147-2157)
- 触发条件(两个调用点,都在电池加热流程
midware/bms/bms.c内):bms.c:2611:电池自加热状态机进入BMS_INV_HEAT_FAULT_FSM(加热故障态)时写入
(进入该态的判据在 bms.c:2591-2596:BMS 报过热码、或报加热故障码、或电池连接检查异常;
另一条在 bms.c:2576-2580:加热超过 9000 ms 未见效)
bms.c:1828:bat_heat_fault_drop_out()(bms.c:1814-1838)"加热异常退出"流程的第 2 步,
即第 1 步下发停加热指令后、超过 3100 ms 时写入
- 清除条件(两个调用点):
bms.c:2306:bat_inv_heat_deal()回到BMS_NORMAL_FSM(正常态)时清 0bms.c:2455:bat_self_heat_data_inti()(加热数据初始化,加热使能关闭或流程重启时)清 0
- 消抖/延时(关键,都是确切数值):
- 加热超时判故障:9000 ms(
bms.c:2576) - 异常退出流程里下发退出指令的等待:3100 ms(
bms.c:1822) - 故障至少呈现 10 分钟才允许恢复:
bms.c:2618——(OSTimeGet() - u32_heat_fault_tick) > 360000
- 加热超时判故障:9000 ms(
且电池连接正常
- 恢复机会最多 3 次:
bms.c:2620——u8_heat_fault_recovery_count < 3才允许回正常态 - 上游数据来源:BMS 上报的过热码/加热故障码(
bms_read_over_temperature_code()、
bms_read_heat_fault_code())、电池连接检查 bat_bat_connet_check()、加热继电器状态 u8_heat_relay_sta
- 源码摘录:
case BMS_INV_HEAT_FAULT_FSM: g_t_bat_info.t_run_info.u8_bms_heat_cmd = 0x55; //向BMS下发退出自加热指令 if(s_heat_self_fsm_sub[BMS_INV_HEAT_FAULT_FSM] == BMS_ENTER_SUB) { g_t_bat_info.t_static_data.u8_heat_state_display = 2; set_bat_heat_fault_flag(1); //记录加热故障 g_t_bat_info.t_static_data.u32_heat_fault_tick = OSTimeGet();if(((OSTimeGet() - g_t_bat_info.t_static_data.u32_heat_fault_tick) > 360000) && (bat_bat_connet_check() == 0)) //加热故障最少呈现10分钟故障状态能上传,且 预留故障恢复的时间 { if(g_t_bat_info.t_static_data.u8_heat_fault_recovery_count < 3)//退出故障加热机会使用三次以内直接恢复正常状态
现场注意:E0079 报出来之后不会马上消失,最少要挂 10 分钟;而且恢复机会用满 3 次后 系统就不再自动恢复了。冬天现场遇到,多半是 加热膜/加热继电器/电池温度传感器 方向。
bit 27 | E0085 | EMS_FAULT_AFCI_SELF_CHECK_FAULT | afci_self_check_fault
- 置位点:
midware/afci/afci_comm.c:727;清除点:midware/afci/afci_comm.c:691;
另有一个外部清除入口 midware/inverter/inverter.c:7980(inverter_set_afci_self_check_clear(0),
在 inverter.c:7975-7983)
- 触发条件:
afci_self_check_handle()(afci_comm.c:663-738)的状态机走到AFCI_FAULT_STATE,
且错误计数 s_u8_err_cnt > 3(即已经失败 4 次)时置 1(afci_comm.c:720-728),
同时置 u8_afci_self_result_shut_down = TRUE(停机)
- 清除条件(两条):
afci_comm.c:687-701:自检成功 ——query_afci_self_check_status()为真
且 query_afci_warning_message() 为真,则清 0 并把 u8_afci_self_check_result 置 TRUE
inverter.c:7980:外部调用inverter_set_afci_self_check_clear(0)时清 0
- 消抖/延时:连续失败 4 次(
s_u8_err_cnt > 3);每次自检之间间隔 > 8 s
(afci_comm.c:685,(OSTimeGet() - u32_ems_afci_self_check_tick) > 8*1000)
- 上游数据来源:AFCI 模块(电弧检测)通过通讯返回的自检状态与告警报文。
调用入口在 alphaess_comm_usart.c:5060(afci_self_check_handle())
- 源码摘录:
case AFCI_FAULT_STATE: s_u8_afci_self_check_sent = FALSE; send_afci_clear_warning_message(); g_t_inv_info.t_afci_info.u32_ems_afci_self_check_tick = OSTimeGet(); if(s_u8_err_cnt > 3) { g_t_inv_info.t_afci_info.u8_afci_self_check_flag = FALSE;//自检使能标志位 g_t_inv_info.t_afci_info.u8_afci_self_check_result = FALSE; g_t_inv_info.t_afci_info.u8_afci_self_result_shut_down = TRUE;//停机 s_u8_err_cnt = 0; set_info_status(AFCI_SELFCHECK_STATUS); ems_set_fault_bit(27,TRUE); }
现场注意:报 E0085 时机器是停机状态(u8_afci_self_result_shut_down = TRUE)。
自检失败 4 次才报,所以柜台看到这个码,基本可以认定 AFCI 模块或它的通讯有问题。
bit 28 | E0092 | EMS_FAULT_BAT_MIXED_INSTALLATION_NUM_ERROR | u8_bat_mixed_installation_num_error
- 置位点:
midware/bms/bms.c:3384;清除点:bms.c:3388(数量恢复正常)、bms.c:3393(前提条件不成立)
都在 get_bat_mixed_installation_num_error_flag()(bms.c:3375-3395)内,
由 task_bms 调用(bms.c:2927),周期 100 ms
- 触发条件(三个层次,缺一不报):
- 电池型号必须是 13.9P:
bms_read_battery_model()与
- 电池型号必须是 13.9P:
TYPE_SMILE_M_BAT_13_9P_1 或 TYPE_SMILE_M_BAT_13_9P_2 完全一致(bms.c:3377-3378)
get_risk_of_system_installation_plan() == TRUE
—— 该函数(inverter.c:8066-8079)只在"机型 = 盛能杰 SET_TYPE 且 子型号 = SUB_M10"时返回 TRUE
- 电池数量超限:
- 混装标志为 0(新老电池混装)时:
bms_read_num() > 2(超过 2 块)- 混装标志为 1(全新电池)时:
bms_read_num() > 3(超过 3 块)
- 混装标志为 1(全新电池)时:
- 混装标志为 0(新老电池混装)时:
(bms.c:3381-3382)
- 清除条件:数量回到阈值以内(第 3 条不成立),或第 1/2 条前提不成立
- 消抖/延时:无
- 上游数据来源:BMS 上报的电池型号字符串、电池数量
u8_num、混装标志
u8_bat_mixed_installation_flag(读取接口 bms_read_mixed_installation_flag(),bms.c:3364)
- 源码摘录:
if(((memcmp(bms_read_battery_model(),TYPE_SMILE_M_BAT_13_9P_1,sizeof(TYPE_SMILE_M_BAT_13_9P_1)) == 0) || (memcmp(bms_read_battery_model(),TYPE_SMILE_M_BAT_13_9P_2,sizeof(TYPE_SMILE_M_BAT_13_9P_2)) == 0)) && get_risk_of_system_installation_plan() == TRUE) { if((bms_read_mixed_installation_flag() == 0 && bms_read_num() > 2) || (bms_read_mixed_installation_flag() == 1 && bms_read_num() > 3)) { ems_set_fault_bit(28,TRUE); }
现场注意:这是安装方案合规性告警,不是硬件坏。 含义是"这套 M10 + 13.9P 电池的方案,电池数量配超了"。⚠️ 前提 2 的实现见 本文件末「不确定的地方」第 2 条。
bit 29 | E0094 | EMS_FAULT_MID_METER_LOST | u8_mid_meter_lost
- 置位点:
midware/meter/meter.c:4587;清除点:meter.c:4591、meter.c:4596
都在 meter_fault_handle() 内
- 触发条件(先看功能有没有使能):
- 若
g_t_meter.t_config_data.u8_mid_meter_function_enable == TRUE(MID 电表功能已启用):
- 若
g_t_meter.t_mid_info.u8_comm_lost_err == TRUE → 置 1
- 若功能未使能 → 无条件强制清 0(
meter.c:4596) - 清除条件:
u8_comm_lost_err清 0,或功能被关掉 - 消抖/延时:不在这一层,上游有两条:
mid_meter_detect()(meter.c:1157-1181):连续 30 次搜不到才置丢失
(调用节流 METER_DETECT_INTERVAL_TIME = 1000 ms → 约 30 s),
置位同时清运行数据;搜到一次就清 0(meter.c:1164)
mid_meter_read_data()(meter.c:4273):连续u8_lost_cnt > METER_LOST_MAX_CNT(15 次)
无有效响应 → 清数据 + 置丢失(meter.c:4330-4340)
- 上游数据来源:MID 三相电表的 Modbus 通讯
- 源码摘录:
//电表丢失 if(g_t_meter.t_config_data.u8_mid_meter_function_enable == TRUE) { if (g_t_meter.t_mid_info.u8_comm_lost_err == TRUE) { ems_set_fault_bit(29,TRUE); } else { ems_set_fault_bit(29,FALSE); } } else { ems_set_fault_bit(29,FALSE); }// 通讯丢失处理 if (g_t_meter.t_mid_info.u8_lost_cnt > METER_LOST_MAX_CNT) { memset(&s_dataTmp, 0, sizeof(s_dataTmp)); meter_run_data_clear(MID_METER); g_t_meter.t_mid_info.u8_found = FALSE; g_t_meter.t_mid_info.u8_comm_lost_err = TRUE; }
现场注意:没装 MID 电表、或功能没开,永远不会有 E0094。
二、死位(永不报)——7 个
全工程对这 7 个位的位域名做了全文检索(.c + .h),除了 data/data.h 里的定义行,
零命中。 也没有任何 ems_set_fault_bit(0/7/8/11/12/14/15, ...) 调用
(已逐个列出全工程 42 处 ems_set_fault_bit( 的实参,出现的索引集合恰为
{1,2,3,4,5,6,10,13,16,17,18,19,20,21,22,23,24,27,28,29} 共 20 个,
加上直接写位域的 bit9/bit25/bit26,合计 23 个活位)。
| bit | 故障码 | 枚举名 | 位域名 |
|---|---|---|---|
| 0 | E0001 | EMS_FAULT_NET_FAULT | u8_net_fault |
| 7 | E0008 | EMS_FAULT_BAT_VOLT_LOW | u8_bat_volt_low |
| 8 | E0009 | EMS_FAULT_BACKUP_OVERLOAD | backup_overload |
| 11 | E0012 | EMS_FAULT_PARALLEL_COMM_ERROR | parallel_comm_error |
| 12 | E0013 | EMS_FAULT_PARALLEL_MODE_DIFFER | parallel_mode_differ |
| 14 | E0015 | EMS_FAULT_SDRAM_ERROR | u8_sdram_error |
| 15 | E0016 | EMS_FAULT_EXTENSION_CAN_ERROR | u8_extension_can_error |
说明: 这 7 个位的码表登记是完整的(fault_warn_log.c:519-534 里都有映射,
所以日志系统认得它们、理论上能记录),但当前 V0.11.676 版本没有任何代码路径会把它们置 1,
因此现场永远不会在屏幕上/App 上看到这 7 个码。
要么是历史遗留(比如 bit8 backup_overload 的同类逻辑已经迁到别的结构体上,
midware/inverter/ginlong/ginlong_comm_usart.c:999 用的是 ginlong 自己结构体里的同名位域,
跟 EMS 位图无关),要么是给后续版本预留的位置。
排查时不要在这 7 个码上浪费时间。
三、现场应该最先记住的几条规律
- **"丢通讯"类故障(E0004/E0005/E0006/E0007/E0010/E0011/E0034/E0035/E0036/E0094)
的门槛都在 15 s ~ 30 s 级别**,瞬断不会报;反过来报出来就说明已经断了相当久,不要用"刚插拔了一下线"解释。
- 离网(UPS)状态、电网频率 < 45 Hz、断点续传/升级中 这三种情况会让
E0004/E0005/E0006/E0094 中的一部分故意不报(例如 meter.c:3652-3666、
meter.c:4076-4089、wireless_driver.c:621-627)。不报 ≠ 没坏,要结合现场工况看。
- E0014(Flash)、E0031(失联 12 h 后的强制关机)、E0033(异常工况强制关机)三个码一旦出现就是永久锁存,
代码里没有清除路径,只有断电/复位才能消。看到"重启一次就没了"不要以为是好了。
- E0010(从机丢失)的根因在逆变器内部(主从 DSP 之间),EMS 只是把逆变器上报的位抄过来,
属于纯镜像;E0042(WIFI 不适配 P1)的根因在通信棒与 P1 电表的配对,不是电表坏。
- E0003 附带自愈:只有"读失败、后续写成功"的页会被主动清标志,所以闪一下就消失的 E0003 可以观察,
一直挂着的才要处理。
- 英国 G100(G98/G99)配置下,任意一个通讯类故障都会导致禁止馈网
(logic.c:390-395 聚合 6 个位 → logic.c:506-511 切成 STATE3_FOBID_FEEDIN,原因码 OVER_STATE3_COM_FAULT)。
现场如果反馈"机器不卖电了",先查这 6 个码有没有一个挂着。
四、不确定的地方(如实标注)
- 编译期机型宏
INVERTER_TYPE在本明文基线里等于INVERTER_NULL(0)
(midware/inverter/inverter.h:172,git 04e72700 提交内容一致;user/EMS_3_0.uvprojx 里
也没有任何 INVERTER_TYPE 的编译期宏定义)。
这会影响两处结论:
- bit16/E0017:条件里
(INVERTER_TYPE == INVERTER_NULL)这一项会恒为真,
按字面读,只要 u16_oem_flag != OEM_HHE,E0017 就永远挂着 —— 这显然不是实际出货行为
- bit28/E0092:其前提函数
get_risk_of_system_installation_plan()(inverter.c:8066)
整个实现包在 #if ((INVERTER_TYPE == INVERTER_MAX) || (INVERTER_TYPE == SET_TYPE)) 里,
按字面读会恒返回 FALSE,E0092 永远不报
→ 我的判断是:这份明文快照的 inverter.h 机型宏很可能不是实际发货固件所用的值
(实际应是 INVERTER_MAX,即"通吃"版本,因为 inverter.c 里到处是
#if ... || (INVERTER_TYPE == INVERTER_MAX) 加运行时 get_inverter_type() 判断的写法)。
但我没有证据(platform-ems-3-6/ 是加密二进制、不可读),所以本文件里这两条
我都按"字面条件 + 现场实际含义"两条都写了,没有替哪个结论下判断。
建议后续用一个能确认机型宏的渠道核实一次,再定稿。
- bit19/E0033 的第二个条件
inverter_get_bat_breaker_statuse() == 2
我只是照代码写了"逆变器报告电池断路器状态 = 2",没有去追这个 2 在逆变器协议里代表什么 (是"断开"还是"异常")。如果要写进定稿给现场用,建议再查一次逆变器协议文档。
- bit18/E0032 里的
u16_ct_volt_a < 300
源码没有标注这个量的单位(同文件 cal_grid_vol() 里也没有注释)。
我按原文写了数值 300,没有换算成伏特。定稿前不要替它加单位。
u8_factory_flag(bit17/E0031 的条件之一)
它只在 system_config.c:108 的 system_sn_init() 里被置 TRUE,另作为 FactoryFlag
参数注册给服务器(net/server.c:3944),可被下发改写。
我在代码里没有找到把它置 FALSE 的自动逻辑,所以"什么时候算出厂完成"这一点我没有查清,
只写了"实际含义是还没走完出厂配置"这个推断,请以推断看待。
- 所有"任务周期"数字
来自各任务循环末尾的 OSTimeDlyHMSM() 实参(如 inverter.c:1188 的 80、meter.c:5881 的 300)。
task_system_data 那一行是 OSSemPend(g_t_power_down_sem, 500, &err)(data/data.c:263),
我按 500 个 OS tick 记为"≈500 ms",没有去核对系统 tick 频率。
其余任务有 OSTimeDlyHMSM 直接给毫秒,是确定的。
五、维护
- 本分册结论全部来自源码静态阅读,未做板级验证。
- 源码更新后需重新提取:23 个活位的置位点可用
grep -rn "ems_set_fault_bit" --include="*.c" platform-ems-3-6-plain/ 快速重定位。
- 任何修改都要写入
docs/变更日志.md(需求号 REQ-001)。