app_led.c
led_display_task() 的脏标志聚合(hmi/led/app_led.c:68-124)4.2 led_build_full_buffer() 的灯带编码(hmi/led/app_led.c:127-208)4.3 get_bms_soc() 的 SOC 折算(hmi/led/app_led.c:210-220)4.4 get_network_status()(hmi/led/app_led.c:221-225)4.5 与 uCOS-II 的配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
hmi/led/app_led.c所属模块:hmi/led(人机界面 - WS2812 灯带应用层) 代码行数:232
1. 文件职责
本文件是 WS2812 灯带模块的应用层(策略层),负责把 EMS 的业务状态翻译成 121 颗灯珠的颜色数组,具体位流编码由下层 led_dev.c 完成。它是"系统状态 → 灯带颜色"这条链路的中间环节:下游是 led_dev.c 的 ws2812_set_color_hex()/ws2812_update()。
数据来源有三个:电池 SOC(bms_read_total_soc())、联网登录状态(g_stEMSInfo.bIsLogin)、系统运行状态色(全局 sys_current_status_color,由 hmi/led.c:654 的 DISPLAY_LCD_RKP_E 分支写入);亮度档位来自全局 u8_led_brightness_value。
驱动方式:设计上由 uCOS-II 任务周期调用 led_display_task()(hmi/led/app_led.h:20 的 LED_TASK_PERIOD 定义为 10 ms),函数内部采用"脏标志 + 1 s 强制刷新"策略,只有输入变化或超过 1 秒未刷新时才重新编码整个灯带。但当前工程中没有任何任务或代码调用 led_display_task(),因此本文件在运行期实际未被驱动(详见第 7 节)。
本文件同时定义并维护全局变量 sys_current_status_color,供 hmi/led.c 写入、本文件读取,是 hmi/led.c 与灯带模块之间唯一的状态传递通道。
2. 关键数据结构与宏
| 名称 | 类型(结构体/枚举/宏/联合体) | 说明 |
|---|---|---|
STATUS_LED_COLOR_E | 枚举(定义于 hmi/led/app_led_cfg.h:48-54) | 长灯带系统状态色:0 灭 / 1 绿 / 2 红 / 3 黄;本文件用它声明并读取 sys_current_status_color |
SOC_LED_COLOR_E | 枚举(定义于 hmi/led/app_led_cfg.h:58-62) | 短灯带颜色:SOC_LED_BLUE(0,未联网) / SOC_LED_WHITE(1,已联网);本文件用 s_current_soc_color 保存 |
LED_TEST | 宏(局部,hmi/led/app_led.c:30) | 空宏,无任何条件编译或代码引用,属调试遗留 |
brightness[] | 静态 const 数组(float[12],hmi/led/app_led.c:135-148) | 亮度档位→缩放系数映射表:索引 0=0.0(熄灭)、1=0.05、2=0.1、3=0.2、4=0.3、5=0.4、6=0.5、7=0.6、8=0.7、9=0.8、10=0.9、11=1.0 |
LED_SOC_NUM / LED_LONG_NUM / RKP_LED_NUM | 宏(hmi/led/led_dev.h:21/27/32) | 短灯带 20 颗、长灯带 101 颗、总数 121 颗;本文件用 LED_SOC_NUM 分段、用 RKP_LED_NUM 作循环上限 |
TRUE / FALSE | 宏(UCOSII/uCOS-CONFIG/includes.h) | 布尔常量,本文件大量用于脏标志比较 |
本文件定义的全局变量
| 名称 | 类型 | 说明 |
|---|---|---|
sys_current_status_color | STATUS_LED_COLOR_E(全局,非 static,hmi/led/app_led.c:44) | 长灯带系统状态色,初值 SYS_STATUS_NOT_RUN_LED_OFF。由 hmi/led.c:654 写入,本文件只读。声明在 hmi/led/app_led_cfg.h:71 |
u8_led_brightness_value | u8(全局,初值 8,hmi/led/app_led.c:49) | 灯带亮度档位 0~11,默认 70%。除本文件外无任何读写点,也没有 Modbus/界面写入路径 |
u16_led_test_num | u16(全局,初值 121,hmi/led/app_led.c:50) | 灯珠数量测试值,全工程无任何引用,死变量 |
s_current_soc_color | static SOC_LED_COLOR_E(初值 SOC_LED_BLUE,hmi/led/app_led.c:47) | 短灯带颜色缓存;注意它保存的其实是联网状态对应的颜色,变量名与语义不一致 |
3. 函数清单
| 函数名 | 作用域(static/全局) | 说明(一句话) |
|---|---|---|
get_bms_soc | static(前向声明 hmi/led/app_led.c:24,定义 hmi/led/app_led.c:210) | 读 BMS 总 SOC(0~1000,即 0.00%~100.00%)并折算成 0~10 的档位数(每档 10%) |
get_network_status | static(前向声明 hmi/led/app_led.c:25,定义 hmi/led/app_led.c:221) | 返回云端登录标志 g_stEMSInfo.bIsLogin(0/1),用作短灯带颜色选择 |
led_build_full_buffer | static(前向声明 hmi/led/app_led.c:26,定义 hmi/led/app_led.c:127) | 按当前 SOC/联网/系统状态/亮度重新编码全部 121 颗灯珠并启动一次发送 |
apply_brightness | static(定义 hmi/led/app_led.c:60,无前向声明) | 把 0xRRGGBB 颜色按 float 系数逐通道缩放后重新打包 |
led_display_task | 全局(定义 hmi/led/app_led.c:68) | 灯带刷新任务主体:聚合 5 路输入变化,置脏标志后调用 led_build_full_buffer() |
本文件共 5 个函数(含 3 个前向声明对应的 static 函数),已全部列出。
4. 核心逻辑解析
4.1 led_display_task() 的脏标志聚合(hmi/led/app_led.c:68-124)
函数内维护 5 个 static 缓存,每轮比较一次,任一变化即置 s_u8_need_update = TRUE:
| 步骤 | 输入 | 缓存变量 | 行号 |
|---|---|---|---|
| 1 | OSTimeGet() | s_u32_last_tick | 82-87:if((current_tick - s_u32_last_tick) > 1000) → 每 1 秒强制刷新一次(无条件) |
| 2 | sys_current_status_color | s_u8_sys_run_state | 89-93 |
| 3 | get_bms_soc() | s_u8_bms_soc | 96-102 |
| 4 | get_network_status()(同时写入 s_current_soc_color) | s_u8_network_status | 105-110 |
| 5 | u8_led_brightness_value | s_u8_led_brightness_value | 112-116 |
全部比较完成后统一执行(hmi/led/app_led.c:119-123):
if(s_u8_need_update == TRUE)
{ led_build_full_buffer(); s_u8_need_update = FALSE; }
注意事项:
s_u8_need_update初值为TRUE,保证任务首次执行必然刷一帧。- 时间比较
(current_tick - s_u32_last_tick) > 1000使用u32无符号差值,天然抗OSTimeGet()回绕(约 49.7 天一次),写法正确,与hmi/led.c的abs()写法不同。 - 步骤 4 把联网状态直接写进
s_current_soc_color(hmi/led/app_led.c:105),颜色语义在这里被固定,后续led_build_full_buffer()不再重新计算。
4.2 led_build_full_buffer() 的灯带编码(hmi/led/app_led.c:127-208)
执行顺序:
- 取亮度系数:
s_u8_brightness_index = u8_led_brightness_value; bright_factor = brightness[s_u8_brightness_index];(150-153)——没有做 0~11 的边界钳位。 - 取两种颜色:
short_color = get_soc_color_to_rgb(s_current_soc_color)(短灯带 = 联网状态色),long_color = get_sys_color_status(sys_current_status_color)(长灯带 = 系统状态色)(156-157)。 - 清屏:
ws2812_set_color_buffer_clear()(160)。 - 逐颗编码(
#if 0 ... #else ... #endif,实际生效的是#else分支,hmi/led/app_led.c:182-205):
for(u8 i = 0; i < RKP_LED_NUM; i++) // 121 颗
{
if(i < LED_SOC_NUM) // 前 20 颗 = 短灯带(SOC 柱)
{
if( i < get_bms_soc() * 2) // 点亮左侧 soc*2 颗
ws2812_set_color_hex(i, apply_brightness(short_color, bright_factor));
else
ws2812_set_color_hex(i, 0); // 其余熄灭
}
else // 后 101 颗 = 长灯带(系统状态)
ws2812_set_color_hex(i, apply_brightness(long_color, bright_factor));
}
ws2812_update()启动一次 DMA 发送(hmi/led/app_led.c:207)。
被编译屏蔽的 #if 0 分支(hmi/led/app_led.c:161-181)用的是另一种点亮规则 i >= LED_SOC_NUM - get_bms_soc()(点亮右侧 soc 颗),说明 SOC 柱的方向与每档颗数曾反复调整(#else 分支上方注释只有一个"昌"字,为现场调试遗留)。
亮度缩放公式(hmi/led/app_led.c:60-66):
r = (u8)(((color >> 16) & 0xFF) * brightness);
g = (u8)(((color >> 8) & 0xFF) * brightness);
b = (u8)(( color & 0xFF) * brightness);
return (r << 16) | (g << 8) | b;
系数为 float,运算结果直接截断取整(非四舍五入),因此 100% 档(1.0)不会改变颜色,0 档(0.0)会使颜色全 0。
4.3 get_bms_soc() 的 SOC 折算(hmi/led/app_led.c:210-220)
u16 soc = bms_read_total_soc(); // 0~1000,单位 0.1%
u8 percent = (u8)(soc / 10); // 0~100
if (percent == 0) return 0;
return (percent + 9) / 10; // 向上取整到 0~10 档
即返回值 = ⌈percent/10⌉,配合 led_build_full_buffer() 的 soc * 2 得到 0~20 颗点亮的 SOC 柱,每颗灯珠代表 5%(hmi/led/led_dev.h:28 的注释写"精度为10%",与实现不符)。
bms_read_total_soc() 数据类型为 u16(midware/bms/bms.h:1190),若上游异常返回 > 1000,percent 会超过 100,(percent+9)/10 可能大于 10,进而 get_bms_soc()*2 > LED_SOC_NUM,短灯带全亮(不会越界,因为循环本身受 i < LED_SOC_NUM 限制),但 SOC 指示会失真。注意 get_bms_soc() 的返回类型是 u8,(percent + 9) 在 percent=255 时为 264,未溢出 u8 计算(C 语言提升为 int),但强转返回仍会截断。
4.4 get_network_status()(hmi/led/app_led.c:221-225)
static u8 get_network_status(void)
{
extern STEMSTab g_stEMSInfo; // 函数内局部 extern 声明
return g_stEMSInfo.bIsLogin;
}
直接返回 EMS 云端登录标志(net/ems.h:285 的 BOOLEAN bIsLogin),返回 0/1 正好与 SOC_LED_COLOR_E 的 SOC_LED_BLUE(0)/SOC_LED_WHITE(1) 数值一一对应,因此调用点用 (SOC_LED_COLOR_E)get_network_status() 强转(hmi/led/app_led.c:105)。
注意:这里只用 bIsLogin,没有考虑 bIsBuiltSocket、路由(g_t_wireless_info)等中间态,与 hmi/led.c:345-383 五级优先级的联网判定相比信息量更少;函数内 extern 声明绕过了头文件(ems.h 已 include,此写法多余)。
4.5 与 uCOS-II 的配合
本文件不创建任务、不申请信号量/邮箱,也不调用 OSTimeDly*;唯一的 OS 调用是 OSTimeGet()(hmi/led/app_led.c:82、84)。任务与调度周期完全由外部调用者决定。与中断的交互发生在下层 led_dev.c/hardware/dma/dma.c:296-303(DMA 传输完成中断清除 g_u_ws2812_flag.bit.write_flag)。
5. 对外接口
本文件导出
| 名称 | 类型 | 使用方 |
|---|---|---|
led_display_task | 全局函数(声明于 hmi/led/app_led.h:43) | 无调用点 |
sys_current_status_color | 全局变量(声明于 hmi/led/app_led_cfg.h:71) | 由 hmi/led.c:654 写入,本文件 hmi/led/app_led.c:89 读取 |
u8_led_brightness_value | 全局变量(无头文件声明) | 仅本文件读写;因无 extern 声明,外部模块即使想写也会与 midware/interface/modbus/modbus.c:45 的 include 声明不匹配 |
u16_led_test_num | 全局变量(无头文件声明) | 无任何引用 |
本文件调用的外部函数/变量
| 名称 | 来源模块 |
|---|---|
bms_read_total_soc | midware/bms/bms.c(声明 midware/bms/bms.h:1190) |
get_soc_color_to_rgb、get_sys_color_status | hmi/led/app_led_cfg.c |
ws2812_set_color_hex、ws2812_set_color_buffer_clear、ws2812_update | hmi/led/led_dev.c |
OSTimeGet | uCOS-II |
g_stEMSInfo(STEMSTab) | net/ems.c(声明 net/ems.h:467) |
sys_current_status_color | 定义在本文件;写入方为 hmi/led.c |
6. 依赖关系
依赖(本文件 include):app_led.h(13)、ems.h(14)、bms.h(15)、meter.h(16)、logic.h(17)、inverter.h(18)。
其中
meter.h、logic.h、inverter.h在本文件正文中没有任何符号被使用,属于多余 include。
被依赖:
- 无。
led_display_task()无调用者;sys_current_status_color、u8_led_brightness_value、u16_led_test_num三个全局量中只有前者被外部(hmi/led.c)使用。 hmi/led.h:17会包含app_led.h,但那只影响hmi/led.c的可见性,hmi/led.c实际只用到sys_current_status_color。
7. 备注与风险点
- 整条灯带链路未接线:
led_display_task()在全工程中只有定义(hmi/led/app_led.c:68)与声明(hmi/led/app_led.h:43),没有任何调用点,user/main.c创建的 16 个任务中也没有灯带任务。因此ws2812_init()(system/board_init/board_init.c:242)之后灯带再不会被更新,led_build_full_buffer()/ws2812_update()永远不会执行。这是本模块最需要确认的问题。 brightness[]数组越界风险:索引来自全局u8_led_brightness_value(hmi/led/app_led.c:150),数组只有 12 个元素(0~11),而变量没有任何钳位、默认值 8,一旦将来被 Modbus(midware/interface/modbus/modbus.c已 includeapp_led.h)或调试命令写成 ≥12,brightness[idx]立即越界读 flash 数据,造成亮度异常或硬件异常。u8_led_brightness_value、u16_led_test_num缺少头文件声明:两个全局量定义在.c中但没有对应的extern声明,其他模块无法安全引用(hmi/led/app_led.h里没有),是明确的"半成品接口"。LED_TEST宏空定义(hmi/led/app_led.c:30)且无引用点,属调试遗留。s_current_soc_color语义错位:变量名表示"当前 SOC 颜色",实际保存的是联网状态色,由get_network_status()赋值(hmi/led/app_led.c:105);同文件中s_u8_bms_soc才是 SOC 相关缓存。容易在后续维护中误用。static u8 s_u8_brightness_index(hmi/led/app_led.c:132)被声明为static但每轮都被重新赋值(150),既是无意义的持久化,也说明作者原意可能是"平滑过渡亮度",未实现。#if 0与#else双套算法:SOC 柱两种点亮方向(i >= LED_SOC_NUM - soc与i < soc*2)同时保留在文件中,靠#if 0切换,现场容易出现"改了注释里的那套却没生效"的问题;//昌注释也未说明含义。#endif数量易读性差:hmi/led/app_led.c:154(#if 1)与161(#if 0)嵌套,205/206连续两个#endif收尾,且没有跟随注释,维护时容易配错。- 亮度缩放使用
float乘法:GD32F407 为 Cortex-M4 无 FPU,浮点按软件库执行,121 颗灯 × 3 通道 × 1 s 一次的计算量虽小,但仍属可优化点(可用 8 位定点表)。 - 亮度 0 档会把所有颜色清 0(等效熄灭),但
ws2812_update()仍会照常发送一帧,属于既有行为。 - 文件头注释(
hmi/led/app_led.c:2)写的是File Name: app_led_cfg.c,与真实文件名不符。