跳到主要内容

驱动 WS2812 流水灯

本文案例代码下载地址
驱动 WS2812 流水灯案例代码https://www.aw-ol.com/downloads?cat=24

R128-DevKit 拥有4颗 WS2812 LED,本文将详细叙述如何点亮他们。

image-20230730203429678

LEDC 模块简介

image-20230730203939070

LEDC 硬件方框图如上图所示,CPU 通过 APB 总线操作 LEDC 寄存器来控制 LEDC;当 CPU配置好 LEDC 的相关寄存器之后,通过 CPU 或 DMA 将 R、G、B 数据从 DRAM 搬到 LEDC FIFO 中,启动 LEDC 之后就可以通过 PIN 脚向外部的 LED 发送数据了。

image-20230324173621026

LED 典型电路如图所示,其中 DI 表示控制数据输入脚,DO 表示控制数据输出脚。DI 端接收从控制器传过来的数据,每个 LED 内部的数据锁存器会存储 24bit 数据,剩余的数据经过内部整形处理电路整形放大后通过 DO 端口开始转发输出给下一个级联的 LED。因此,每经过一个LED,数据减少 24bit。

image-20230324173635697

注意,如果在单次直接设置第 n 个 LED 的亮度和色彩的时候,前面 n-1 个 LED 的亮度数据会在第 n 个 LED 的数据前发送,不过这些数据将会是原来 n-1 个 LED 的亮度数据。

由于拥有独立的 LEDC 模块,在 R128 平台上驱动 WS2812 类似的 RGB LED 不需要使用 SPI 模拟,也不需要使用 PWM 配置时序。直接使用这个模块即可。

设置 LEDC 驱动

运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 LEDC Devices

Drivers Options  --->
soc related device drivers --->
LEDC devices --->
[*] enable ledc driver

找到 LEDC Devices

image-20230730203555869

勾选如下选项

image-20230730204157518

配置 LEDC 参数

参考电路图可知,LEDC 模块连接的是 R128 的 PA13 引脚。参考手册可知 MUX 为 7

image-20230730204335088

前往 lichee/rtos/drivers/rtos-hal/hal/source/ledc/platform/ledc_sun20iw2.h 并编辑 LEDC 的引脚和MUX

#define LEDC_PIN	GPIOA(13)
#define LEDC_PINMUXSEL 7

image-20230730204505356

然后编辑 lichee/rtos/drivers/rtos-hal/hal/source/ledc/hal_ledc.c 配置 WS2812 的时序参数:

struct ledc_config ledc_config = {
.led_count = 4,
.reset_ns = 84,
.t1h_ns = 1000,
.t1l_ns = 1000,
.t0h_ns = 580,
.t0l_ns = 1000,
.wait_time0_ns = 84,
.wait_time1_ns = 84,
.wait_data_time_ns = 600000,
.output_mode = "GRB",
};

image-20230730204634317

编译测试

编译后烧录开发板

image-20230730205555943

可以用命令 hal_ledc 来测试

hal_ledc <LED号> <R|G|B> <亮度>

点亮红色 LED

运行命令

hal_ledc 1 R 100

image-20230730205753242

即可点亮第一颗 LED

image-20230730205731463

点亮绿色 LED

运行命令

hal_ledc 2 G 100

image-20230730205921963

第二颗 LED 即可点亮绿色

image-20230730205956129

实现七彩流水灯

前往项目文件夹编辑 main.c,这里我选择在 M33 核心上编写程序,所以选用的是 lichee/rtos/projects/r128s2/module_m33/src/main.c ,如果是编写 C906 核心的程序,请修改 lichee/rtos/projects/r128s2/module_c906/src/main.c

#include <sunxi_hal_ledc.h>
#include <hal_cmd.h>
#include <hal_timer.h>

// 使用RGB 分量合成颜色值
#define MERAGECOLOR(G, R, B) (((uint32_t)G << 16) | ((uint16_t)R << 8) | B)
#define PIXEL_NUM 4

// 生成颜色
uint32_t WS281x_Wheel(uint8_t wheelPos) {
wheelPos = 255 - wheelPos;
if (wheelPos < 85) {
return MERAGECOLOR(255 - wheelPos * 3, 0, wheelPos * 3);
}
if (wheelPos < 170) {
wheelPos -= 85;
return MERAGECOLOR(0, wheelPos * 3, 255 - wheelPos * 3);
}
wheelPos -= 170;
return MERAGECOLOR(wheelPos * 3, 255 - wheelPos * 3, 0);
}

// 亮度设置
uint32_t WS281xLSet(uint32_t rgb, float k) {
uint8_t r, g, b;
float h, s, v;
uint8_t cmax, cmin, cdes;

r = (uint8_t) (rgb >> 16);
g = (uint8_t) (rgb >> 8);
b = (uint8_t) (rgb);

cmax = r > g ? r : g;
if (b > cmax)
cmax = b;
cmin = r < g ? r : g;
if (b < cmin)
cmin = b;
cdes = cmax - cmin;

v = cmax / 255.0f;
s = cmax == 0 ? 0 : cdes / (float) cmax;
h = 0;

if (cmax == r && g >= b)
h = ((g - b) * 60.0f / cdes) + 0;
else if (cmax == r && g < b)
h = ((g - b) * 60.0f / cdes) + 360;
else if (cmax == g)
h = ((b - r) * 60.0f / cdes) + 120;
else
h = ((r - g) * 60.0f / cdes) + 240;

v *= k;

float f, p, q, t;
float rf, gf, bf;
int i = ((int) (h / 60) % 6);
f = (h / 60) - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i) {
case 0:
rf = v;
gf = t;
bf = p;
break;
case 1:
rf = q;
gf = v;
bf = p;
break;
case 2:
rf = p;
gf = v;
bf = t;
break;
case 3:
rf = p;
gf = q;
bf = v;
break;
case 4:
rf = t;
gf = p;
bf = v;
break;
case 5:
rf = v;
gf = p;
bf = q;
break;
default:
break;
}

r = (uint8_t) (rf * 255.0);
g = (uint8_t) (gf * 255.0);
b = (uint8_t) (bf * 255.0);

return ((uint32_t) r << 16) | ((uint32_t) g << 8) | b;
}

// 延时函数
static inline int msleep(int ms) {
vTaskDelay(ms / portTICK_RATE_MS);
}

// 测试 LEDC
int ledc_test_loop() {
int i = 0, j = 0, err;
int mode = 0;
uint8_t R = 0, G = 0, B = 0;

err = hal_ledc_init();
if (err) {
printf("ledc init error\n");
return -1;
}

while (1) {
for (j = 0; j < 256; j++) {
for (i = 0; i < PIXEL_NUM; i++) {
sunxi_set_led_brightness(
i + 1, WS281xLSet(WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255), 0.2));
msleep(1);
}
msleep(10);
}
}
return 1;
}

并且将测试函数加入到 cpu0_app_entry 中。

image-20230730210755413

重新烧录即可实现七彩流水灯

image-20230730210852640