本帖最后由 HonestQiao 于 2023-12-12 13:29 编辑
#申请原创#
本篇文章,分享了在 NUCLEO-U5A5ZJ-Q 开发板上,使用WS2812B 炫彩灯环的经验,展示的效果,包括按照顺序亮灭灯环上的灯珠,已经根据简谱点亮对应数量的灯珠。
一、硬件了解
炫彩灯带是一种非常常见的LED灯带,一般有5050灯带、WS28系列灯带(WS2801、WS2812、WS2813、WS2815和WS2818),在日常要使用灯光效果的场合,应用非常的多。
其中WS2812是最常见的一种,可以编程控制每一颗灯珠的亮灭,以及不同的颜色。
我手头有WS2812的灯板(1、2、4、8*8)、灯带(60、92)、灯环(24),其中24灯珠的灯环最常用:
我给这个灯环配了一个时空之门的底座,科幻感十足,当灯环上的灯珠亮起来的时候,仿佛开启了时空之门。
这是一款DFRobot出品的灯环,其参数如下:
工作电压:3.3~5V
最大工作电流:<100mA
接口类型:PH2.0-3P
灯珠型号:WS2812
灯珠数量:24
颜色:1600万色
要控制WS2812,除了供电以外,只需要1根数据信号线,即可控制所有的灯珠,非常的方便。
关于WS2812的具体控制原理,可以参考我的文章:【GD32F427开发板试用】点亮WS2812B炫彩灯环,里面详细讲述了灯环的电路原理和灯珠和颜色控制的原理。
在咱们 NUCLEO-U5A5ZJ-Q 开发板 开发板上,可与使用SPI的方式进行控制,将其信号线,连接到开发板的Arduino兼容接口的MOSI即可:
具体连接如下:
如果有多个这样的灯环,那灯环直接,还可以串联起来使用:
不过,需要注意的是,如果控制的灯珠较多,那么务必需要使用外部供电。
通常情况下,1颗灯珠,1种颜色,需要20mA电流,白色需要60mA电流,控制100颗,就需要最大6A电流。
二、驱动适配
在Zephyr中,提供了ws2812B的处理,但是没有提供咱们NUCLEO-U5A5ZJ-Q 开发板对应的处理定义,需要自己处理适配一下。
文件1:samples/drivers/led_ws2812/boards/nucleo_u5a5zj_q.conf
CONFIG_WS2812_STRIP_SPI=y
CONFIG_SPI=y
这个文件定义了使用SPI,以及使用SPI控制WS2812。
文件2:samples/drivers/led_ws2812/boards/nucleo_u5a5zj_q.overlay:
/*
* Copyright (c) 2022, STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/led/led.h>
&arduino_spi {
led_strip: ws2812[url=home.php?mod=space&uid=2514928]@0[/url] {
compatible = "worldsemi,ws2812-spi";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <DT_FREQ_M(4)>;
frame-format = <32768>; /* SPI_FRAME_FORMAT_TI */
/* WS2812 */
chain-length = <24>; /* arbitrary; change at will */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
color-mapping = <LED_COLOR_ID_GREEN
LED_COLOR_ID_RED
LED_COLOR_ID_BLUE>;
};
};
/ {
aliases {
led-strip = &led_strip;
};
};
这个文件定义了基于arduio_spi的led-strip设备,使用4MHz通信速率,灯珠数量24颗,颜色对应关系为GRB。
对,就是GRB,WS2812就是这样定义颜色的:
三、基础测试
在Zephyr中,提供了ws2812的基础实例:samples/drivers/led_ws2812
其中main.c内容如下:
/*
* Copyright (c) 2017 Linaro Limited
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <string.h>
#define LOG_LEVEL 4
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);
#include <zephyr/kernel.h>
#include <zephyr/drivers/led_strip.h>
#include <zephyr/device.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/sys/util.h>
#define STRIP_NODE DT_ALIAS(led_strip)
#define STRIP_NUM_PIXELS DT_PROP(DT_ALIAS(led_strip), chain_length)
#define DELAY_TIME K_MSEC(CONFIG_SAMPLE_LED_UPDATE_DELAY)
#define RGB(_r, _g, _b) { .r = (_r), .g = (_g), .b = (_b) }
static const struct led_rgb colors[] = {
RGB(0x0f, 0x00, 0x00), /* red */
RGB(0x00, 0x0f, 0x00), /* green */
RGB(0x00, 0x00, 0x0f), /* blue */
};
struct led_rgb pixels[STRIP_NUM_PIXELS];
static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE);
int main(void)
{
size_t cursor = 0, color = 0;
int rc;
if (device_is_ready(strip)) {
LOG_INF("Found LED strip device %s", strip->name);
} else {
LOG_ERR("LED strip device %s is not ready", strip->name);
return 0;
}
LOG_INF("Displaying pattern on strip");
while (1) {
memset(&pixels, 0x00, sizeof(pixels));
memcpy(&pixels[cursor], &colors[color], sizeof(struct led_rgb));
rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);
if (rc) {
LOG_ERR("couldn't update strip: %d", rc);
}
cursor++;
if (cursor >= STRIP_NUM_PIXELS) {
cursor = 0;
color++;
if (color == ARRAY_SIZE(colors)) {
color = 0;
}
}
k_sleep(DELAY_TIME);
}
return 0;
}
这个代码的逻辑如下:
1. 定义了一个 colors ,用于灯珠显示的颜色定义
2. 定义了一个 pixels ,用于存放每颗灯珠的颜色数据
3. while循环过程中,cursor用于指向下一颗要点亮的灯珠,如果打倒了最大值,则从第一颗重新点亮
4. while循环过程中,没循环一遍灯珠,就指向下一个定义的颜色,如果颜色循环完了,则从头开始。
5. 使用 led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS) 来更新灯珠的颜色,也就是通过SPI发送数据到WS2812。
编译下载后,具体的运行效果如下:
四、根据简谱控制灯珠点亮
在上面的基础测试中,是依次点亮灯珠。
下面,就根据《小星星》的简谱,来实现根据简谱点亮对应的灯珠。
首先,在代码中,添加一个颜色定义:
static const struct led_rgb num_colors[] = {
RGB(0x00, 0x00, 0x00), /* black */
RGB(0x0f, 0x00, 0x00), /* red */
RGB(0x00, 0x0f, 0x00), /* green */
RGB(0x00, 0x00, 0x0f), /* blue */
RGB(0x0f, 0x0f, 0x00), /* ?? */
RGB(0x00, 0x0f, 0x0f), /* ?? */
RGB(0x0f, 0x00, 0x0f), /* ?? */
RGB(0x0f, 0x0f, 0x0f), /* white */
};
实际的颜色,大家可以对照RGB颜色表来取值。
再添加一个曲谱的定义:
uint8_t nums[] = {1,1,5,5,6,6,5,0,4,4,3,3,2,2,1,0,5,5,4,4,3,3,2,0,5,5,4,4,3,3,2,0,1,1,5,5,6,6,5,0,4,4,3,3,2,2,1};
然后,把while循环替换为:
while(1) {
memset(&pixels, 0x00, sizeof(pixels));
for(int i=0;i<nums[cursor];i++) {
memcpy(&pixels[i], &num_colors[nums[cursor]], sizeof(struct led_rgb));
}
rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);
if (rc) {
LOG_ERR("couldn't update strip: %d", rc);
}
cursor++;
if (cursor >= sizeof(nums)/sizeof(nums[0])) {
cursor = 0;
}
k_sleep(K_MSEC(500));
}
上述代码,就是通过循环简谱的每一位,来确定要点亮的灯珠数量,控制pixels 中灯珠的颜色。具体的颜色,再根据要控制的数量,从 num_colors 中取值。
编译下载后,运行效果如下:
五、总结
WS2812的控制,是一件非常有意思的事情,了解其原理也是一件非常值得学习的事情。
在 NUCLEO-U5A5ZJ-Q 开发板 上使用WS2812,也是一件非常简单方便的事情。
如果感兴趣的话,还可以和音乐播放等结合起来,达到更好的效果。
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
|