led.c
led_display_test() 开机自检5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
hmi/led.c所属模块:hmi(人机界面) 代码行数:1084
1. 文件职责
本文件是 EMS 的指示灯(LED)状态机与显示驱动,负责把系统的运行状态转换成面板上各个 LED 的亮灭/闪烁。它不初始化任何硬件,只调用 hardware/gpio/gpio.c 里已经封装好的 LED_Green_ON()/led_db0_on() 之类的电平操作函数。
数据流位置:上游由 hmi/key.c 的 task_hmi_parse() 任务驱动(hmi/key.c:8896 调用 led_display_test() 做开机自检,hmi/key.c:8953 每轮循环调用 led_display()),显示类型 type 由 inverter_read_display_type()(midware/inverter/inverter.c:1776)按机型/OEM/SN 决定;下游则是 GPIO 电平与(林内项目专用)WS2812 灯带的颜色状态量 sys_current_status_color。
它的核心工作是:先统一计算 6 个"逻辑灯"状态(正常/故障/电池/电表/联网亮灭 + 联网颜色),再按 type 分支把这 6 个状态映射到 4 种物理灯板的驱动输出:字符 LCD 面板的 4 颗独立 LED、四代机长灯条(led_db0..db4)、双色小灯板(led_db0..db5)、五代机亚克力灯板(LED_Red/LED_Blue + led_db0..db7,含 5 位故障码显示与 5 段 SOC 柱)。
其中 DISPLAY_LCD_RKP_E(林内项目)分支不直接驱动 GPIO,而是计算出一个 4 值枚举写入全局 sys_current_status_color,交给新增的 WS2812 灯带模块 hmi/led/app_led.c 消费。
2. 关键数据结构与宏
本文件不定义任何结构体/枚举/宏,全部使用外部定义的类型与宏。
| 名称 | 类型(结构体/枚举/宏) | 说明 | ||
|---|---|---|---|---|
DISPLAY_TYPE_E | 枚举(定义于 hmi/key.h:187-195) | 显示硬件类型,取值 DISPLAY_NONE_E、DISPLAY_LCD_CHARACTER_E(LCD 面板)、DISPLAY_LED_LAMP_STRIP_E(四代机灯条)、DISPLAY_LED_BICOLOR_E(双色小灯板)、DISPLAY_LED_LAMP_PANEL_E(五代机亚克力灯板)、DISPLAY_LCD_PARAGRAPH_E、DISPLAY_LCD_RKP_E(林内项目)。本文件的 type 参数即此枚举 | ||
STATUS_LED_COLOR_E | 枚举(定义于 hmi/led/app_led_cfg.h:48-54) | 长灯带系统状态色:SYS_STATUS_NOT_RUN_LED_OFF(0)/SYS_STATUS_NORMAL_LED_GREEN(1)/SYS_STATUS_FAULT_LED_RED(2)/SYS_STATUS_WARNING_LED_YELLOW(3),本文件在 RKP 分支写入 sys_current_status_color | ||
INVERTER_TYPE / INVERTER_MAX / KELONG_TYPE / GINLONG_TYPE / SET_TYPE / ALPHAESS_TYPE / GOODWE_TYPE / INOVANCE_TYPE | 宏(midware/inverter/inverter.h:163-172) | 机型编译开关,本文件用 `#if ((INVERTER_TYPE == INVERTER_MAX) \ | \ | ...)` 决定哪些灯板分支参与编译 |
cCheckMode/cNormalMode/cUPS | 宏(midware/inverter/inverter.h:22-24,值 2/3/4) | 逆变器运行模式,用于"正常灯""电表灯""故障灯"的判定 | ||
GRID_METER / PV_METER | 枚举(midware/meter/meter.h:165-166) | 电表站点选择,用于电表灯判定 | ||
AC_SYSTEM_MODE / HYBIRD_SYSTEM_MODE | 宏(logic/logic.h:22、logic/logic.h:20,值 0x01/0x03) | 系统模式,用于电表灯的双表判定分支 | ||
g_t_error_log_tab | 全局结构体 ERROR_LOG_TABLE_T(data/fault_warn_log/fault_warn_log.h:692、703) | 故障日志表;本文件读 u_bms_display_error.bit.u8_bms_lost、u_inv_display_error.bit.u8_inv_lost/u8_grid_loss_fault、u_accessories_display_error.bit.u8_grid_meter_lost/u8_pv_meter_lost | ||
INV_DISPLAY_ERROR_U | 联合体(data/fault_warn_log/fault_warn_log.h:537-583) | 逆变器显示故障位域,u8_grid_loss_fault 位于 BYTE3 bit0(fault_warn_log.h:574) | ||
BMS_DISPLAY_ERROR_U | 联合体(fault_warn_log.h:488-535) | BMS 显示故障位域,u8_bms_lost 位于 BYTE0 bit0(fault_warn_log.h:495) | ||
ACCESSORIES_DISPLAY_ERROR_U | 联合体(fault_warn_log.h:585-632) | 附件(电表/充电桩等)显示故障位域,u8_grid_meter_lost/u8_pv_meter_lost 位于 BYTE0 bit0/bit1(fault_warn_log.h:592-593) | ||
sys_current_status_color | 全局变量(STATUS_LED_COLOR_E,定义于 hmi/led/app_led.c:44) | 长灯带(WS2812)系统状态颜色,本文件在 hmi/led.c:654 写入 | ||
g_t_logic_info.t_config_data.u8_system_mode | 全局结构体字段(logic/logic.h:317) | 系统类型(DC/AC/Hybird),电表灯判定用 | ||
g_t_app_info.u8_app_connect_flag | 全局结构体字段(net/ems.h:353) | APP 直连标志,联网灯最高优先级 | ||
g_stEMSInfo.bIsBuiltSocket / .bIsLogin | 全局结构体字段(net/ems.h:285-286、467) | 云端 socket 建立/登录标志 | ||
g_t_wireless_info.u8linkstatus / .u8_wireless_found | 全局结构体字段(midware/ethernt/wireless/wireless_driver.h:88) | 路由/无线模块连接与发现标志 |
3. 函数清单
| 函数名 | 作用域(static/全局) | 说明(一句话) |
|---|---|---|
led_display_test | 全局(hmi/led.c:42) | 开机/切换机型时的灯显自检:按 type 依次点亮全部可用 LED(每套流程末尾 OSTimeDlyHMSM(...,300)),用于产线与上电确认硬件 |
analysis_error_code | static(hmi/led.c:125) | 取 32 位故障码中最低的置位 bit 序号,作为 5 位灯显编码(≥31 时归 0) |
led_display | 全局(hmi/led.c:157) | 指示灯周期刷新主体:计算 6 个逻辑灯状态,再按 type 分支驱动对应灯板 |
本文件共 3 个函数,已全部列出。
4. 核心逻辑解析
4.1 整体结构
led_display(type)(hmi/led.c:157-1083)是一次调用完成全部工作的无状态机式条件刷新函数,可分三段:
- 升级闪烁计数(
hmi/led.c:179-206) - 6 个逻辑灯状态的统一计算(
hmi/led.c:208-383) - 按
type分派到 5 种灯板的输出映射(hmi/led.c:386-1082)
所有中间状态都用函数内 static 变量保存(s_u8_normal_led_sts 等),因此 led_display() 必须被周期调用才能产生闪烁效果。
4.2 升级/下载指示状态机
if ((IsSystemDownload() == TRUE) || (IsSystemUpgrade() == TRUE))
if (abs(s_u32_upgrade_tick - OSTimeGet()) > 200)
{ s_u32_upgrade_tick = OSTimeGet(); s_u8_upgrade_cnt++; } // hmi/led.c:180-187
- 每 200 ms 让
s_u8_upgrade_cnt自增一次,作为"流水灯"的相位计数。 s_u8_upgrade_sts(hmi/led.c:190-205):1 = 正在下载(IsSystemDownload())、2 = 正在升级(IsSystemUpgrade())、0 = 空闲。该段在INVERTER_TYPE != GOODWE_TYPE时编译。- 注意
s_u8_upgrade_cnt为u8,长期处于升级态会自然回绕(0x00~0xFF),各灯板用%3/%4取相位,回绕不影响相位连续性(256 是 4 的倍数,也是 3 的倍数? 256%3=1,会有一次多余跳变,但可忽略)。
4.3 6 个逻辑灯状态的判定条件
| 逻辑灯 | 变量 | 判定逻辑(hmi/led.c 行号) |
|---|---|---|
| 正常(绿) | s_u8_normal_led_sts | cUPS → TRUE;任一故障码(ems_read_fault_code() / inverter_read_fault_code() / ..._extend() / ..._extend2() / bms_read_fault_code())→ FALSE;cNormalMode → TRUE;cCheckMode(开机自检)→ 每 500 ms 翻转一次;其他 → FALSE(208-238) |
| 故障(红) | s_u8_fault_led_sts + s_u8_error_led_sts | cUPS → FALSE;任一故障码(额外含 get_accessories_display_error_code())→ TRUE,并在故障期间每 100 ms 翻转 s_u8_error_led_sts 产生闪红;无故障 → FALSE(240-257) |
| 电池(黄/电池灯) | s_u8_battery_led_sts | bms_get_ready_enable() → FALSE(电池未就绪);bms_get_connect_state() 且 bms_read_work_status() != FALSE → TRUE;已连接但未工作 → 每 500 ms 翻转;未连接 → FALSE(259-282) |
| 电表 | s_u8_meter_led_sts | 分支一:亚克力灯板 + cUPS → 每 100 ms 翻转;分支二:AC_SYSTEM_MODE/HYBIRD_SYSTEM_MODE → 两表都连 TRUE,仅 PV 连每 200 ms 翻转,仅 GRID 连每 500 ms 翻转,都不连 FALSE;分支三:其他系统模式只判 GRID_METER 是否连接(284-333) |
| 联网 | s_u8_net_led_sts、s_u8_net_led_color | 五级优先级(335-383),见 4.4 |
| 故障码灯 | l_u8_display_code | 仅亚克力灯板分支内计算,见 4.7 |
4.4 联网灯优先级与颜色
hmi/led.c:335-383 是按优先级排列的 if / else if 链:
| 优先级 | 条件 | 亮灭 | 颜色 s_u8_net_led_color |
|---|---|---|---|
| 1 | g_t_app_info.u8_app_connect_flag == TRUE(APP 直连) | 每 100 ms 翻转 | 在 0 ↔ 2 之间交替(调色闪烁) |
| 2 | g_stEMSInfo.bIsBuiltSocket && g_stEMSInfo.bIsLogin(已登录云端) | 常亮 | 固定 2(绿) |
| 3 | g_stEMSInfo.bIsBuiltSocket(已建 TCP,未登录) | 每 2000 ms 翻转 | 在 0 ↔ 2 之间交替 |
| 4 | g_t_wireless_info.u8linkstatus(路由已连) | 每 1000 ms 翻转 | 固定 1(红) |
| 5 | g_t_wireless_info.u8_wireless_found(已发现无线模块) | 每 500 ms 翻转 | 在 0 ↔ 1 之间交替 |
| — | 以上都不满足 | 灭 | 0 |
颜色值语义:0 = 灭,1 = 红,2 = 绿(见 hmi/led.c:170 注释、4.8 双色板用法)。
4.5 闪烁节拍实现方式
所有闪烁统一使用"绝对时间差 > 阈值则翻转"的写法,例如:
if (abs(s_u32_net_tick - OSTimeGet()) > 2000)
{ s_u32_net_tick = OSTimeGet(); s_u8_net_led_sts = (s_u8_net_led_sts == FALSE); }
周期为 2×阈值(翻转间隔 + 保持时间),即 100 ms 阈值 → 200 ms 完整周期;2000 ms 阈值 → 4 s 完整周期。基准时钟为 uCOS-II 的 OSTimeGet()(1 tick = 1 ms,UCOSII/uCOS-CONFIG/os_cfg.h:51 OS_TICKS_PER_SEC = 1000u)。
4.6 各种灯板的输出映射
(1) DISPLAY_LCD_CHARACTER_E(字符 LCD 面板,hmi/led.c:386-433)
4 颗独立 LED 直接映射:s_u8_normal_led_sts → LED_Green_ON/OFF;s_u8_fault_led_sts → LED_Red;s_u8_battery_led_sts → LED_Yellow;s_u8_net_led_sts → LED_Blue。特例:透传模式且未识别到逆变器(get_passthrough_flag() && get_inverter_type() == INVERTER_NULL)时 4 颗全亮。
(2) DISPLAY_LED_LAMP_STRIP_E(四代机灯条,hmi/led.c:434-530)
升级时用 s_u8_upgrade_cnt 生成 4 拍流水覆盖状态灯:下载态 l_u8_led_sts = 3-(cnt%4),升级态 l_u8_led_sts = cnt%4;4 拍分别把"故障 / 正常+电池 / 电表 / 全灭"打到 s_u8_normal_led_sts/s_u8_fault_led_sts/s_u8_battery_led_sts/s_u8_meter_led_sts。输出映射:led_db0=正常、led_db1=故障、led_db2=电池、led_db3=电表、led_db4=联网。
(3) DISPLAY_LED_BICOLOR_E(双色小灯板,hmi/led.c:531-620)
升级时用 3 拍流水(下载 cnt%3,升级 2-(cnt%3)),并在升级时强制 s_u8_fault_led_color = 2。正常输出:led_db0=正常、led_db1=常灭、led_db2/db3=故障双色(s_u8_fault_led_color==2 时点 db2,否则点 db3)、led_db4/db5=联网双色(s_u8_net_led_color==1 点 db5 红,==2 点 db4 绿)。
(4) DISPLAY_LCD_RKP_E(林内项目长灯带,hmi/led.c:621-658)
不驱动任何 GPIO,只做一次 4 值状态归约 + 去抖:
has_fault = ems/inv/inv_ext/inv_ext2/bms 故障码 || get_accessories_display_error_code();
has_warning = !has_fault && bms_get_connect_state() && !bms_read_work_status();
has_normal = !has_fault && !has_warning && (run_mode == cNormalMode || run_mode == cUPS);
if (has_fault) new_status = SYS_STATUS_FAULT_LED_RED; // 2 红
else if (has_warning) new_status = SYS_STATUS_WARNING_LED_YELLOW; // 3 黄
else if (has_normal) new_status = SYS_STATUS_NORMAL_LED_GREEN; // 1 绿
else new_status = SYS_STATUS_NOT_RUN_LED_OFF; // 0 灭
去抖:状态变化时只刷新 s_u32_last_change,连续保持 200 ms 不变才把 new_status 写入 sys_current_status_color(hmi/led.c:650-656)。s_u8_last_status 初值 0xFF 作为"尚未确定"哨兵,保证首次必写。该全局量被 hmi/led/app_led.c:89-93 读取。
(5) DISPLAY_LED_LAMP_PANEL_E(五代机亚克力灯板,hmi/led.c:659-1081)
引脚分配:LED_Red = 系统红;led_db0 = 联网;led_db1 = 电池;led_db2 = 系统绿/故障码 bit0(二选一);led_db3 = 电表/故障码;led_db4..db7 = SOC 柱或故障码 bit1..bit4;LED_Blue = SOC 满或故障码 bit0。
- 升级态(
hmi/led.c:662-776):先按故障/正常决定LED_Red与led_db2;led_db1电池、led_db3电表照常;LED_Blue与led_db4..db7组成 4 拍流水 —— 下载态相位 0 亮LED_Blue+db7,相位 1 亮db4+db6,相位 2 亮db5,相位 3 全灭;升级态相位顺序整体后移(0 亮db5、1 亮db4+db6、2 亮LED_Blue+db7、3 全灭)。
4.7 故障码 5 位灯显编码
analysis_error_code()(hmi/led.c:125-148)从 bit0 开始扫描 32 位故障码,返回最低置位 bit 的序号;若扫描完未找到(值为 0)或结果 ≥31,则返回 0。返回值 l_u8_display_code 的 bit0..bit4 分别驱动 LED_Blue、led_db4、led_db5、led_db6、led_db7(hmi/led.c:939-982)。
亚克力灯板的故障分支按优先级四级互斥(hmi/led.c:779-937):
| 优先级 | 条件 | 行为 | ||||||
|---|---|---|---|---|---|---|---|---|
| 1 | 通讯故障:u_bms_display_error.bit.u8_bms_lost \ | \ | u_inv_display_error.bit.u8_inv_lost \ | \ | u_accessories_display_error.bit.u8_grid_meter_lost \ | \ | ...u8_pv_meter_lost | led_db2 灭,LED_Red 亮(逆变器掉线时用 s_u8_error_led_sts 闪红,其余常亮),l_u8_display_code = 0 |
| 2 | get_bms_display_error_code() 非 0 | 系统红常亮,电池灯用 s_u8_error_led_sts 闪烁,显示码 = BMS 故障码最低位 | ||||||
| 3 | get_inv_display_error_code() 非 0 | 系统红闪烁(由 s_u8_error_led_sts),电池灯正常,显示码 = 逆变器故障码最低位 | ||||||
| 4 | get_accessories_display_error_code() 非 0 | 系统红常亮,电表灯用 s_u8_error_led_sts 闪烁,显示码 = 附件故障码最低位 | ||||||
| — | 均无(但 s_u8_fault_led_sts 为真) | 仅按 s_u8_error_led_sts 闪红,显示码 0 |
正常的"无故障"分支(hmi/led.c:984-1070):u_inv_display_error.bit.u8_grid_loss_fault(市电丢失)→ LED_Red 常亮、led_db2 灭;否则 LED_Red 灭、led_db2 跟随 s_u8_normal_led_sts。SOC 柱按 bms_read_total_soc() 分 6 档点亮 led_db4..db7 + LED_Blue:
bms_read_total_soc() 区间(单位 0.1%) | 点亮 |
|---|---|
| ≤ 50(≤5%) | 全灭 |
| ≤ 200(≤20%) | db7 |
| ≤ 400(≤40%) | db6、db7 |
| ≤ 600(≤60%) | db5、db6、db7 |
| ≤ 800(≤80%) | db4~db7 |
| > 800(>80%) | LED_Blue + db4~db7 |
最后无论何种分支,都用 s_u8_net_led_sts 驱动 led_db0(hmi/led.c:1073-1080)。
4.8 led_display_test() 开机自检
hmi/led.c:42-116 按 type 依次动作,每套流程结束时 OSTimeDlyHMSM(0,0,0,300) 挂起 300 ms:
DISPLAY_LCD_CHARACTER_E:绿/红/黄/蓝四灯同亮。DISPLAY_LED_LAMP_STRIP_E(KELONG/GINLONG/INVERTER_MAX):LED_Blue+led_db0..db4同亮。DISPLAY_LED_BICOLOR_E(GINLONG/INVERTER_MAX):先亮db0/db2/db4,延时 300 ms 后切换到db1/db3/db5,db6/db7保持灭。DISPLAY_LED_LAMP_PANEL_E(INVERTER_MAX/SET/ALPHAESS/INOVANCE):led_db0..db7与LED_Blue全亮,LED_Red灭;延时 300 ms 后LED_Red点亮、led_db2灭。
该函数由 hmi/key.c:8896 在 task_hmi_parse() 初始化阶段调用一次,会阻塞 HMI 任务约 300~600 ms。
5. 对外接口
本文件导出
| 名称 | 类型 | 使用方 |
|---|---|---|
led_display_test | 全局函数(声明于 hmi/led.h:22) | hmi/key.c:8896 |
led_display | 全局函数(声明于 hmi/led.h:23) | hmi/key.c:8953 |
本文件不导出任何全局变量(自身定义的都是函数内 static)。
本文件调用的外部函数/变量
| 名称 | 来源模块 |
|---|---|
LED_Green_ON/OFF、LED_Red_ON/OFF、LED_Yellow_ON/OFF、LED_Blue_ON/OFF、led_db0_on/off…led_db7_on/off | hardware/gpio/gpio.c(声明 hardware/gpio/gpio.h:137-148) |
OSTimeGet、OSTimeDlyHMSM | uCOS-II |
abs | C 标准库 |
IsSystemDownload、IsSystemUpgrade | net/ems_download.c(声明 net/ems_download.h:41-42) |
ems_read_fault_code | data/data.c(声明 data/data.h:127) |
inverter_read_run_mode、inverter_read_fault_code、inverter_read_fault_code_extend、inverter_read_fault_code_extend2、get_inverter_type | midware/inverter/inverter.c(声明 inverter.h:2011-2019、1940) |
bms_get_ready_enable、bms_get_connect_state、bms_read_work_status、bms_read_fault_code、bms_read_total_soc | midware/bms/bms.c |
meter_read_connect_status | midware/meter/meter.c |
get_bms_display_error_code、get_inv_display_error_code、get_accessories_display_error_code | data/fault_warn_log/fault_warn_log.c |
get_passthrough_flag | midware/debug/ate.c |
g_t_error_log_tab | data/fault_warn_log/fault_warn_log.c(SDRAM 固定地址) |
g_t_logic_info | logic/logic.c |
g_t_app_info | net/ems.c |
g_stEMSInfo | midware/ethernt/ethernet.c |
g_t_wireless_info | midware/ethernt/wireless/ |
sys_current_status_color | 写出(hmi/led.c:654),定义于 hmi/led/app_led.c:44 |
6. 依赖关系
依赖(本文件 include):led.h(13)、fault_warn_log.h(14)、key.h(15)、gpio.h(16)、ems.h(17)、meter.h(18)、logic.h(19)、inverter.h(20)、alphaess_comm_usart.h(21)、ethernet.h(22)、data.h(23)、bms.h(24)、ems_download.h(25)、ate.h(26)。
被依赖:
hmi/key.c(task_hmi_parse()每轮调用led_display())hmi/led/app_led.c(读取本文件写入的sys_current_status_color)
7. 备注与风险点
abs(x - OSTimeGet())的溢出/回绕写法:OSTimeGet()返回 32 位毫秒 tick,约 49.7 天回绕一次。本文件全部用abs(有符号差值)(如hmi/led.c:182、248、272、288、303、311、338、352、363、371)而不是(u32)(a - b) > T,回绕瞬间或长时间停机后abs()会得到巨大值,导致闪烁相位错乱或误翻转。hmi/led/app_led.c:83使用的是正确的无符号差值写法,两处风格不统一。sys_current_status_color写入被#if包围(hmi/led.c:621-658):该分支在INVERTER_MAX/ALPHAESS_TYPE之外不编译,而hmi/led/app_led.c的 WS2812 灯带在任何机型下都会编译,于是非 ALPHAESS 机型下sys_current_status_color永远停留在初值SYS_STATUS_NOT_RUN_LED_OFF(长灯带恒灭)。analysis_error_code()的else分支是空操作(hmi/led.c:142-145:l_u8_faultt_cnt_temp = l_u8_faultt_cnt_temp;),且返回值只使用 bit0..bit4,因此最低置位 bit 在 5~30 之间的故障码在亚克力灯板上表现为"无故障码图案"(5 个灯全灭),与通讯故障的显示完全一致,容易误导现场判断。led_display_test()内使用OSTimeDlyHMSM阻塞:双色板与亚克力灯板分支各有一次 300 ms 中间延时,加上函数末尾固定的 300 ms,单次调用最长阻塞约 600 ms,在 HMI 任务初始化阶段调用。- 同一物理 LED 复用:亚克力灯板下
LED_Blue与led_db2分别承担"故障码 bit0 / SOC 满"与"系统状态/故障"两种语义,画面语义随分支变化;s_u8_normal_led_sts在升级分支内被改写(hmi/led.c:452-481),升级结束后虽会重新计算,但同轮之后的代码读到的是被流水覆盖后的值。 - 重复的故障条件表达式:
ems_read_fault_code() || inverter_read_fault_code() || inverter_read_fault_code_extend() || inverter_read_fault_code_extend2() || bms_read_fault_code()在hmi/led.c:213、245、628-630重复出现三次,任一处漏改会导致"正常灯"与"故障灯"逻辑不一致。 s_u8_error_led_sts是全局闪烁相位:电池灯、电表灯、系统红都复用同一个s_u8_error_led_sts(hmi/led.c:251),因此多个故障同时存在时闪烁是同相位的,无法区分是哪一个部件故障。- 机型开关事实:本快照
midware/inverter/inverter.h:172把INVERTER_TYPE定义为INVERTER_NULL(0),因此#if ((INVERTER_TYPE == INVERTER_MAX) || ...)形式的灯板分支在本仓库状态下预处理为假,实际生效的只有DISPLAY_LCD_CHARACTER_E与(被#if排除的)其他分支之外的代码;发布构建如何取INVERTER_TYPE需以当时的 Keil 工程配置为准。 - 文件头注释为通用模板(
hmi/led.c:1-8,@version V1.0.0、@date 2017/05/18),与当前 1084 行的实际内容严重不符,属历史遗留。