- /*
 
-  * 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 */
 
- };
再添加一个曲谱的定义:
 
- 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,也是一件非常简单方便的事情。
如果感兴趣的话,还可以和音乐播放等结合起来,达到更好的效果。