瑞萨单片机论坛
直播中

jf_56333662

4年用户 12经验值
擅长:可编程逻辑 嵌入式技术
私信 关注
[经验]

【瑞萨RA6E2】驱动 WS2812 实现 RGB 跑马灯效果

VID_20251125_014134

首先说明硬件接线调整:WS2812 灯珠串联时,第一个灯珠的 DI 引脚连接 RA6E2 的 P001 引脚,VCC 接开发板 5V 电源,GND 与开发板 GND 共地,后续灯珠的 DO 引脚连接下一个灯珠的 DI 引脚。
{A66423B1-1AC0-4074-B49B-E40B666E44F2}.png

接下来对代码进行拆解说明:

需要包含必要的头文件,hal_data.h用于瑞萨 FSP 框架的硬件初始化及外设控制,string.h用于内存操作函数支持:

#include "hal_data.h"
#include "string.h"

定义硬件相关参数,将 WS2812 的数据引脚改为 P001(对应瑞萨 FSP 的引脚标识为 BSP_IO_PORT_00_PIN_01),灯珠数量为 3 颗,系统主频为 RA6E2 的 120MHz,用于时序计算:

#define WS2812_PIN        BSP_IO_PORT_00_PIN_01
#define WS2812_NUM        3
#define SYS_CLOCK         120000000

WS2812 的通信时序要求严格,需通过 GPIO 模拟高低电平时长。根据手册,T0H 为 0.4μs、T0L 为 0.85μs,T1H 为 0.8μs、T1L 为 0.45μs;120MHz 主频下单个__NOP()指令约 8.33ns,因此通过计数__NOP()指令实现时序控制:

void ws2812_send_bit(uint8_t bit)
{
    if(bit)
    {
        // 发送逻辑1:T1H(0.8μs)高电平,T1L(0.45μs)低电平
        R_IOPORT_PinWrite(&g_ioport_ctrl, WS2812_PIN, BSP_IO_LEVEL_HIGH);
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        
        R_IOPORT_PinWrite(&g_ioport_ctrl, WS2812_PIN, BSP_IO_LEVEL_LOW);
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
    }
    else
    {
        // 发送逻辑0:T0H(0.4μs)高电平,T0L(0.85μs)低电平
        R_IOPORT_PinWrite(&g_ioport_ctrl, WS2812_PIN, BSP_IO_LEVEL_HIGH);
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        
        R_IOPORT_PinWrite(&g_ioport_ctrl, WS2812_PIN, BSP_IO_LEVEL_LOW);
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
    }
}

WS2812 采用 GRB 颜色数据格式,需按绿色、红色、蓝色的顺序逐位发送 24bit 数据,每个颜色分量 8 位,从高位到低位依次传输:

void ws2812_send_pixel(uint8_t g, uint8_t r, uint8_t b)
{
    uint8_t i;
    // 发送绿色分量(高位优先)
    for(i = 0; i < 8; i++) ws2812_send_bit((g << i) & 0x80);
    // 发送红色分量(高位优先)
    for(i = 0; i < 8; i++) ws2812_send_bit((r << i) & 0x80);
    // 发送蓝色分量(高位优先)
    for(i = 0; i < 8; i++) ws2812_send_bit((b << i) & 0x80);
}

批量发送所有灯珠的颜色数据时,需关闭全局中断避免时序被打断,发送完成后恢复中断;最后需输出至少 50μs 的低电平复位信号,确保灯珠锁存数据:

void ws2812_send_all(uint8_t pixels[WS2812_NUM][3])
{
    uint8_t i;
    __disable_irq();  // 关闭全局中断,保证时序完整性
    for(i = 0; i < WS2812_NUM; i++)
    {
        ws2812_send_pixel(pixels[i][0], pixels[i][1], pixels[i][2]);
    }
    __enable_irq();   // 恢复全局中断
    
    // 输出复位信号(低电平持续100μs)
    R_IOPORT_PinWrite(&g_ioport_ctrl, WS2812_PIN, BSP_IO_LEVEL_LOW);
    R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MICROSECONDS);
}

实现跑马灯效果的逻辑:定义颜色数组存储 GRB 格式的红、绿、蓝三色,通过循环变量控制当前点亮的灯珠位置,每次清空所有灯珠颜色后,仅点亮对应位置的灯珠,延时后切换位置形成循环效果:

void rgb_running_light(void)
{
    uint8_t pixels[WS2812_NUM][3] = {0};  // 灯珠颜色缓存数组,初始化为0
    uint8_t pos = 0;                      // 当前点亮的灯珠位置索引
    // GRB格式颜色定义:红色、绿色、蓝色
    uint8_t colors[3][3] = {{0, 255, 0}, {255, 0, 0}, {0, 0, 255}};
    
    while(1)
    {
        memset(pixels, 0, sizeof(pixels));  // 清空所有灯珠颜色数据
        // 设置当前位置灯珠的颜色
        pixels[pos][0] = colors[pos][0];
        pixels[pos][1] = colors[pos][1];
        pixels[pos][2] = colors[pos][2];
        
        ws2812_send_all(pixels);           // 发送颜色数据至灯珠
        // 延时500ms,控制跑马灯切换速度
        R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
        
        pos = (pos + 1) % WS2812_NUM;      // 循环切换灯珠位置
    }
}

在瑞萨 FSP 框架的程序入口函数hal_entry中调用跑马灯函数,启动效果:

void hal_entry(void)
{
    rgb_running_light();  // 执行跑马灯逻辑
    
#if BSP_TZ_SECURE_BUILD
    R_BSP_NonSecureEnter();
#endif
}

需注意的是,P001 引脚需在 FSP 配置中设置为输出模式;WS2812 工作时电流较大,建议外接 5V 电源保证供电稳定;若灯珠显示异常,需根据实际硬件时序微调__NOP()指令数量,确保高低电平时长符合要求。

{6FDE484C-DC10-4817-9B18-E34DB574FBC7}.png

更多回帖

发帖
×
20
完善资料,
赚取积分