[STM32U5] 【NUCLEO-U5A5ZJ-Q测评】驱动DFRobot 24灯珠炫彩灯环带你穿越时空之门

[复制链接]
1458|4
 楼主| HonestQiao 发表于 2023-12-12 00:51 | 显示全部楼层 |阅读模式
本帖最后由 HonestQiao 于 2023-12-12 13:29 编辑

#申请原创#

本篇文章,分享了在 NUCLEO-U5A5ZJ-Q 开发板上,使用WS2812B 炫彩灯环的经验,展示的效果,包括按照顺序亮灭灯环上的灯珠,已经根据简谱点亮对应数量的灯珠。

一、硬件了解
炫彩灯带是一种非常常见的LED灯带,一般有5050灯带、WS28系列灯带(WS2801、WS2812、WS2813、WS2815和WS2818),在日常要使用灯光效果的场合,应用非常的多。
n-ws2812-01.jpg

其中WS2812是最常见的一种,可以编程控制每一颗灯珠的亮灭,以及不同的颜色。

我手头有WS2812的灯板(1、2、4、8*8)、灯带(60、92)、灯环(24),其中24灯珠的灯环最常用:
n-ws2812-03.jpg
我给这个灯环配了一个时空之门的底座,科幻感十足,当灯环上的灯珠亮起来的时候,仿佛开启了时空之门。


这是一款DFRobot出品的灯环,其参数如下:
工作电压:3.3~5V
最大工作电流:<100mA
接口类型:PH2.0-3P
灯珠型号:WS2812
灯珠数量:24
颜色:1600万色

要控制WS2812,除了供电以外,只需要1根数据信号线,即可控制所有的灯珠,非常的方便。
关于WS2812的具体控制原理,可以参考我的文章:【GD32F427开发板试用】点亮WS2812B炫彩灯环,里面详细讲述了灯环的电路原理和灯珠和颜色控制的原理。

在咱们 NUCLEO-U5A5ZJ-Q 开发板 开发板上,可与使用SPI的方式进行控制,将其信号线,连接到开发板的Arduino兼容接口的MOSI即可:
iShot_2023-12-12_00.19.46.png

具体连接如下:
n-ws2812-05.jpg

如果有多个这样的灯环,那灯环直接,还可以串联起来使用:
n-ws2812-06.png


不过,需要注意的是,如果控制的灯珠较多,那么务必需要使用外部供电。
通常情况下,1颗灯珠,1种颜色,需要20mA电流,白色需要60mA电流,控制100颗,就需要最大6A电流。

二、驱动适配
在Zephyr中,提供了ws2812B的处理,但是没有提供咱们NUCLEO-U5A5ZJ-Q 开发板对应的处理定义,需要自己处理适配一下。
文件1:samples/drivers/led_ws2812/boards/nucleo_u5a5zj_q.conf
  1. CONFIG_WS2812_STRIP_SPI=y
  2. CONFIG_SPI=y
这个文件定义了使用SPI,以及使用SPI控制WS2812。

文件2:samples/drivers/led_ws2812/boards/nucleo_u5a5zj_q.overlay:
  1. /*
  2. * Copyright (c) 2022, STMicroelectronics
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */

  6. #include <zephyr/dt-bindings/led/led.h>

  7. &arduino_spi {
  8.         led_strip: ws2812[url=home.php?mod=space&uid=2514928]@0[/url] {
  9.                 compatible = "worldsemi,ws2812-spi";

  10.                 /* SPI */
  11.                 reg = <0>; /* ignored, but necessary for SPI bindings */
  12.                 spi-max-frequency = <DT_FREQ_M(4)>;
  13.                 frame-format = <32768>; /* SPI_FRAME_FORMAT_TI */

  14.                 /* WS2812 */
  15.                 chain-length = <24>; /* arbitrary; change at will */
  16.                 spi-one-frame = <0x70>;
  17.                 spi-zero-frame = <0x40>;
  18.                 color-mapping = <LED_COLOR_ID_GREEN
  19.                                  LED_COLOR_ID_RED
  20.                                  LED_COLOR_ID_BLUE>;
  21.         };
  22. };

  23. / {
  24.         aliases {
  25.                 led-strip = &led_strip;
  26.         };
  27. };

这个文件定义了基于arduio_spi的led-strip设备,使用4MHz通信速率,灯珠数量24颗,颜色对应关系为GRB。
对,就是GRB,WS2812就是这样定义颜色的:
n-ws2812-07.png

