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