Merge pull request #119 from suda-morris/feature/led_strip_clock_src

led_strip: support dma feature and various clock source
This commit is contained in:
Tomas Rezucha 2023-01-04 08:24:55 +01:00 committed by GitHub
commit 8b76019808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 133 additions and 44 deletions

14
led_strip/CHANGELOG.md Normal file
View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -7,16 +7,12 @@
#include <stdint.h>
#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

View File

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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 */
struct {
uint32_t invert_out: 1; /*!< Invert output signal */
} flags;
} led_strip_config_t;
#ifdef __cplusplus
}
#endif

View File

@ -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);
@ -75,12 +77,20 @@ 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,
.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");
@ -89,7 +99,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;