首先说明硬件接线调整:WS2812 灯珠串联时,第一个灯珠的 DI 引脚连接 RA6E2 的 P001 引脚,VCC 接开发板 5V 电源,GND 与开发板 GND 共地,后续灯珠的 DO 引脚连接下一个灯珠的 DI 引脚。
接下来对代码进行拆解说明:
需要包含必要的头文件,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()指令数量,确保高低电平时长符合要求。

更多回帖