led_dev.h
WS2812_FLAG_U4.4 与 uCOS-II / 中断的配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
hmi/led/led_dev.h所属模块:hmi/led(人机界面 - WS2812 灯带编码/驱动层) 代码行数:87
1. 文件职责
本文件是 WS2812 灯带编码/驱动层的头文件,定义灯带规格(颗数、每颗 bit 数、DMA 缓冲大小)、时序脉冲常量(T0_PULSE/T1_PULSE)、读写互斥标志联合体 WS2812_FLAG_U,并声明 5 个驱动函数与 2 个全局对象。
它处在灯带数据流的最底层入口:上层 hmi/led/app_led.c 通过 ws2812_set_color_hex() 逐颗写入编码缓冲,通过 ws2812_update() 触发一次 DMA 发送;中断侧 hardware/dma/dma.c:266-283(DMA0_Channel0_IRQHandler)通过 g_u_ws2812_flag.bit.write_flag 与本层做握手。
本层不做业务判断,也不直接操作定时器/GPIO 寄存器(那些在 hardware/timer/timer4.c、hardware/dma/dma.c 中),它只负责把颜色转换成"每 bit 一个比较值"的 32 位数组。
被谁驱动:system/board_init/board_init.c:242 在上电初始化阶段调用 ws2812_init();之后的 ws2812_update() 只可能由 hmi/led/app_led.c:207 的 led_build_full_buffer() 调用(当前该调用链未接线)。
2. 关键数据结构与宏
| 名称 | 类型(结构体/枚举/宏/联合体) | 说明 |
|---|---|---|
__LED_DEV_H__ | 宏(防重包含) | 头文件保护宏,hmi/led/led_dev.h:9-10 |
LED_LONG_NUM | 宏(= 101) | 长灯带(系统状态指示灯)灯珠数,hmi/led/led_dev.h:21。注释说明:绿=正常、红=故障、黄=告警、灭=未运行 |
LED_SOC_NUM | 宏(= 20) | 短灯带(SOC 灯显)灯珠数,hmi/led/led_dev.h:27。注释写"精度为10%、左侧 0%、右侧 100%、白=联网、蓝=未联网" |
RKP_LED_NUM | 宏(= LED_LONG_NUM + LED_SOC_NUM = 121) | 灯珠总数,hmi/led/led_dev.h:32。注释残留 //最终39,与实际 121 不符 |
LED_BITS | 宏(= 24) | 每颗灯珠的数据位数(GRB 各 8 bit),hmi/led/led_dev.h:33 |
LED_BUFFER_SIZE | 宏(= RKP_LED_NUM * LED_BITS = 2904) | DMA 缓冲区表项数,hmi/led/led_dev.h:34 |
T0_PULSE | 宏(#if 1 分支 = 54) | WS2812 数据"0"对应的定时器比较值,hmi/led/led_dev.h:38。注释:0.32/0.00595 ≈ 53.7 个周期 |
T1_PULSE | 宏(#if 1 分支 = 134) | 数据"1"对应的比较值,hmi/led/led_dev.h:39。注释:0.67/0.00595 ≈ 112.6(注释与取值不一致,取值 134) |
T0_PULSE/T1_PULSE(#else 分支) | 宏(54/134 互换) | 针对"取反"极性的备选取值,hmi/led/led_dev.h:43-44,当前被 #if 1 屏蔽 |
WS2812_FLAG_U | 联合体(hmi/led/led_dev.h:52-67) | 读写互斥标志:u8 u8_all 与位域 bit{ read_flag:1; write_flag:1; soc_color_flag:1; net_color_flag:1; sys_run_color_flag:1; reserve:3; }。本层实际只用前两位 |
g_u16_ws2812_data | 全局数组声明(u32[LED_BUFFER_SIZE + 3],hmi/led/led_dev.h:72) | DMA 编码缓冲,定义于 hmi/led/led_dev.c:61,用 __attribute__((at(SDRAM_WS2812_DATA_ADDR))) 固定到 SDRAM。多出的 3 个表项用于帧尾附加周期 |
g_u_ws2812_flag | 全局变量声明(volatile WS2812_FLAG_U,hmi/led/led_dev.h:73) | 任务/中断共用的读写标志,定义于 hmi/led/led_dev.c:62 |
3. 函数清单
本文件只有函数声明,没有任何函数定义。括号内为对应实现在 hmi/led/led_dev.c 的行号。
| 函数名 | 作用域(static/全局) | 说明(一句话) |
|---|---|---|
ws2812_init | 全局(声明 hmi/led/led_dev.h:79,定义 led_dev.c:72) | 初始化 TIMER4 的 800 kHz PWM 与 DMA0 通道 0,并清零编码缓冲 |
ws2812_update | 全局(声明 hmi/led/led_dev.h:80,定义 led_dev.c:86) | 等待写缓冲空闲后,把 PA3 切回复用功能、使能 DMA 与定时器,启动一次帧发送 |
ws2812_set_color_rgb | 全局(声明 hmi/led/led_dev.h:81,定义 led_dev.c:101) | 以 R/G/B 三通道参数写入第 led_idx 颗灯珠的 24 bit 编码(RGB 顺序,无 GRB 转换,全工程无调用点) |
ws2812_set_color_hex | 全局(声明 hmi/led/led_dev.h:82,定义 led_dev.c:118) | 以 0xRRGGBB 写入第 led_idx 颗灯珠,内部转换成 GRB 顺序后逐 bit 编码 |
ws2812_set_color_buffer_clear | 全局(声明 hmi/led/led_dev.h:83,定义 led_dev.c:153) | 把全部 RKP_LED_NUM 颗灯珠颜色清零(依赖 ws2812_set_color_hex 实现) |
注:
hmi/led/led_dev.c:170还定义了一个未在本头文件声明的函数ws2812_set_on_off_color_light(u8, u32),见hmi/led/led_dev.c.md。
4. 核心逻辑解析
本文件是纯声明头,无逻辑。它定义的常量决定了整条灯带的运行参数,推导如下:
4.1 缓冲与帧长
- 每个灯珠 24 bit,每位用 1 个
u32(存放定时器比较值)表示,因此LED_BUFFER_SIZE = 121 × 24 = 2904,数组总长 2907(+3)。占用空间2907 × 4 = 11628字节,与hardware/sdram/sdram.h:64中SDRAM_WS2812_DATA_ADDR预留的 16 KB 相符。 - 一帧的位周期由定时器决定(
hardware/timer/timer4.c:49的period = 209→ 210 个计数 @168 MHz = 1.25 µs,正好是 WS2812 的标准位宽)。因此整帧发送时间约2907 × 1.25 µs ≈ 3.63 ms。 - 帧尾的多余 3 个表项在
ws2812_init()中被memset为 0,用于在 DMA 结束后提供额外的低电平时间,配合 DMA 完成中断把 PA3 拉低形成 WS2812 要求的 >200 µs 复位(Reset)信号。
4.2 位编码方式
T0_PULSE/T1_PULSE 是定时器比较寄存器的加载值,不是电平宽度本身:
| 宏 | 值 | 注释中的对应时间 | 换算(1 计数 = 1/168 MHz = 5.95 ns) |
|---|---|---|---|
T0_PULSE | 54 | 0.32 µs | 54 × 5.95 ns ≈ 0.321 µs |
T1_PULSE | 134 | 注释写 0.67 µs(并附 (112) 备选) | 134 × 5.95 ns ≈ 0.797 µs |
hardware/timer/timer4.c:59 把通道 3 配置为 TIMER_OC_POLARITY_LOW(反相输出),而本头文件当前生效的却是标注 //不取反 的 #if 1 分支(hmi/led/led_dev.h:36-39)。两者对"取反/不取反"的标注互相矛盾,属于现场调参遗留(见第 7 节)。
4.3 读写互斥标志 WS2812_FLAG_U
| 位 | 名称 | 写入方 | 读取方 | 语义 |
|---|---|---|---|---|
| bit0 | read_flag | ws2812_set_color_hex()/ws2812_set_color_rgb() 置 1 与清 0(hmi/led/led_dev.c:109/114) | ws2812_update() 自旋等待(led_dev.c:88) | "正在修改内存缓冲" |
| bit1 | write_flag | ws2812_update() 置 1(led_dev.c:92);DMA0_Channel0_IRQHandler 清 0(hardware/dma/dma.c:280) | 全部写函数开头检查(led_dev.c:103/121/155/174) | "DMA 正在发送,禁止改缓冲" |
| bit2-4 | soc_color_flag/net_color_flag/sys_run_color_flag | 无 | 无 | 预留,未使用;reserve:3 补齐 8 位 |
由于 u8_all 与位域共用同一字节,置位/清位都是对同一字节的读-改-写,任务上下文与 DMA 中断上下文之间没有关中断保护,存在标志丢失的可能(详见第 7 节)。
4.4 与 uCOS-II / 中断的配合
- 本层不创建任务、不申请信号量/邮箱,握手完全靠
g_u_ws2812_flag这两个位 +OSTimeDlyHMSM(0,0,0,1)自旋(1 ms,因OS_TICKS_PER_SEC = 1000u)。 - 中断侧入口在
hardware/dma/dma.c:266,命中DMA_INT_FLAG_FTF后关闭定时器与 DMA 通道、把 PA3 改为输出并拉低、清write_flag。
5. 对外接口
本文件导出
| 名称 | 类型 | 使用方 |
|---|---|---|
ws2812_init | 函数声明 | system/board_init/board_init.c:242 |
ws2812_update | 函数声明 | hmi/led/app_led.c:207 |
ws2812_set_color_hex | 函数声明 | hmi/led/app_led.c:191、201(生效分支)与 169、173(#if 0 分支内)、hmi/led/led_dev.c:160、190 |
ws2812_set_color_buffer_clear | 函数声明 | hmi/led/app_led.c:160 |
ws2812_set_color_rgb | 函数声明 | 无调用点 |
g_u16_ws2812_data | 全局数组声明 | hardware/dma/dma.c:53/55(DMA 源地址与长度)、hmi/led/led_dev.c:76 |
g_u_ws2812_flag | 全局变量声明 | hardware/dma/dma.c:280(中断清 write_flag)、hmi/led/led_dev.c 全部函数 |
LED_LONG_NUM/LED_SOC_NUM/RKP_LED_NUM/LED_BITS/LED_BUFFER_SIZE/T0_PULSE/T1_PULSE/WS2812_FLAG_U | 宏/类型 | hmi/led/led_dev.c、hmi/led/app_led.c(仅 LED_SOC_NUM、RKP_LED_NUM);hardware/dma/dma.h:17 通过 include 间接可见 |
本文件调用的外部函数/变量:无(纯声明头)。其 #include "chip.h"、"sdram.h" 分别提供 u8/u16/u32 类型与 SDRAM_WS2812_DATA_ADDR 地址宏。
6. 依赖关系
- 依赖:
chip.h(15)、sdram.h(16)。 - 被依赖:
hmi/led/led_dev.c:45hmi/led/app_led_cfg.h:16→ 进而hmi/led/app_led.h:16→hmi/led.h:17→hmi/led.c、hmi/key.c、system/board_init/board_init.c、midware/debug/board_test.chardware/dma/dma.h:17(DMA 层为访问g_u16_ws2812_data/g_u_ws2812_flag而反向包含驱动层头文件,形成hardware → hmi的分层倒置)
7. 备注与风险点
RKP_LED_NUM注释过期:hmi/led/led_dev.h:32的//最终39与实际 121 颗矛盾,说明灯珠数经历过 39 → 121 的扩容而注释未同步。LED_SOC_NUM注释与实现不符:注释写"精度为10%"(hmi/led/led_dev.h:28),但实际算法给每颗灯珠 5%(hmi/led/app_led.c:188的i < get_bms_soc()*2,20 颗对应 100%)。T1_PULSE取值与注释不符:注释推导出0.67/0.00595 ≈ 112.6,代码却用134(≈0.80 µs);#if 1标注//不取反,而hardware/timer/timer4.c:59明确配置为TIMER_OC_POLARITY_LOW(取反)。两者不一致,实际输出波形需以逻辑分析仪确认,属高风险的历史调参点。WS2812_FLAG_U位域跨上下文无保护:g_u_ws2812_flag声明为volatile(hmi/led/led_dev.h:73),但volatile只阻止编译器优化,不提供原子性。任务侧g_u_ws2812_flag.bit.read_flag = TRUE/FALSE(led_dev.c:109/114)与中断侧g_u_ws2812_flag.bit.write_flag = FALSE(dma.c:280)是对同一字节的读-改-写,若中断恰好落在任务侧读-改-写之间,write_flag的清零会被覆盖,导致后续所有写函数永久被"DMA 忙"挡住 → 灯带冻结。这是本层最需要防护的竞态。hardware/dma/dma.h反向包含hmi/led/led_dev.h(hardware/dma/dma.h:17):硬件抽象层依赖hmi应用层头文件,破坏分层;且dma.h同时被大量串口/USB 文件包含,使灯带规格宏扩散到无关模块。- 缓冲区长度用
sizeof(g_u16_ws2812_data) / 4计算(hardware/dma/dma.c:55):一旦led_dev.h中把数组元素类型从u32改成u16(变量名u16_前缀已暗示历史遗留),DMA 传输长度会静默变成实际的 2 倍,超出缓冲并越界读 SDRAM。 - 变量名
g_u16_ws2812_data与实际类型u32[]不符(hmi/led/led_dev.h:72),是改名未彻底的遗留。 - 位域
reserve:3无命名,属于占位写法;WS2812_FLAG_U同时提供u8_all供整体读写,但全工程无人使用。 - 文件头注释(
hmi/led/led_dev.h:2)写的是File Name: app_led_cfg.h,与真实文件名不符。