From 9ad87cdff0739d27ba6bd9f05faf74edc7d37023 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Thu, 22 Dec 2022 13:38:07 +0800 Subject: [PATCH 1/4] led_strip: support dma feature and various clock source --- led_strip/README.md | 34 ++++++++++++++++++---- led_strip/idf_component.yml | 2 +- led_strip/include/led_strip.h | 35 +--------------------- led_strip/include/led_strip_rmt.h | 45 +++++++++++++++++++++++++++++ led_strip/include/led_strip_types.h | 29 +++++++++++++++++++ led_strip/src/led_strip_rmt_dev.c | 9 +++++- 6 files changed, 112 insertions(+), 42 deletions(-) create mode 100644 led_strip/include/led_strip_rmt.h create mode 100644 led_strip/include/led_strip_types.h diff --git a/led_strip/README.md b/led_strip/README.md index 785ee63..892419e 100644 --- a/led_strip/README.md +++ b/led_strip/README.md @@ -1,12 +1,34 @@ -# LED Strip Component +# LED Strip Driver [![Component Registry](https://components.espressif.com/components/espressif/led_strip/badge.svg)](https://components.espressif.com/components/espressif/led_strip) -This directory contains an implementation for addressable LEDs by different peripherals. Currently only RMT is supported as the led strip backend. +This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line. -The driver should be compatible with: +## Backend Controllers -* [WS2812](http://www.world-semi.com/Certifications/WS2812B.html) -* SK68XX +### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral -To learn more about how to use this component, please check API Documentation from header file [led_strip.h](./include/led_strip.h). +This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible. [^1] + +#### Allocate LED Strip Object with RMT Backend + +```c +#define BLINK_GPIO 0 + +led_strip_handle_t led_strip; + +/* LED strip initialization with the GPIO and pixels number*/ +led_strip_config_t strip_config = { + .strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line + .max_leds = 1, // The number of LEDs in the strip +}; + +led_strip_rmt_config_t rmt_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption + .resolution_hz = 10 * 1000 * 1000, // 10MHz + .flags.with_dma = false, // wether to enable the DMA feature +}; +ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); +``` + +[^1]: The DMA feature is not available on all ESP chips. Please check the data sheet before using it. diff --git a/led_strip/idf_component.yml b/led_strip/idf_component.yml index 1b9bc8f..6bc7a3c 100644 --- a/led_strip/idf_component.yml +++ b/led_strip/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.0" +version: "2.1.0" description: Driver for Addressable LED Strip (WS2812, etc) url: https://github.com/espressif/idf-extra-components/tree/master/led_strip dependencies: diff --git a/led_strip/include/led_strip.h b/led_strip/include/led_strip.h index c119429..f4572be 100644 --- a/led_strip/include/led_strip.h +++ b/led_strip/include/led_strip.h @@ -7,16 +7,12 @@ #include #include "esp_err.h" +#include "led_strip_rmt.h" #ifdef __cplusplus extern "C" { #endif -/** - * @brief LED strip handle - */ -typedef struct led_strip_t *led_strip_handle_t; - /** * @brief Set RGB for a specific pixel * @@ -69,35 +65,6 @@ esp_err_t led_strip_clear(led_strip_handle_t strip); */ esp_err_t led_strip_del(led_strip_handle_t strip); -/** - * @brief LED Strip Configuration - */ -typedef struct { - uint32_t strip_gpio_num; /*!< GPIO number that used by LED strip */ - uint32_t max_leds; /*!< Maximum LEDs in a single strip */ -} led_strip_config_t; - -/** - * @brief LED Strip RMT specific configuration - */ -typedef struct { - uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */ -} led_strip_rmt_config_t; - -/** - * @brief Create LED strip based on RMT TX channel - * - * @param led_config LED strip configuration - * @param rmt_config RMT specific configuration - * @param ret_strip Returned LED strip handle - * @return - * - ESP_OK: create LED strip handle successfully - * - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument - * - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory - * - ESP_FAIL: create LED strip handle failed because some other error - */ -esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip); - #ifdef __cplusplus } #endif diff --git a/led_strip/include/led_strip_rmt.h b/led_strip/include/led_strip_rmt.h new file mode 100644 index 0000000..d7a18fd --- /dev/null +++ b/led_strip/include/led_strip_rmt.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "driver/rmt_types.h" +#include "led_strip_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief LED Strip RMT specific configuration + */ +typedef struct { + rmt_clock_source_t clk_src; /*!< RMT clock source */ + uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */ + struct { + uint32_t with_dma: 1; /*!< Use DMA to transmit data */ + } flags; +} led_strip_rmt_config_t; + +/** + * @brief Create LED strip based on RMT TX channel + * + * @param led_config LED strip configuration + * @param rmt_config RMT specific configuration + * @param ret_strip Returned LED strip handle + * @return + * - ESP_OK: create LED strip handle successfully + * - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument + * - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory + * - ESP_FAIL: create LED strip handle failed because some other error + */ +esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip); + +#ifdef __cplusplus +} +#endif diff --git a/led_strip/include/led_strip_types.h b/led_strip/include/led_strip_types.h new file mode 100644 index 0000000..ad49c4b --- /dev/null +++ b/led_strip/include/led_strip_types.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LED strip handle + */ +typedef struct led_strip_t *led_strip_handle_t; + +/** + * @brief LED Strip Configuration + */ +typedef struct { + uint32_t strip_gpio_num; /*!< GPIO number that used by LED strip */ + uint32_t max_leds; /*!< Maximum LEDs in a single strip */ +} led_strip_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/led_strip/src/led_strip_rmt_dev.c b/led_strip/src/led_strip_rmt_dev.c index d1b80b9..5634923 100644 --- a/led_strip/src/led_strip_rmt_dev.c +++ b/led_strip/src/led_strip_rmt_dev.c @@ -75,12 +75,19 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * 3); ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip"); uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION; + + // for backward compatibility, if the user does not set the clk_src, use the default value + rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT; + if (rmt_config->clk_src) { + clk_src = rmt_config->clk_src; + } rmt_tx_channel_config_t rmt_chan_config = { - .clk_src = RMT_CLK_SRC_DEFAULT, + .clk_src = clk_src, .gpio_num = led_config->strip_gpio_num, .mem_block_symbols = 64, .resolution_hz = resolution, .trans_queue_depth = 4, + .flags.with_dma = rmt_config->flags.with_dma, }; ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed"); From 631cdb2a04067b3fe18ea50ba5a1c112e3b8681a Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Thu, 29 Dec 2022 11:37:02 +0800 Subject: [PATCH 2/4] led_strip: acquire and release the pm lock before and after the refresh --- led_strip/src/led_strip_rmt_dev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/led_strip/src/led_strip_rmt_dev.c b/led_strip/src/led_strip_rmt_dev.c index 5634923..cd88419 100644 --- a/led_strip/src/led_strip_rmt_dev.c +++ b/led_strip/src/led_strip_rmt_dev.c @@ -43,9 +43,12 @@ static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) rmt_transmit_config_t tx_conf = { .loop_count = 0, }; + + ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed"); ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf, rmt_strip->strip_len * 3, &tx_conf), TAG, "transmit pixels by RMT failed"); ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed"); + ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed"); return ESP_OK; } @@ -60,7 +63,6 @@ static esp_err_t led_strip_rmt_clear(led_strip_t *strip) static esp_err_t led_strip_rmt_del(led_strip_t *strip) { led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); - ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed"); ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed"); ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed"); free(rmt_strip); @@ -96,7 +98,6 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l }; ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed"); - ESP_GOTO_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), err, TAG, "enable RMT channel failed"); rmt_strip->strip_len = led_config->max_leds; rmt_strip->base.set_pixel = led_strip_rmt_set_pixel; From 7d48a92734bc937d1299dfce4aa111d415e2fb6d Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Tue, 3 Jan 2023 10:51:21 +0800 Subject: [PATCH 3/4] led_strip: support invert output --- led_strip/include/led_strip_types.h | 3 +++ led_strip/src/led_strip_rmt_dev.c | 1 + 2 files changed, 4 insertions(+) diff --git a/led_strip/include/led_strip_types.h b/led_strip/include/led_strip_types.h index ad49c4b..cf507c9 100644 --- a/led_strip/include/led_strip_types.h +++ b/led_strip/include/led_strip_types.h @@ -22,6 +22,9 @@ typedef struct led_strip_t *led_strip_handle_t; typedef struct { uint32_t strip_gpio_num; /*!< GPIO number that used by LED strip */ uint32_t max_leds; /*!< Maximum LEDs in a single strip */ + struct { + uint32_t invert_out: 1; /*!< Invert output signal */ + } flags; } led_strip_config_t; #ifdef __cplusplus diff --git a/led_strip/src/led_strip_rmt_dev.c b/led_strip/src/led_strip_rmt_dev.c index cd88419..61de29b 100644 --- a/led_strip/src/led_strip_rmt_dev.c +++ b/led_strip/src/led_strip_rmt_dev.c @@ -90,6 +90,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l .resolution_hz = resolution, .trans_queue_depth = 4, .flags.with_dma = rmt_config->flags.with_dma, + .flags.invert_out = led_config->flags.invert_out, }; ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed"); From 2e9e31e8cbfdd15b3dd94aed1d98a521413223f1 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Tue, 3 Jan 2023 10:39:51 +0800 Subject: [PATCH 4/4] led_strip: added change log --- led_strip/CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 led_strip/CHANGELOG.md diff --git a/led_strip/CHANGELOG.md b/led_strip/CHANGELOG.md new file mode 100644 index 0000000..531f0d3 --- /dev/null +++ b/led_strip/CHANGELOG.md @@ -0,0 +1,14 @@ +## 1.0.0 + +- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`) + +## 2.0.0 + +- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`) + +## 2.1.0 + +- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs +- Support various RMT clock sources +- Acquire and release the power management lock before and after each refresh +- New driver flag: `invert_out` which can invert the led control signal by hardware