三、基础测试
在Zephyr中,提供了ws2812的基础实例:samples/drivers/led_ws2812
其中main.c内容如下:
  1. /*
  2. * Copyright (c) 2017 Linaro Limited
  3. * Copyright (c) 2018 Intel Corporation
  4. *
  5. * SPDX-License-Identifier: Apache-2.0
  6. */

  7. #include <errno.h>
  8. #include <string.h>

  9. #define LOG_LEVEL 4
  10. #include <zephyr/logging/log.h>
  11. LOG_MODULE_REGISTER(main);

  12. #include <zephyr/kernel.h>
  13. #include <zephyr/drivers/led_strip.h>
  14. #include <zephyr/device.h>
  15. #include <zephyr/drivers/spi.h>
  16. #include <zephyr/sys/util.h>

  17. #define STRIP_NODE                DT_ALIAS(led_strip)
  18. #define STRIP_NUM_PIXELS        DT_PROP(DT_ALIAS(led_strip), chain_length)

  19. #define DELAY_TIME K_MSEC(CONFIG_SAMPLE_LED_UPDATE_DELAY)

  20. #define RGB(_r, _g, _b) { .r = (_r), .g = (_g), .b = (_b) }

  21. static const struct led_rgb colors[] = {
  22.         RGB(0x0f, 0x00, 0x00), /* red */
  23.         RGB(0x00, 0x0f, 0x00), /* green */
  24.         RGB(0x00, 0x00, 0x0f), /* blue */
  25. };

  26. struct led_rgb pixels[STRIP_NUM_PIXELS];

  27. static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE);

  28. int main(void)
  29. {
  30.         size_t cursor = 0, color = 0;
  31.         int rc;

  32.         if (device_is_ready(strip)) {
  33.                 LOG_INF("Found LED strip device %s", strip->name);
  34.         } else {
  35.                 LOG_ERR("LED strip device %s is not ready", strip->name);
  36.                 return 0;
  37.         }

  38.         LOG_INF("Displaying pattern on strip");
  39.         while (1) {
  40.                 memset(&pixels, 0x00, sizeof(pixels));
  41.                 memcpy(&pixels[cursor], &colors[color], sizeof(struct led_rgb));
  42.                 rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);

  43.                 if (rc) {
  44.                         LOG_ERR("couldn't update strip: %d", rc);
  45.                 }

  46.                 cursor++;
  47.                 if (cursor >= STRIP_NUM_PIXELS) {
  48.                         cursor = 0;
  49.                         color++;
  50.                         if (color == ARRAY_SIZE(colors)) {
  51.                                 color = 0;
  52.                         }
  53.                 }

  54.                 k_sleep(DELAY_TIME);
  55.         }

  56.         return 0;
  57. }


这个代码的逻辑如下:
1. 定义了一个 colors ,用于灯珠显示的颜色定义
2. 定义了一个 pixels ,用于存放每颗灯珠的颜色数据
3. while循环过程中,cursor用于指向下一颗要点亮的灯珠,如果打倒了最大值,则从第一颗重新点亮
4. while循环过程中,没循环一遍灯珠,就指向下一个定义的颜色,如果颜色循环完了,则从头开始。
5. 使用 led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS) 来更新灯珠的颜色,也就是通过SPI发送数据到WS2812。

编译下载后,具体的运行效果如下:
v1.gif

四、根据简谱控制灯珠点亮
在上面的基础测试中,是依次点亮灯珠。
下面,就根据《小星星》的简谱,来实现根据简谱点亮对应的灯珠。
首先,在代码中,添加一个颜色定义:
  1. static const struct led_rgb num_colors[] = {
  2.         RGB(0x00, 0x00, 0x00), /* black */
  3.         RGB(0x0f, 0x00, 0x00), /* red */
  4.         RGB(0x00, 0x0f, 0x00), /* green */
  5.         RGB(0x00, 0x00, 0x0f), /* blue */

  6.         RGB(0x0f, 0x0f, 0x00), /* ?? */
  7.         RGB(0x00, 0x0f, 0x0f), /* ?? */
  8.         RGB(0x0f, 0x00, 0x0f), /* ?? */
  9.         RGB(0x0f, 0x0f, 0x0f), /* white */
  10. };
实际的颜色,大家可以对照RGB颜色表来取值。

再添加一个曲谱的定义:
iShot_2023-12-12_00.43.23.png
  1. 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循环替换为:
  1.         while(1) {
  2.                 memset(&pixels, 0x00, sizeof(pixels));
  3.                 for(int i=0;i<nums[cursor];i++) {
  4.                         memcpy(&pixels[i], &num_colors[nums[cursor]], sizeof(struct led_rgb));
  5.                 }
  6.                 rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);

  7.                 if (rc) {
  8.                         LOG_ERR("couldn't update strip: %d", rc);
  9.                 }

  10.                 cursor++;
  11.                 if (cursor >= sizeof(nums)/sizeof(nums[0])) {
  12.                         cursor = 0;
  13.                 }

  14.                 k_sleep(K_MSEC(500));
  15.         }


上述代码,就是通过循环简谱的每一位,来确定要点亮的灯珠数量,控制pixels 中灯珠的颜色。具体的颜色,再根据要控制的数量,从 num_colors 中取值。

编译下载后,运行效果如下:
v2.gif

五、总结
WS2812的控制,是一件非常有意思的事情,了解其原理也是一件非常值得学习的事情。
在 NUCLEO-U5A5ZJ-Q 开发板 上使用WS2812,也是一件非常简单方便的事情。
如果感兴趣的话,还可以和音乐播放等结合起来,达到更好的效果。
OliviaSH 发表于 2023-12-13 10:50 来自手机 | 显示全部楼层
颜色对应谱子,这想法不错
SophiaOP 发表于 2023-12-13 11:30 | 显示全部楼层
WS2812最多可以串联多少啊?
jobszheng 发表于 2023-12-14 06:35 | 显示全部楼层

居然使用了Zephyr操作系统!
看楼主的展示代码,在Zephyr下操作灯珠几行代码就生成了如此酪炫的效果!

 楼主| HonestQiao 发表于 2023-12-15 17:16 | 显示全部楼层
Zephyr系统相当的好用啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

115

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部