diff --git a/.build-test-rules.yml b/.build-test-rules.yml index bd98e5dd4d..57bc7cc82f 100644 --- a/.build-test-rules.yml +++ b/.build-test-rules.yml @@ -11,6 +11,11 @@ led_strip/examples/led_strip_spi_ws2812: - if: CONFIG_SOC_GPSPI_SUPPORTED != 1 reason: Relevant only for SPI enabled targets +led_strip/examples/led_strip_parlio_ws2812: + disable: + - if: CONFIG_SOC_PARLIO_SUPPORTED != 1 + reason: Relevant only for PARLIO enabled targets + esp_delta_ota: enable: - if: IDF_VERSION_MAJOR > 4 diff --git a/led_strip/CHANGELOG.md b/led_strip/CHANGELOG.md index 4b57dce2f8..fc636c9d6a 100644 --- a/led_strip/CHANGELOG.md +++ b/led_strip/CHANGELOG.md @@ -1,7 +1,17 @@ +## 3.1.0 + +- Support for led strip group based on the Parallel IO backend + - new interface type led_strip_group_handle + - new API led_strip_group_get_strip_handle + - new API led_strip_group_del +- Add async refresh API, the next frame is free to be computed while the current frame is being transmitted. +- Support to use custom timing config in RMT backend +- Support to switch GPIO at runtime in RMT backend + ## 3.0.1 - Support WS2811 bit timing - + ## 3.0.0 - Discontinued support for ESP-IDF v4.x diff --git a/led_strip/CMakeLists.txt b/led_strip/CMakeLists.txt index 9e82ccaceb..6a5985df34 100644 --- a/led_strip/CMakeLists.txt +++ b/led_strip/CMakeLists.txt @@ -7,6 +7,10 @@ if(CONFIG_SOC_RMT_SUPPORTED) list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c") endif() +if(CONFIG_SOC_PARLIO_SUPPORTED) + list(APPEND srcs "src/led_strip_parlio_dev.c") +endif() + # the SPI backend driver relies on some feature that was available in IDF 5.1 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1") if(CONFIG_SOC_GPSPI_SUPPORTED) @@ -16,7 +20,7 @@ endif() # Starting from esp-idf v5.3, the RMT and SPI drivers are moved to separate components if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3") - list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi") + list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi" "esp_driver_parlio") else() list(APPEND public_requires "driver") endif() diff --git a/led_strip/README.md b/led_strip/README.md index ca3cac20e9..4dbd442731 100644 --- a/led_strip/README.md +++ b/led_strip/README.md @@ -81,6 +81,58 @@ ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip) The number of LED strip objects can be created depends on how many free SPI buses are free to use in your project. +### The [Parallel IO](https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/parlio.html) Peripheral + +Parallel IO peripheral can also be used to generate the timing required by the LED strip. Since the Parallel IO peripheral is a parallel interface, we manage it through groups. The number of LED strips supported in a strip group depends on the maximum data width of the Parallel IO tx_unit. +The strip_handle obtained through **get_strip_handle** API is consistent with the handles used in RMT backend and SPI backend. However, it cannot be deleted individually. Call **group_del** API to delete the entire group. + +Please note, the Parallel IO backend has a dependency of **ESP-IDF >= 5.1** + +#### Allocate LED Strip Object with Parallel IO Backend + +```c +#define LED_STRIP_COUNT 4 +#define LED_STRIP0_GPIO_PIN 0 +#define LED_STRIP1_GPIO_PIN 1 +#define LED_STRIP2_GPIO_PIN 2 +#define LED_STRIP3_GPIO_PIN 3 + +/// LED strip common configuration +led_strip_config_t strip_config = { + .max_leds = 1, // The number of LEDs in the strip, + .led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing + .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B + .flags = { + .invert_out = false, // don't invert the output signal + } +}; + +/// Parallel IO backend specific configuration +led_strip_parlio_config_t parlio_config = { + .clk_src = PARLIO_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption + .strip_count = LED_STRIP_COUNT, + .strip_gpio_num = { + LED_STRIP0_GPIO_PIN, + LED_STRIP1_GPIO_PIN, + LED_STRIP2_GPIO_PIN, + LED_STRIP3_GPIO_PIN, + ..., + }, +}; + +/// Create the LED strip group object +led_strip_group_handle_t parlio_group; +ESP_ERROR_CHECK(led_strip_new_parlio_group(&strip_config, &parlio_config, &parlio_group)); + +/// get the LED strip object +led_strip_handle_t *led_strip = calloc(LED_STRIP_COUNT, sizeof(led_strip_handle_t)); +for(int i = 0; i < LED_STRIP_COUNT; i++) { + ESP_ERROR_CHECK(led_strip_group_get_strip_handle(parlio_group, i, &led_strip[i])); +} +``` + +The number of LED strip group objects can be created depends on how many free Parallel IO TX unit are free to use in your project. + ## FAQ * Which led_strip backend should I choose? diff --git a/led_strip/api.md b/led_strip/api.md index abe7f76763..f001172cdb 100644 --- a/led_strip/api.md +++ b/led_strip/api.md @@ -3,6 +3,7 @@ ## Header files - [include/led_strip.h](#file-includeled_striph) +- [include/led_strip_parlio.h](#file-includeled_strip_parlioh) - [include/led_strip_rmt.h](#file-includeled_strip_rmth) - [include/led_strip_spi.h](#file-includeled_strip_spih) - [include/led_strip_types.h](#file-includeled_strip_typesh) @@ -16,10 +17,15 @@ | ---: | :--- | | esp\_err\_t | [**led\_strip\_clear**](#function-led_strip_clear) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip)
_Clear LED strip (turn off all LEDs)_ | | esp\_err\_t | [**led\_strip\_del**](#function-led_strip_del) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip)
_Free LED strip resources._ | +| esp\_err\_t | [**led\_strip\_group\_del**](#function-led_strip_group_del) ([**led\_strip\_group\_handle\_t**](#typedef-led_strip_group_handle_t) group)
_Delete the LED strip group._ | +| esp\_err\_t | [**led\_strip\_group\_get\_strip\_handle**](#function-led_strip_group_get_strip_handle) ([**led\_strip\_group\_handle\_t**](#typedef-led_strip_group_handle_t) group, uint8\_t index, [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) \*ret\_strip)
_Get the handle of the LED strip._ | | esp\_err\_t | [**led\_strip\_refresh**](#function-led_strip_refresh) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip)
_Refresh memory colors to LEDs._ | +| esp\_err\_t | [**led\_strip\_refresh\_async**](#function-led_strip_refresh_async) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip)
_Refresh memory colors to LEDs asynchronously._ | +| esp\_err\_t | [**led\_strip\_refresh\_wait\_async\_done**](#function-led_strip_refresh_wait_async_done) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip)
_Wait for the async refresh to complete._ | | esp\_err\_t | [**led\_strip\_set\_pixel**](#function-led_strip_set_pixel) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue)
_Set RGB for a specific pixel._ | | esp\_err\_t | [**led\_strip\_set\_pixel\_hsv**](#function-led_strip_set_pixel_hsv) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint16\_t hue, uint8\_t saturation, uint8\_t value)
_Set HSV for a specific pixel._ | | esp\_err\_t | [**led\_strip\_set\_pixel\_rgbw**](#function-led_strip_set_pixel_rgbw) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue, uint32\_t white)
_Set RGBW for a specific pixel._ | +| esp\_err\_t | [**led\_strip\_switch\_gpio**](#function-led_strip_switch_gpio) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, gpio\_num\_t new\_gpio\_num, bool invert\_output)
_Switch GPIO of LED strip._ | ## Functions Documentation @@ -61,6 +67,48 @@ esp_err_t led_strip_del ( - ESP\_OK: Free resources successfully - ESP\_FAIL: Free resources failed because error occurred +### function `led_strip_group_del` + +_Delete the LED strip group._ + +```c +esp_err_t led_strip_group_del ( + led_strip_group_handle_t group +) +``` + +**Parameters:** + +- `group` Handle of the LED strip group + +**Returns:** + +- ESP\_OK: Delete the LED strip group successfully +- ESP\_ERR\_INVALID\_ARG: Invalid argument + +### function `led_strip_group_get_strip_handle` + +_Get the handle of the LED strip._ + +```c +esp_err_t led_strip_group_get_strip_handle ( + led_strip_group_handle_t group, + uint8_t index, + led_strip_handle_t *ret_strip +) +``` + +**Parameters:** + +- `group` LED strip group handle +- `index` Index of the LED strip in the group +- `ret_strip` Pointer to store the handle of the LED strip + +**Returns:** + +- ESP\_OK: Get the handle of the LED strip successfully +- ESP\_ERR\_INVALID\_ARG: Invalid argument + ### function `led_strip_refresh` _Refresh memory colors to LEDs._ @@ -82,7 +130,48 @@ esp_err_t led_strip_refresh ( **Note:** -: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. +After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. + +### function `led_strip_refresh_async` + +_Refresh memory colors to LEDs asynchronously._ + +```c +esp_err_t led_strip_refresh_async ( + led_strip_handle_t strip +) +``` + +**Parameters:** + +- `strip` LED strip + +**Returns:** + +- ESP\_OK: Refresh successfully +- ESP\_FAIL: Refresh failed because some other error occurred + +**Note:** + +This function is non-blocking, so you need to call `led_strip_refresh_wait_async_done` to wait for the refresh to complete before modifying the LED colors again. + +### function `led_strip_refresh_wait_async_done` + +_Wait for the async refresh to complete._ + +```c +esp_err_t led_strip_refresh_wait_async_done ( + led_strip_handle_t strip +) +``` + +**Parameters:** + +- `strip` LED strip + +**Returns:** + +- ESP\_OK: Wait for the async refresh to complete successfully ### function `led_strip_set_pixel` @@ -178,6 +267,92 @@ Also see `led_strip_set_pixel` if you only want to specify the RGB part of the c - ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument - ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred +### function `led_strip_switch_gpio` + +_Switch GPIO of LED strip._ + +```c +esp_err_t led_strip_switch_gpio ( + led_strip_handle_t strip, + gpio_num_t new_gpio_num, + bool invert_output +) +``` + +**Parameters:** + +- `strip` LED strip +- `new_gpio_num` new GPIO number +- `invert_output` invert output + +**Note:** + +Only support RMT backend now + +**Returns:** + +- ESP\_OK: Switch GPIO successfully +- ESP\_FAIL: Switch GPIO failed because some other error occurred + +## File include/led_strip_parlio.h + +## Structures and Types + +| Type | Name | +| ---: | :--- | +| struct | [**led\_strip\_parlio\_config\_t**](#struct-led_strip_parlio_config_t)
_LED Strip PARLIO specific configuration._ | + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_err\_t | [**led\_strip\_new\_parlio\_group**](#function-led_strip_new_parlio_group) (const [**led\_strip\_config\_t**](#struct-led_strip_config_t) \*led\_config, const [**led\_strip\_parlio\_config\_t**](#struct-led_strip_parlio_config_t) \*parlio\_config, [**led\_strip\_group\_handle\_t**](#typedef-led_strip_group_handle_t) \*ret\_group)
_Create LED strip group based on PARLIO\_TX unit._ | + +## Structures and Types Documentation + +### struct `led_strip_parlio_config_t` + +_LED Strip PARLIO specific configuration._ + +Variables: + +- parlio\_clock\_source\_t clk_src
PARLIO clock source + +- uint8\_t strip_count
Number of LED strips. Should be a power of 2 and not larger than PARLIO\_TX\_UNIT\_MAX\_DATA\_WIDTH + +- gpio\_num\_t strip_gpio_num
GPIO number that used by LED strip + +## Functions Documentation + +### function `led_strip_new_parlio_group` + +_Create LED strip group based on PARLIO\_TX unit._ + +```c +esp_err_t led_strip_new_parlio_group ( + const led_strip_config_t *led_config, + const led_strip_parlio_config_t *parlio_config, + led_strip_group_handle_t *ret_group +) +``` + +**Note:** + +The strip\_gpio\_num in led\_config no longer takes effect, and other configurations will be shared by all LED strips in the group. + +**Parameters:** + +- `led_config` LED strip configuration +- `parlio_config` PARLIO specific configuration +- `ret_group` Returned LED strip group handle + +**Returns:** + +- ESP\_OK: create LED strip handle successfully +- ESP\_ERR\_INVALID\_ARG: create LED strip handle failed because of invalid argument +- ESP\_ERR\_NOT\_SUPPORTED: create LED strip handle failed because of unsupported configuration +- ESP\_ERR\_NO\_MEM: create LED strip handle failed because of out of memory + ## File include/led_strip_rmt.h ## Structures and Types @@ -249,6 +424,7 @@ esp_err_t led_strip_new_rmt_device ( | Type | Name | | ---: | :--- | | struct | [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t)
_LED Strip SPI specific configuration._ | +| struct | [**led\_strip\_spi\_extra\_config**](#struct-led_strip_spi_config_tled_strip_spi_extra_config)
| ## Functions @@ -266,10 +442,14 @@ Variables: - spi\_clock\_source\_t clk_src
SPI clock source -- struct [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t) flags
Extra driver flags +- struct [**led\_strip\_spi\_config\_t::led\_strip\_spi\_extra\_config**](#struct-led_strip_spi_config_tled_strip_spi_extra_config) flags
Extra driver flags - spi\_host\_device\_t spi_bus
SPI bus ID. Which buses are available depends on the specific chip +### struct `led_strip_spi_config_t::led_strip_spi_extra_config` + +Variables: + - uint32\_t with_dma
Use DMA to transmit data ## Functions Documentation @@ -315,6 +495,8 @@ Although only the MOSI line is used for generating the signal, the whole SPI bus | enum | [**led\_model\_t**](#enum-led_model_t)
_LED strip model._ | | struct | [**led\_strip\_config\_t**](#struct-led_strip_config_t)
_LED Strip common configurations The common configurations are not specific to any backend peripheral._ | | struct | [**led\_strip\_extra\_flags**](#struct-led_strip_config_tled_strip_extra_flags)
| +| struct | [**led\_strip\_encoder\_timings\_t**](#struct-led_strip_encoder_timings_t)
_LED strip encoder timings._ | +| typedef struct [**led\_strip\_group\_t**](#struct-led_strip_group_t) \* | [**led\_strip\_group\_handle\_t**](#typedef-led_strip_group_handle_t)
_Type of LED strip group handle._ | | typedef struct [**led\_strip\_t**](#struct-led_strip_t) \* | [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t)
_Type of LED strip handle._ | ## Macros @@ -366,6 +548,8 @@ _LED strip model._ enum led_model_t { LED_MODEL_WS2812, LED_MODEL_SK6812, + LED_MODEL_WS2811, + LED_MODEL_CUSTOM, LED_MODEL_INVALID }; ``` @@ -380,7 +564,7 @@ _LED Strip common configurations The common configurations are not specific to a Variables: -- [**led\_color\_component\_format\_t**](#union-led_color_component_format_t) color_component_format
Specifies the order of color components in each pixel. Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format LED strip extra driver flags +- [**led\_color\_component\_format\_t**](#union-led_color_component_format_t) color_component_format
Specifies the order of color components in each pixel. Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format - struct [**led\_strip\_config\_t::led\_strip\_extra\_flags**](#struct-led_strip_config_tled_strip_extra_flags) flags
Extra driver flags @@ -390,12 +574,42 @@ Variables: - int strip_gpio_num
GPIO number that used by LED strip +- [**led\_strip\_encoder\_timings\_t**](#struct-led_strip_encoder_timings_t) timings
Encoder timings, only valid for RMT backend LED strip extra driver flags + ### struct `led_strip_config_t::led_strip_extra_flags` Variables: - uint32\_t invert_out
Invert output signal +### struct `led_strip_encoder_timings_t` + +_LED strip encoder timings._ + +**Note:** + +The logic timings are in nanoseconds and the reset timings is in microseconds. + +Variables: + +- uint32\_t reset
Reset time, microseconds + +- uint32\_t t0h
High time for 0 bit, nanoseconds + +- uint32\_t t0l
Low time for 0 bit, nanoseconds + +- uint32\_t t1h
High time for 1 bit, nanoseconds + +- uint32\_t t1l
Low time for 1 bit, nanoseconds + +### typedef `led_strip_group_handle_t` + +_Type of LED strip group handle._ + +```c +typedef struct led_strip_group_t* led_strip_group_handle_t; +``` + ### typedef `led_strip_handle_t` _Type of LED strip handle._ @@ -438,11 +652,47 @@ _Helper macros to set the color component format._ | Type | Name | | ---: | :--- | +| struct | [**led\_strip\_group\_t**](#struct-led_strip_group_t)
_LED strip group interface definition._ | +| typedef struct [**led\_strip\_group\_t**](#struct-led_strip_group_t) | [**led\_strip\_group\_t**](#typedef-led_strip_group_t)
| | struct | [**led\_strip\_t**](#struct-led_strip_t)
_LED strip interface definition._ | -| typedef struct led\_strip\_t | [**led\_strip\_t**](#typedef-led_strip_t)
| +| typedef struct [**led\_strip\_t**](#struct-led_strip_t) | [**led\_strip\_t**](#typedef-led_strip_t)
| ## Structures and Types Documentation +### struct `led_strip_group_t` + +_LED strip group interface definition._ + +Variables: + +- esp\_err\_t(\* del
_Free LED strip group resources._
**Parameters:** + +- `group` LED strip group + +**Returns:** + +- ESP\_OK: Free resources successfully +- ESP\_FAIL: Free resources failed because error occurred + +- esp\_err\_t(\* get_strip_handle
_Get LED strip handle by index._
**Parameters:** + +- `group` LED strip group +- `index` LED strip index +- `ret_strip` Returned LED strip handle + +**Returns:** + +- ESP\_OK: Success +- ESP\_ERR\_INVALID\_ARG: Invalid argument + +### typedef `led_strip_group_t` + +```c +typedef struct led_strip_group_t led_strip_group_t; +``` + +Type of LED group strip + ### struct `led_strip_t` _LED strip interface definition._ @@ -452,7 +702,6 @@ Variables: - esp\_err\_t(\* clear
_Clear LED strip (turn off all LEDs)_
**Parameters:** - `strip` LED strip -- `timeout_ms` timeout value for clearing task **Returns:** @@ -471,7 +720,6 @@ Variables: - esp\_err\_t(\* refresh
_Refresh memory colors to LEDs._
**Parameters:** - `strip` LED strip -- `timeout_ms` timeout value for refreshing task **Returns:** @@ -480,7 +728,28 @@ Variables: **Note:** -: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. +After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. + +- esp\_err\_t(\* refresh_async
_Refresh memory colors to LEDs asynchronously._
**Parameters:** + +- `strip` LED strip + +**Returns:** + +- ESP\_OK: Refresh successfully +- ESP\_FAIL: Refresh failed because some other error occurred + +**Note:** + +This function is non-blocking, so you need to call `led_strip_refresh_wait_async_done` to wait for the refresh to complete before modifying the LED colors again. + +- esp\_err\_t(\* refresh_wait_async_done
_Wait for the async refresh to complete._
**Parameters:** + +- `strip` LED strip + +**Returns:** + +- ESP\_OK: Wait for the async refresh to complete successfully - esp\_err\_t(\* set_pixel
_Set RGB for a specific pixel._
**Parameters:** @@ -511,6 +780,21 @@ Variables: - ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument - ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred +- esp\_err\_t(\* switch_gpio
_Switch GPIO of LED strip._
**Parameters:** + +- `strip` LED strip +- `new_gpio_num` new GPIO number +- `invert_output` invert output + +**Note:** + +Only support RMT backend now + +**Returns:** + +- ESP\_OK: Switch GPIO successfully +- ESP\_FAIL: Switch GPIO failed because some other error occurred + ### typedef `led_strip_t` ```c diff --git a/led_strip/examples/led_strip_parlio_ws2812/CMakeLists.txt b/led_strip/examples/led_strip_parlio_ws2812/CMakeLists.txt new file mode 100644 index 0000000000..44b969be56 --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.16) + +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(led_strip_parlio_ws2812) diff --git a/led_strip/examples/led_strip_parlio_ws2812/README.md b/led_strip/examples/led_strip_parlio_ws2812/README.md new file mode 100644 index 0000000000..1342a70681 --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/README.md @@ -0,0 +1,32 @@ +# LED Strip Example (Parallel IO backend + WS2812) + +This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component. + +## How to Use Example + +### Hardware Required + +* A development board with Espressif SoC +* A USB cable for Power supply and programming +* WS2812 LED strip + +### Configure the Example + +Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. Then assign the proper GPIO in the [source file](main/led_strip_parlio_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number. + +### Build and Flash + +Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +```text +I (250) example: Created LED strip object with PARLIO backend +I (250) example: Start blinking LED strip +I (260) example: LED OFF! +I (760) example: LED ON! +``` diff --git a/led_strip/examples/led_strip_parlio_ws2812/main/CMakeLists.txt b/led_strip/examples/led_strip_parlio_ws2812/main/CMakeLists.txt new file mode 100644 index 0000000000..04c40d3b3f --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "led_strip_parlio_ws2812_main.c" + INCLUDE_DIRS ".") diff --git a/led_strip/examples/led_strip_parlio_ws2812/main/idf_component.yml b/led_strip/examples/led_strip_parlio_ws2812/main/idf_component.yml new file mode 100644 index 0000000000..ed00d7eb3c --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/led_strip: + version: '^3' + override_path: '../../../' + idf: ">=5.1" diff --git a/led_strip/examples/led_strip_parlio_ws2812/main/led_strip_parlio_ws2812_main.c b/led_strip/examples/led_strip_parlio_ws2812/main/led_strip_parlio_ws2812_main.c new file mode 100644 index 0000000000..abd577252c --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/main/led_strip_parlio_ws2812_main.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "led_strip.h" +#include "esp_log.h" +#include "esp_err.h" + +// GPIO assignment +#define LED_STRIP0_GPIO_PIN 0 +#define LED_STRIP1_GPIO_PIN 1 +#define LED_STRIP2_GPIO_PIN 2 +#define LED_STRIP3_GPIO_PIN 3 + +// Numbers of the LED in the strip +#define LED_STRIP_LED_COUNT 8 +// Numbers of the strip +#define LED_STRIP_COUNT 4 + +static const char *TAG = "example"; + +led_strip_handle_t *configure_led(void) +{ + // LED strip general initialization, according to your led board design + led_strip_config_t strip_config = { + .max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip, + .led_model = LED_MODEL_WS2812, // LED strip model + // set the color order of the strip: GRB + .color_component_format = { + .format = { + .r_pos = 1, // red is the second byte in the color data + .g_pos = 0, // green is the first byte in the color data + .b_pos = 2, // blue is the third byte in the color data + .num_components = 3, // total 3 color components + }, + }, + .flags = { + .invert_out = false, // don't invert the output signal + } + }; + + // LED strip backend configuration: PARLIO + led_strip_parlio_config_t parlio_config = { + .clk_src = PARLIO_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption + .strip_count = LED_STRIP_COUNT, + .strip_gpio_num = { + LED_STRIP0_GPIO_PIN, + LED_STRIP1_GPIO_PIN, + LED_STRIP2_GPIO_PIN, + LED_STRIP3_GPIO_PIN, + }, + }; + + // LED Strip group handle + led_strip_group_handle_t parlio_group; + ESP_ERROR_CHECK(led_strip_new_parlio_group(&strip_config, &parlio_config, &parlio_group)); + led_strip_handle_t *led_strip = calloc(LED_STRIP_COUNT, sizeof(led_strip_handle_t)); + for (int i = 0; i < LED_STRIP_COUNT; i++) { + ESP_ERROR_CHECK(led_strip_group_get_strip_handle(parlio_group, i, &led_strip[i])); + } + + ESP_LOGI(TAG, "Created LED strip object with PARLIO backend"); + return led_strip; +} + +void app_main(void) +{ + led_strip_handle_t *led_strip = configure_led(); + bool led_on_off = false; + + ESP_LOGI(TAG, "Start blinking LED strip"); + while (1) { + if (led_on_off) { + /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */ + for (int i = 0; i < LED_STRIP_COUNT; i++) { + for (int j = 0; j < LED_STRIP_LED_COUNT; j++) { + ESP_ERROR_CHECK(led_strip_set_pixel(led_strip[i], j, 5, 5, 5)); + } + } + + /* Refresh the strip to send data */ + for (int i = 0; i < LED_STRIP_COUNT; i++) { + ESP_ERROR_CHECK(led_strip_refresh(led_strip[i])); + } + ESP_LOGI(TAG, "LED ON!"); + } else { + /* Set all LED off to clear all pixels */ + for (int i = 0; i < LED_STRIP_COUNT; i++) { + ESP_ERROR_CHECK(led_strip_clear(led_strip[i])); + } + ESP_LOGI(TAG, "LED OFF!"); + } + + led_on_off = !led_on_off; + vTaskDelay(pdMS_TO_TICKS(500)); + } +} diff --git a/led_strip/examples/led_strip_parlio_ws2812/pytest_led_strip_parlio_ws2812.py b/led_strip/examples/led_strip_parlio_ws2812/pytest_led_strip_parlio_ws2812.py new file mode 100644 index 0000000000..b7ffa4796d --- /dev/null +++ b/led_strip/examples/led_strip_parlio_ws2812/pytest_led_strip_parlio_ws2812.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_idf.utils import soc_filtered_targets + +@pytest.mark.generic +@idf_parametrize( + 'target', soc_filtered_targets('SOC_PARLIO_SUPPORTED == 1'), indirect=['target'] +) +def test_led_strip_parlio_ws2812(dut: Dut) -> None: + dut.expect_exact('example: Created LED strip object with PARLIO backend') + dut.expect_exact('example: Start blinking LED strip') + dut.expect_exact('example: LED OFF!') + dut.expect_exact('example: LED ON!') + + diff --git a/led_strip/examples/led_strip_rmt_ws2812/pytest_led_strip_rmt_ws2812.py b/led_strip/examples/led_strip_rmt_ws2812/pytest_led_strip_rmt_ws2812.py new file mode 100644 index 0000000000..7b70614fc2 --- /dev/null +++ b/led_strip/examples/led_strip_rmt_ws2812/pytest_led_strip_rmt_ws2812.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_idf.utils import soc_filtered_targets + +@pytest.mark.generic +@idf_parametrize( + 'target', soc_filtered_targets('SOC_RMT_SUPPORTED == 1'), indirect=['target'] +) +def test_led_strip_rmt_ws2812(dut: Dut) -> None: + dut.expect_exact('example: Created LED strip object with RMT backend') + dut.expect_exact('example: Start blinking LED strip') + dut.expect_exact('example: LED OFF!') + dut.expect_exact('example: LED ON!') + + diff --git a/led_strip/examples/led_strip_spi_ws2812/pytest_led_strip_spi_ws2812.py b/led_strip/examples/led_strip_spi_ws2812/pytest_led_strip_spi_ws2812.py new file mode 100644 index 0000000000..b2d349778d --- /dev/null +++ b/led_strip/examples/led_strip_spi_ws2812/pytest_led_strip_spi_ws2812.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_idf.utils import soc_filtered_targets + +@pytest.mark.generic +@idf_parametrize( + 'target', soc_filtered_targets('SOC_SPI_SUPPORTED == 1'), indirect=['target'] +) +def test_led_strip_spi_ws2812(dut: Dut) -> None: + dut.expect_exact('example: Created LED strip object with SPI backend') + dut.expect_exact('example: Start blinking LED strip') + dut.expect_exact('example: LED OFF!') + dut.expect_exact('example: LED ON!') + + diff --git a/led_strip/idf_component.yml b/led_strip/idf_component.yml index 2778c5ac80..9f6a671263 100644 --- a/led_strip/idf_component.yml +++ b/led_strip/idf_component.yml @@ -1,4 +1,4 @@ -version: "3.0.1" +version: "3.1.0" description: Driver for Addressable LED Strip (WS2812, etc) url: https://github.com/espressif/idf-extra-components/tree/master/led_strip issues: https://github.com/espressif/idf-extra-components/issues diff --git a/led_strip/include/led_strip.h b/led_strip/include/led_strip.h index 36fe5fefff..2ba2dc28c8 100644 --- a/led_strip/include/led_strip.h +++ b/led_strip/include/led_strip.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,7 @@ #include "esp_err.h" #include "led_strip_rmt.h" #include "led_strip_spi.h" +#include "led_strip_parlio.h" #ifdef __cplusplus extern "C" { @@ -75,11 +76,33 @@ esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint * - ESP_OK: Refresh successfully * - ESP_FAIL: Refresh failed because some other error occurred * - * @note: - * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. + * @note After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. */ esp_err_t led_strip_refresh(led_strip_handle_t strip); +/** + * @brief Refresh memory colors to LEDs asynchronously + * + * @param strip: LED strip + * + * @return + * - ESP_OK: Refresh successfully + * - ESP_FAIL: Refresh failed because some other error occurred + * + * @note This function is non-blocking, so you need to call `led_strip_refresh_wait_async_done` to wait for the refresh to complete before modifying the LED colors again. + */ +esp_err_t led_strip_refresh_async(led_strip_handle_t strip); + +/** + * @brief Wait for the async refresh to complete + * + * @param strip: LED strip + * + * @return + * - ESP_OK: Wait for the async refresh to complete successfully + */ +esp_err_t led_strip_refresh_wait_async_done(led_strip_handle_t strip); + /** * @brief Clear LED strip (turn off all LEDs) * @@ -91,6 +114,21 @@ esp_err_t led_strip_refresh(led_strip_handle_t strip); */ esp_err_t led_strip_clear(led_strip_handle_t strip); +/** + * @brief Switch GPIO of LED strip + * + * @param strip: LED strip + * @param new_gpio_num: new GPIO number + * @param invert_output: invert output + * + * @note Only support RMT backend now + * + * @return + * - ESP_OK: Switch GPIO successfully + * - ESP_FAIL: Switch GPIO failed because some other error occurred + */ +esp_err_t led_strip_switch_gpio(led_strip_handle_t strip, gpio_num_t new_gpio_num, bool invert_output); + /** * @brief Free LED strip resources * @@ -102,6 +140,30 @@ esp_err_t led_strip_clear(led_strip_handle_t strip); */ esp_err_t led_strip_del(led_strip_handle_t strip); +/** + * @brief Get the handle of the LED strip + * + * @param group: LED strip group handle + * @param index: Index of the LED strip in the group + * @param ret_strip: Pointer to store the handle of the LED strip + * + * @return + * - ESP_OK: Get the handle of the LED strip successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t led_strip_group_get_strip_handle(led_strip_group_handle_t group, uint8_t index, led_strip_handle_t *ret_strip); + +/** + * @brief Delete the LED strip group + * + * @param group: Handle of the LED strip group + * + * @return + * - ESP_OK: Delete the LED strip group successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t led_strip_group_del(led_strip_group_handle_t group); + #ifdef __cplusplus } #endif diff --git a/led_strip/include/led_strip_parlio.h b/led_strip/include/led_strip_parlio.h new file mode 100644 index 0000000000..ef25ac9659 --- /dev/null +++ b/led_strip/include/led_strip_parlio.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "driver/parlio_tx.h" +#include "driver/parlio_types.h" +#include "led_strip_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LED Strip PARLIO specific configuration + */ +typedef struct { + parlio_clock_source_t clk_src; /*!< PARLIO clock source */ + uint8_t strip_count; /*!< Number of LED strips. Should be a power of 2 and not larger than PARLIO_TX_UNIT_MAX_DATA_WIDTH */ + gpio_num_t strip_gpio_num[PARLIO_TX_UNIT_MAX_DATA_WIDTH]; /*!< GPIO number that used by LED strip */ +} led_strip_parlio_config_t; + +/** + * @brief Create LED strip group based on PARLIO_TX unit + * + * @note The strip_gpio_num in led_config no longer takes effect, and other configurations will be shared by all LED strips in the group. + * + * @param led_config LED strip configuration + * @param parlio_config PARLIO specific configuration + * @param ret_group Returned LED strip group handle + * @return + * - ESP_OK: create LED strip handle successfully + * - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument + * - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration + * - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory + */ +esp_err_t led_strip_new_parlio_group(const led_strip_config_t *led_config, const led_strip_parlio_config_t *parlio_config, led_strip_group_handle_t *ret_group); + +#ifdef __cplusplus +} +#endif diff --git a/led_strip/include/led_strip_spi.h b/led_strip/include/led_strip_spi.h index cd66e7a8f1..8a19aacfdb 100644 --- a/led_strip/include/led_strip_spi.h +++ b/led_strip/include/led_strip_spi.h @@ -20,7 +20,7 @@ extern "C" { typedef struct { spi_clock_source_t clk_src; /*!< SPI clock source */ spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */ - struct { + struct led_strip_spi_extra_config { uint32_t with_dma: 1; /*!< Use DMA to transmit data */ } flags; /*!< Extra driver flags */ } led_strip_spi_config_t; diff --git a/led_strip/include/led_strip_types.h b/led_strip/include/led_strip_types.h index 7b7b8e6a2f..0470c9b2ea 100644 --- a/led_strip/include/led_strip_types.h +++ b/led_strip/include/led_strip_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,11 @@ extern "C" { */ typedef struct led_strip_t *led_strip_handle_t; +/** + * @brief Type of LED strip group handle + */ +typedef struct led_strip_group_t *led_strip_group_handle_t; + /** * @brief LED strip model * @note Different led model may have different timing parameters, so we need to distinguish them. @@ -24,9 +29,22 @@ typedef enum { LED_MODEL_WS2812, /*!< LED strip model: WS2812 */ LED_MODEL_SK6812, /*!< LED strip model: SK6812 */ LED_MODEL_WS2811, /*!< LED strip model: WS2811 */ + LED_MODEL_CUSTOM, /*!< Custom LED strip model. Only used for RMT backend. The timings can be specified by the `led_strip_encoder_timings_t` */ LED_MODEL_INVALID /*!< Invalid LED strip model */ } led_model_t; +/** + * @brief LED strip encoder timings. + * @note The logic timings are in nanoseconds and the reset timings is in microseconds. + */ +typedef struct { + uint32_t t0h; /*!< High time for 0 bit, nanoseconds */ + uint32_t t1h; /*!< High time for 1 bit, nanoseconds */ + uint32_t t0l; /*!< Low time for 0 bit, nanoseconds */ + uint32_t t1l; /*!< Low time for 1 bit, nanoseconds */ + uint32_t reset; /*!< Reset time, microseconds */ +} led_strip_encoder_timings_t; + /** * @brief LED color component format * @note The format is used to specify the order of color components in each pixel, also the number of color components. @@ -59,6 +77,7 @@ typedef struct { led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */ led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel. Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */ + led_strip_encoder_timings_t timings; /*!< Encoder timings, only valid for RMT backend */ /*!< LED strip extra driver flags */ struct led_strip_extra_flags { uint32_t invert_out: 1; /*!< Invert output signal */ diff --git a/led_strip/interface/led_strip_interface.h b/led_strip/interface/led_strip_interface.h index 3de4c27155..e3e9e2a39d 100644 --- a/led_strip/interface/led_strip_interface.h +++ b/led_strip/interface/led_strip_interface.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,8 @@ extern "C" { typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */ +typedef struct led_strip_group_t led_strip_group_t; /*!< Type of LED group strip */ + /** * @brief LED strip interface definition */ @@ -55,22 +57,42 @@ struct led_strip_t { * @brief Refresh memory colors to LEDs * * @param strip: LED strip - * @param timeout_ms: timeout value for refreshing task * * @return * - ESP_OK: Refresh successfully * - ESP_FAIL: Refresh failed because some other error occurred * - * @note: - * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. + * @note After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. */ esp_err_t (*refresh)(led_strip_t *strip); + /** + * @brief Refresh memory colors to LEDs asynchronously + * + * @param strip: LED strip + * + * @return + * - ESP_OK: Refresh successfully + * - ESP_FAIL: Refresh failed because some other error occurred + * + * @note This function is non-blocking, so you need to call `led_strip_refresh_wait_async_done` to wait for the refresh to complete before modifying the LED colors again. + */ + esp_err_t (*refresh_async)(led_strip_t *strip); + + /** + * @brief Wait for the async refresh to complete + * + * @param strip: LED strip + * + * @return + * - ESP_OK: Wait for the async refresh to complete successfully + */ + esp_err_t (*refresh_wait_async_done)(led_strip_t *strip); + /** * @brief Clear LED strip (turn off all LEDs) * * @param strip: LED strip - * @param timeout_ms: timeout value for clearing task * * @return * - ESP_OK: Clear LEDs successfully @@ -78,6 +100,21 @@ struct led_strip_t { */ esp_err_t (*clear)(led_strip_t *strip); + /** + * @brief Switch GPIO of LED strip + * + * @param strip: LED strip + * @param new_gpio_num: new GPIO number + * @param invert_output: invert output + * + * @note Only support RMT backend now + * + * @return + * - ESP_OK: Switch GPIO successfully + * - ESP_FAIL: Switch GPIO failed because some other error occurred + */ + esp_err_t (*switch_gpio)(led_strip_t *strip, gpio_num_t new_gpio_num, bool invert_output); + /** * @brief Free LED strip resources * @@ -90,6 +127,35 @@ struct led_strip_t { esp_err_t (*del)(led_strip_t *strip); }; + +/** + * @brief LED strip group interface definition + */ +struct led_strip_group_t { + /** + * @brief Get LED strip handle by index + * + * @param group: LED strip group + * @param index: LED strip index + * @param ret_strip: Returned LED strip handle + * + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid argument + */ + esp_err_t (*get_strip_handle)(led_strip_group_t *group, uint8_t index, led_strip_handle_t *ret_strip); + /** + * @brief Free LED strip group resources + * + * @param group: LED strip group + * + * @return + * - ESP_OK: Free resources successfully + * - ESP_FAIL: Free resources failed because error occurred + */ + esp_err_t (*del)(led_strip_group_t *group); +}; + #ifdef __cplusplus } #endif diff --git a/led_strip/src/led_strip_api.c b/led_strip/src/led_strip_api.c index 6eb86b8f1b..89d553583f 100644 --- a/led_strip/src/led_strip_api.c +++ b/led_strip/src/led_strip_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -92,3 +92,21 @@ esp_err_t led_strip_del(led_strip_handle_t strip) ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return strip->del(strip); } + +esp_err_t led_strip_switch_gpio(led_strip_handle_t strip, gpio_num_t new_gpio_num, bool invert_output) +{ + ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + return strip->switch_gpio(strip, new_gpio_num, invert_output); +} + +esp_err_t led_strip_group_get_strip_handle(led_strip_group_handle_t group, uint8_t index, led_strip_handle_t *strip) +{ + ESP_RETURN_ON_FALSE(group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + return group->get_strip_handle(group, index, strip); +} + +esp_err_t led_strip_group_del(led_strip_group_handle_t group) +{ + ESP_RETURN_ON_FALSE(group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + return group->del(group); +} diff --git a/led_strip/src/led_strip_parlio_dev.c b/led_strip/src/led_strip_parlio_dev.c new file mode 100644 index 0000000000..77403f4c11 --- /dev/null +++ b/led_strip/src/led_strip_parlio_dev.c @@ -0,0 +1,312 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_rom_gpio.h" +#include "led_strip.h" +#include "led_strip_interface.h" + +#define LED_STRIP_PARLIO_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution +#define LED_STRIP_PARLIO_RESET_TIME 20 // Reset time between two frames, 20 * 400 ns/bit * 8 bit = 64 us + +// Each color of 1 bit is represented by 3 bits of PARLIO, low_level:100 ,high_level:110 +#define PARLIO_DATA_BITS_PER_COLOR_BIT 3 + +static const char *TAG = "led_strip_parlio"; + +typedef struct led_strip_parlio_group_t led_strip_parlio_group_t; +typedef struct led_strip_parlio_obj led_strip_parlio_obj; + +struct led_strip_parlio_obj { + led_strip_t base; + uint8_t strip_index; + led_strip_parlio_group_t *parlio_group; +}; + +struct led_strip_parlio_group_t { + led_strip_group_t base; // Base object + uint8_t strip_count; // Number of the LED strip channels + uint8_t buffer_bytes_per_color; // PARLIO buffer bytes occupied by 1 color byte + uint8_t bytes_per_pixel; // Number of bytes per pixel + uint32_t strip_len; // Number of LEDs in one strip + parlio_tx_unit_handle_t tx_unit; // PARLIO TX unit handle + led_color_component_format_t component_fmt; // LED color component format + led_strip_parlio_obj *parlio_strip[SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH]; // PARLIO LED strip object + uint8_t pixel_buf[]; +}; + +static void __led_strip_parlio_bit(uint8_t data, uint8_t *buf, uint8_t strip_count, uint8_t strip_index) +{ + // Each color of 1 bit is represented by 3 bits of PARLIO, low_level:100 ,high_level:110 + // So a color byte occupies 24 data bits of PARLIO. + // And buffer byte is share by all strip + // So 1 data bit occupies (strip_count / 8) buffer bits + + /* + * Taking 4 strips as an example, the bit distribution of each color byte is as follows: + * + * Original data bits: + * Strip 0: G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 R7 R6 R5 R4 R3 R2 R1 R0 + * Strip 1: G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 R7 R6 R5 R4 R3 R2 R1 R0 + * Strip 2: G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 R7 R6 R5 R4 R3 R2 R1 R0 + * Strip 3: G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 R7 R6 R5 R4 R3 R2 R1 R0 + * + * After PARLIO encoding, the distribution in the buffer is as follows: + * + * buffer: uint_8 uint_8 uint_8 uint_8 uint_8 uint_8 uint_8 uint_8 + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * Strip 0 | G7 G7 | G7 G6 | G6 G6 | ...... | ...... | R1 R1 | R1 R0 | R0 R0 | + * Strip 1 | G7 G7 | G7 G6 | G6 G6 | ...... | ...... | R1 R1 | R1 R0 | R0 R0 | + * Strip 2 | G7 G7 | G7 G6 | G6 G6 | ...... | ...... | R1 R1 | R1 R0 | R0 R0 | + * Strip 3 | G7 G7 | G7 G6 | G6 G6 | ...... | ...... | R1 R1 | R1 R0 | R0 R0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + */ + + uint8_t buffer_index = 0; + uint8_t logic_dilimiter = strip_index; + // Start with the MSB (Bit 7) + for (int i = 7; i >= 0; i--) { + while (logic_dilimiter >= 8) { + logic_dilimiter -= 8; + buffer_index += 1; + } + *(buf + buffer_index) |= BIT(logic_dilimiter); + logic_dilimiter += strip_count; + while (logic_dilimiter >= 8) { + logic_dilimiter -= 8; + buffer_index += 1; + } + if (data & BIT(i)) { + *(buf + buffer_index) |= BIT(logic_dilimiter); + } else { + *(buf + buffer_index) &= ~BIT(logic_dilimiter); + } + // the last color bit is always 0, skip + logic_dilimiter += 2 * strip_count; + } +} + +static esp_err_t led_strip_parlio_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) +{ + led_strip_parlio_obj *parlio_strip = __containerof(strip, led_strip_parlio_obj, base); + led_strip_parlio_group_t *parlio_group = parlio_strip->parlio_group; + ESP_RETURN_ON_FALSE(index < parlio_group->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); + // 1 pixel needs 3 color bytes, 1 color byte needs (PARLIO_DATA_BITS_PER_COLOR_BIT * strip_count) bytes + + uint8_t strip_count = parlio_group->strip_count; + uint8_t buffer_bytes_per_color = parlio_group->buffer_bytes_per_color; + uint32_t start = index * parlio_group->bytes_per_pixel * buffer_bytes_per_color; + uint8_t *pixel_buf = parlio_group->pixel_buf; + led_color_component_format_t component_fmt = parlio_group->component_fmt; + + __led_strip_parlio_bit(red, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.r_pos], strip_count, parlio_strip->strip_index); + __led_strip_parlio_bit(green, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.g_pos], strip_count, parlio_strip->strip_index); + __led_strip_parlio_bit(blue, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.b_pos], strip_count, parlio_strip->strip_index); + if (component_fmt.format.num_components > 3) { + __led_strip_parlio_bit(0, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.w_pos], strip_count, parlio_strip->strip_index); + } + + return ESP_OK; +} + +static esp_err_t led_strip_parlio_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white) +{ + led_strip_parlio_obj *parlio_strip = __containerof(strip, led_strip_parlio_obj, base); + led_strip_parlio_group_t *parlio_group = parlio_strip->parlio_group; + led_color_component_format_t component_fmt = parlio_group->component_fmt; + ESP_RETURN_ON_FALSE(index < parlio_group->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); + ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components"); + + uint8_t strip_count = parlio_group->strip_count; + uint8_t buffer_bytes_per_color = parlio_group->buffer_bytes_per_color; + uint32_t start = index * parlio_group->bytes_per_pixel * buffer_bytes_per_color; + uint8_t *pixel_buf = parlio_group->pixel_buf; + + __led_strip_parlio_bit(red, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.r_pos], strip_count, parlio_strip->strip_index); + __led_strip_parlio_bit(green, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.g_pos], strip_count, parlio_strip->strip_index); + __led_strip_parlio_bit(blue, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.b_pos], strip_count, parlio_strip->strip_index); + __led_strip_parlio_bit(white, &pixel_buf[start + buffer_bytes_per_color * component_fmt.format.w_pos], strip_count, parlio_strip->strip_index); + + return ESP_OK; +} + +static esp_err_t led_strip_parlio_refresh_async(led_strip_t *strip) +{ + led_strip_parlio_obj *parlio_strip = __containerof(strip, led_strip_parlio_obj, base); + led_strip_parlio_group_t *parlio_group = parlio_strip->parlio_group; + parlio_transmit_config_t transmit_config = { + .idle_value = 0x00, + .flags.queue_nonblocking = true, + }; + parlio_tx_unit_handle_t tx_unit = parlio_group->tx_unit; + uint8_t *tx_buffer = parlio_group->pixel_buf; + size_t tx_length = parlio_group->strip_len * parlio_group->bytes_per_pixel * parlio_group->buffer_bytes_per_color * 8; + size_t reset_length = parlio_group->strip_count * LED_STRIP_PARLIO_RESET_TIME * 8; + ESP_RETURN_ON_ERROR(parlio_tx_unit_enable(parlio_group->tx_unit), TAG, "enable parlio unit failed"); + ESP_RETURN_ON_ERROR(parlio_tx_unit_transmit(tx_unit, tx_buffer, tx_length + reset_length, &transmit_config), TAG, "transmit pixels by PARLIO failed"); + return ESP_OK; +} + +static esp_err_t led_strip_parlio_refresh_wait_async_done(led_strip_t *strip) +{ + led_strip_parlio_obj *parlio_strip = __containerof(strip, led_strip_parlio_obj, base); + led_strip_parlio_group_t *parlio_group = parlio_strip->parlio_group; + ESP_RETURN_ON_ERROR(parlio_tx_unit_wait_all_done(parlio_group->tx_unit, -1), TAG, "wait for done failed"); + ESP_RETURN_ON_ERROR(parlio_tx_unit_disable(parlio_group->tx_unit), TAG, "disable parlio unit failed"); + return ESP_OK; +} + +static esp_err_t led_strip_parlio_refresh(led_strip_t *strip) +{ + ESP_RETURN_ON_ERROR(led_strip_parlio_refresh_async(strip), TAG, "refresh failed"); + ESP_RETURN_ON_ERROR(led_strip_parlio_refresh_wait_async_done(strip), TAG, "wait for done failed"); + return ESP_OK; +} + +static esp_err_t led_strip_parlio_clear(led_strip_t *strip) +{ + led_strip_parlio_obj *parlio_strip = __containerof(strip, led_strip_parlio_obj, base); + led_strip_parlio_group_t *parlio_group = parlio_strip->parlio_group; + uint8_t buffer_bytes_per_color = parlio_group->buffer_bytes_per_color; + + uint8_t *buf = parlio_group->pixel_buf; + for (int index = 0; index < parlio_group->strip_len * parlio_group->bytes_per_pixel; index++) { + __led_strip_parlio_bit(0, buf, parlio_group->strip_count, parlio_strip->strip_index); + buf += buffer_bytes_per_color; + } + + return led_strip_parlio_refresh(strip); +} + +static esp_err_t led_strip_parlio_del(led_strip_t *strip) +{ + ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "please call ""led_strip_group_del"" to delete the group"); + return ESP_OK; +} + +static esp_err_t led_strip_parlio_group_get_strip_handle(led_strip_group_t *strip_group, uint8_t index, led_strip_handle_t *ret_strip) +{ + led_strip_parlio_group_t *parlio_group = __containerof(strip_group, led_strip_parlio_group_t, base); + ESP_RETURN_ON_FALSE(index <= parlio_group->strip_count, ESP_ERR_INVALID_ARG, TAG, "invalid index"); + *ret_strip = &parlio_group->parlio_strip[index]->base; + return ESP_OK; +} + +static esp_err_t led_strip_parlio_group_del(led_strip_group_t *strip_group) +{ + led_strip_parlio_group_t *parlio_group = __containerof(strip_group, led_strip_parlio_group_t, base); + + if (parlio_group->tx_unit) { + ESP_RETURN_ON_ERROR(parlio_del_tx_unit(parlio_group->tx_unit), TAG, "delete parlio_tx failed"); + } + for (int i = 0; i < parlio_group->strip_count; i++) { + if (parlio_group->parlio_strip[i]) { + free(parlio_group->parlio_strip[i]); + } + } + free(parlio_group); + + return ESP_OK; +} + +esp_err_t led_strip_new_parlio_group(const led_strip_config_t *led_config, const led_strip_parlio_config_t *parlio_config, led_strip_group_handle_t *ret_group) +{ + led_strip_parlio_group_t *parlio_group = NULL; + led_strip_parlio_obj *parlio_strip = NULL; + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(led_config && parlio_config && ret_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + // strip_count must be power of 2 and less than or equal to SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH + ESP_RETURN_ON_FALSE(parlio_config->strip_count && (parlio_config->strip_count <= SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH) && ((parlio_config->strip_count & (parlio_config->strip_count - 1)) == 0), + ESP_ERR_INVALID_ARG, TAG, "invalid strip count"); + + led_color_component_format_t component_fmt = led_config->color_component_format; + // If R/G/B order is not specified, set default GRB order as fallback + if (component_fmt.format_id == 0) { + component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB; + } + // check the validation of the color component format + uint8_t mask = 0; + if (component_fmt.format.num_components == 3) { + mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos); + // Check for invalid values + ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument"); + } else if (component_fmt.format.num_components == 4) { + mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos); + // Check for invalid values + ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument"); + } else { + ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components); + } + // TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component? + uint8_t bytes_per_pixel = component_fmt.format.num_components; + uint32_t mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; + + // buffer is share by all strip + uint32_t buffer_bytes_per_color = parlio_config->strip_count * PARLIO_DATA_BITS_PER_COLOR_BIT; + parlio_group = heap_caps_calloc(1, sizeof(led_strip_parlio_group_t) + led_config->max_leds * bytes_per_pixel * buffer_bytes_per_color + parlio_config->strip_count * LED_STRIP_PARLIO_RESET_TIME, mem_caps); + parlio_strip = heap_caps_calloc(parlio_config->strip_count, sizeof(led_strip_parlio_obj), mem_caps); + ESP_GOTO_ON_FALSE(parlio_group && parlio_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for parlio strip"); + + // for backward compatibility, if the user does not set the clk_src, use the default value + parlio_clock_source_t clk_src = PARLIO_CLK_SRC_DEFAULT; + if (parlio_config->clk_src) { + clk_src = parlio_config->clk_src; + } + + parlio_tx_unit_config_t parlio_tx_unit_config = { + .clk_src = clk_src, + .data_width = parlio_config->strip_count, + .clk_in_gpio_num = -1, + .clk_out_gpio_num = -1, + .output_clk_freq_hz = LED_STRIP_PARLIO_DEFAULT_RESOLUTION, + .trans_queue_depth = 1, // Only have one pixel_buf, so only one transaction is needed + .max_transfer_size = led_config->max_leds * bytes_per_pixel * buffer_bytes_per_color + LED_STRIP_PARLIO_RESET_TIME * parlio_config->strip_count, + .valid_gpio_num = -1, + }; + memcpy(parlio_tx_unit_config.data_gpio_nums, parlio_config->strip_gpio_num, parlio_config->strip_count * sizeof(parlio_config->strip_gpio_num[0])); + + ESP_GOTO_ON_ERROR(parlio_new_tx_unit(&parlio_tx_unit_config, &parlio_group->tx_unit), err, TAG, "init parlio unit failed"); + + //ensure the reset time is enough + esp_rom_delay_us(10); + + parlio_group->buffer_bytes_per_color = buffer_bytes_per_color; + parlio_group->component_fmt = component_fmt; + parlio_group->bytes_per_pixel = bytes_per_pixel; + parlio_group->strip_len = led_config->max_leds; + parlio_group->strip_count = parlio_config->strip_count; + parlio_group->base.get_strip_handle = led_strip_parlio_group_get_strip_handle; + parlio_group->base.del = led_strip_parlio_group_del; + + for (int i = 0; i < parlio_group->strip_count; i++) { + parlio_strip[i].base.set_pixel = led_strip_parlio_set_pixel; + parlio_strip[i].base.set_pixel_rgbw = led_strip_parlio_set_pixel_rgbw; + parlio_strip[i].base.refresh = led_strip_parlio_refresh; + parlio_strip[i].base.refresh_async = led_strip_parlio_refresh_async; + parlio_strip[i].base.refresh_wait_async_done = led_strip_parlio_refresh_wait_async_done; + parlio_strip[i].base.clear = led_strip_parlio_clear; + parlio_strip[i].base.del = led_strip_parlio_del; + parlio_strip[i].strip_index = i; + parlio_strip[i].parlio_group = parlio_group; + parlio_group->parlio_strip[i] = &parlio_strip[i]; + } + *ret_group = &parlio_group->base; + return ESP_OK; +err: + if (parlio_group) { + if (parlio_group->tx_unit) { + parlio_del_tx_unit(parlio_group->tx_unit); + } + free(parlio_group); + } + if (parlio_strip) { + free(parlio_strip); + } + return ret; +} diff --git a/led_strip/src/led_strip_rmt_dev.c b/led_strip/src/led_strip_rmt_dev.c index 4a38f4eee7..1f750864d6 100644 --- a/led_strip/src/led_strip_rmt_dev.c +++ b/led_strip/src/led_strip_rmt_dev.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,7 +71,7 @@ static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index return ESP_OK; } -static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) +static esp_err_t led_strip_rmt_refresh_async(led_strip_t *strip) { led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); rmt_transmit_config_t tx_conf = { @@ -81,11 +81,24 @@ static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) 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 * rmt_strip->bytes_per_pixel, &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"); + return ESP_OK; +} + +static esp_err_t led_strip_rmt_refresh_wait_async_done(led_strip_t *strip) +{ + led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); + ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "wait for RMT done failed"); ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed"); return ESP_OK; } +static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) +{ + ESP_RETURN_ON_ERROR(led_strip_rmt_refresh_async(strip), TAG, "refresh failed"); + ESP_RETURN_ON_ERROR(led_strip_rmt_refresh_wait_async_done(strip), TAG, "wait for done failed"); + return ESP_OK; +} + static esp_err_t led_strip_rmt_clear(led_strip_t *strip) { led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); @@ -103,6 +116,13 @@ static esp_err_t led_strip_rmt_del(led_strip_t *strip) return ESP_OK; } +static esp_err_t led_strip_rmt_switch_gpio(led_strip_t *strip, gpio_num_t new_gpio_num, bool invert_output) +{ + led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); + ESP_RETURN_ON_ERROR(rmt_tx_switch_gpio(rmt_strip->rmt_chan, new_gpio_num, invert_output), TAG, "switch RMT GPIO failed"); + return ESP_OK; +} + 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) { led_strip_rmt_obj *rmt_strip = NULL; @@ -155,7 +175,8 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l led_strip_encoder_config_t strip_encoder_conf = { .resolution = resolution, - .led_model = led_config->led_model + .led_model = led_config->led_model, + .timings = led_config->timings, }; ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed"); @@ -165,8 +186,11 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l rmt_strip->base.set_pixel = led_strip_rmt_set_pixel; rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw; rmt_strip->base.refresh = led_strip_rmt_refresh; + rmt_strip->base.refresh_async = led_strip_rmt_refresh_async; + rmt_strip->base.refresh_wait_async_done = led_strip_rmt_refresh_wait_async_done; rmt_strip->base.clear = led_strip_rmt_clear; rmt_strip->base.del = led_strip_rmt_del; + rmt_strip->base.switch_gpio = led_strip_rmt_switch_gpio; *ret_strip = &rmt_strip->base; return ESP_OK; diff --git a/led_strip/src/led_strip_rmt_encoder.c b/led_strip/src/led_strip_rmt_encoder.c index d105136d3f..9bcb045bc4 100644 --- a/led_strip/src/led_strip_rmt_encoder.c +++ b/led_strip/src/led_strip_rmt_encoder.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -122,19 +122,38 @@ esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rm bytes_encoder_config = (rmt_bytes_encoder_config_t) { .bit0 = { .level0 = 1, - .duration0 = 0.5 * config->resolution / 1000000., // T0H=0.5us + .duration0 = 0.5 * config->resolution / 1000000, // T0H=0.5us .level1 = 0, - .duration1 = 2.0 * config->resolution / 1000000., // T0L=2.0us + .duration1 = 2.0 * config->resolution / 1000000, // T0L=2.0us }, .bit1 = { .level0 = 1, - .duration0 = 1.2 * config->resolution / 1000000., // T1H=1.2us + .duration0 = 1.2 * config->resolution / 1000000, // T1H=1.2us .level1 = 0, - .duration1 = 1.3 * config->resolution / 1000000., // T1L=1.3us + .duration1 = 1.3 * config->resolution / 1000000, // T1L=1.3us }, .flags.msb_first = 1 }; reset_ticks = config->resolution / 1000000 * 50 / 2; // divide by 2... signal is sent twice + } else if (config->led_model == LED_MODEL_CUSTOM) { + // Custom LED strip model. Use the timings from the config + ESP_GOTO_ON_FALSE(config->timings.t0h && config->timings.t0l && config->timings.t1h && config->timings.t1l && config->timings.reset, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + bytes_encoder_config = (rmt_bytes_encoder_config_t) { + .bit0 = { + .level0 = 1, + .duration0 = (float)config->timings.t0h * config->resolution / 1000000000, // Convert ns to ticks + .level1 = 0, + .duration1 = (float)config->timings.t0l * config->resolution / 1000000000, + }, + .bit1 = { + .level0 = 1, + .duration0 = (float)config->timings.t1h * config->resolution / 1000000000, + .level1 = 0, + .duration1 = (float)config->timings.t1l * config->resolution / 1000000000, + }, + .flags.msb_first = 1 + }; + reset_ticks = config->resolution / 1000000 * config->timings.reset / 2; // divide by 2... signal is sent twice } else { assert(false); } diff --git a/led_strip/src/led_strip_rmt_encoder.h b/led_strip/src/led_strip_rmt_encoder.h index ba71e60ab6..688d75c2e8 100644 --- a/led_strip/src/led_strip_rmt_encoder.h +++ b/led_strip/src/led_strip_rmt_encoder.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,6 +19,7 @@ extern "C" { typedef struct { uint32_t resolution; /*!< Encoder resolution, in Hz */ led_model_t led_model; /*!< LED model */ + led_strip_encoder_timings_t timings; /*!< Encoder timings */ } led_strip_encoder_config_t; /** diff --git a/led_strip/src/led_strip_spi_dev.c b/led_strip/src/led_strip_spi_dev.c index 81e0259ceb..b2209a79d2 100644 --- a/led_strip/src/led_strip_spi_dev.c +++ b/led_strip/src/led_strip_spi_dev.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ #define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution #define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4 +// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110 #define SPI_BYTES_PER_COLOR_BYTE 3 #define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8) @@ -87,7 +88,7 @@ static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index return ESP_OK; } -static esp_err_t led_strip_spi_refresh(led_strip_t *strip) +static esp_err_t led_strip_spi_refresh_async(led_strip_t *strip) { led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); spi_transaction_t tx_conf; @@ -96,8 +97,22 @@ static esp_err_t led_strip_spi_refresh(led_strip_t *strip) tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE; tx_conf.tx_buffer = spi_strip->pixel_buf; tx_conf.rx_buffer = NULL; - ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed"); + ESP_RETURN_ON_ERROR(spi_device_queue_trans(spi_strip->spi_device, &tx_conf, portMAX_DELAY), TAG, "transmit pixels by SPI failed"); + return ESP_OK; +} + +static esp_err_t led_strip_spi_refresh_wait_async_done(led_strip_t *strip) +{ + led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base); + spi_transaction_t *tx_conf = NULL; + ESP_RETURN_ON_ERROR(spi_device_get_trans_result(spi_strip->spi_device, &tx_conf, portMAX_DELAY), TAG, "wait for done failed"); + return ESP_OK; +} +static esp_err_t led_strip_spi_refresh(led_strip_t *strip) +{ + ESP_RETURN_ON_ERROR(led_strip_spi_refresh_async(strip), TAG, "refresh async failed"); + ESP_RETURN_ON_ERROR(led_strip_spi_refresh_wait_async_done(strip), TAG, "wait for done failed"); return ESP_OK; } @@ -211,6 +226,8 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l spi_strip->base.set_pixel = led_strip_spi_set_pixel; spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw; spi_strip->base.refresh = led_strip_spi_refresh; + spi_strip->base.refresh_async = led_strip_spi_refresh_async; + spi_strip->base.refresh_wait_async_done = led_strip_spi_refresh_wait_async_done; spi_strip->base.clear = led_strip_spi_clear; spi_strip->base.del = led_strip_spi_del;