Files
stc32g128k/Docs/60_coding/mod-led.md

4.4 KiB
Raw Blame History

编码实现 - WS2812B 可编程 RGB 灯效 (Docs/60_coding/mod-led.md)

本文件对应可编程幻彩 RGB 灯珠硬件 SPI 驱动与报警指示灯控的编码实现细节。

1. 对应代码源文件

  • App/main.c (定义 SPI 配置、展宽查表及 1ms 定时中断中的闪烁控制)

2. 关键代码片段与逻辑实现

2.1 驱动初始化与管脚分配

WS2812B 输入脚接在单片机物理引脚 LED_RGB (P2.3)

sbit WS2812_DI = P2^3; // Pin 24

void WS2812_Init(void) {
    // P2.3 (LED_RGB) 配置为推挽输出,默认输出低电平 0
    P2M1 &= ~(1 << 3);
    P2M0 |=  (1 << 3);
    WS2812_DI = 0;
}

2.2 驱动防噪声发送 (WS2812_Write24Bit)

由于马达引脚占用 P2.5 (Pin 26),为了规避马达震动时的共地杂噪,在串行输出数据期间关闭总中断并将马达引脚 P2.5 设为高阻输入。 展宽 LUT 定义:

const u16 code SPI_LUT[16] = {
    0x924, 0x926, 0x934, 0x936, 0x9A4, 0x9A6, 0x9B4, 0x9B6,
    0xD24, 0xD26, 0xD34, 0xD36, 0xDA4, 0xDA6, 0xDB4, 0xDB6
};

发送函数实现:

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; // 关中断保证串行传输的连续性
    
    // 配置 P2.5 (MOTOR_PWM) 为高阻输入模式,屏蔽共地马达干扰
    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 (MOTOR_PWM) 为推挽输出,恢复马达控制
    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 调用一次) 中,管理指示灯闪烁状态:

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();
    }
}