# 编码实现 - WS2812B 可编程 RGB 灯效 (Docs/60_coding/mod-led.md) 本文件对应可编程幻彩 RGB 灯珠硬件 SPI 驱动与报警指示灯控的编码实现细节。 ## 1. 对应代码源文件 * [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义 SPI 配置、展宽查表及 1ms 定时中断中的闪烁控制) ## 2. 关键代码片段与逻辑实现 ### 2.1 驱动初始化与管脚分配 WS2812B 输入脚接在单片机硬件 SPI 组管脚中的 MOSI (`P2.3`): ```c sbit WS2812_DI = P2^3; void WS2812_Init(void) { // P2.3 (MOSI) 配置为推挽输出,默认输出低电平 0 P2M1 &= ~(1 << 3); P2M0 |= (1 << 3); WS2812_DI = 0; } ``` ### 2.2 硬件 SPI 防噪声极速发送 (WS2812_Write24Bit) 由于 SPI 时钟 SCLK 与马达引脚复用 `P2.5`,发送期间关闭总中断并将 `P2.5` 设为高阻输入以规避马达杂音,通过 SPDAT 展开 24bit 数据串行输出。 展宽 LUT 定义: ```c const u16 code SPI_LUT[16] = { 0x924, 0x926, 0x934, 0x936, 0x9A4, 0x9A6, 0x9B4, 0x9B6, 0xD24, 0xD26, 0xD34, 0xD36, 0xDA4, 0xDA6, 0xDB4, 0xDB6 }; ``` 发送函数实现: ```c void WS2812_Write24Bit(u8 g, u8 r, u8 b) { u8 data buf[10]; u8 b0, b1, b2, b3, b4, b5, b6, b7, b8, b9; buf[0] = 0x00; // 虚拟前导字节,吸收硬件启动瞬态抖动 WS2812_EncodeByte(g, &buf[1]); WS2812_EncodeByte(r, &buf[4]); WS2812_EncodeByte(b, &buf[7]); b0 = buf[0]; b1 = buf[1]; b2 = buf[2]; b3 = buf[3]; b4 = buf[4]; b5 = buf[5]; b6 = buf[6]; b7 = buf[7]; b8 = buf[8]; b9 = buf[9]; EA = 0; // 关中断保证 SPI 传输的连续性 // 配置 P2.5 为高阻输入模式,屏蔽马达噪声 P2M1 |= (1 << 5); P2M0 &= ~(1 << 5); SPSTAT = 0xC0; // 清除 SPI 状态寄存器 SPDAT = b0; // 发送数据 SPCTL = 0xD0; // 开启 SPI 硬件传输 // 展开的非阻塞高速发送时序 - 发送第 1 个灯珠 (Top LED) while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9; // 展开的非阻塞高速发送时序 - 发送第 2 个灯珠 (Bottom LED, 级联相同颜色) while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPCTL = 0x90; // 关闭 SPI 硬件使能 WS2812_DI = 0; // 恢复 P2.5 为推挽输出,控制马达常规功能 P2M1 &= ~(1 << 5); P2M0 |= (1 << 5); MOTOR = 0; // 确保马达断开 EA = 1; // 恢复中断 } void WS2812_Reset(void) { WS2812_DI = 0; // 延时 > 80us 触发重置 { u8 i; for (i = 0; i < 100; i++) { _nop_(); } } } ``` ### 2.3 报警状态交替闪烁逻辑 (LED_Flash_Process) 在 `Timer1_Isr` (每 10ms 调用一次) 中,管理指示灯闪烁状态: ```c u16 led_flash_time = 0; bit led_flash_state = 0; bit led_running_flag = 0; u8 led_color_r = 0, led_color_g = 0, led_color_b = 0; void LED_Flash_Process(void) { if (!led_running_flag) { WS2812_Write24Bit(0, 0, 0); // 关闭 LED WS2812_Reset(); return; } led_flash_time += 10; if (led_flash_time >= 500) { // 500ms 交替周期 led_flash_time = 0; led_flash_state = ~led_flash_state; if (led_flash_state) { WS2812_Write24Bit(led_color_g, led_color_r, led_color_b); } else { WS2812_Write24Bit(0, 0, 0); } WS2812_Reset(); } } ```