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;