Skip to content

Commit 10dc80c

Browse files
committed
Merge branch 'feat/update_ws2812_to_new_rmt_driver' into 'master'
feat(ws2812): Updated ws2812 based on new rmt driver See merge request adf/esp-adf-internal!1427
2 parents 25c484c + 3473a6e commit 10dc80c

File tree

4 files changed

+263
-35
lines changed

4 files changed

+263
-35
lines changed

examples/display/led_pixels/README_CN.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,9 @@ LED 矩阵有多种排列方式,下方列举三种 6 X 7 的 LED 矩阵排列
126126
│  ├── pixel_renderer
127127
│  │ ├── led_driver
128128
│  │ │ ├── ws2812_rmt
129-
| │ | | ├── ws2812_rmt.c
130-
| │ | | └── ws2812_rmt.h <!-- WS2812 灯珠的 RMT 驱动 -->
129+
| │ | | ├── ws2812_rmt.h <!-- WS2812 灯珠的 RMT 驱动 -->
130+
| │ | | ├── ws2812_rmt.c <!-- IDF version < 5.0.0 -->
131+
| │ | | └── ws2812_rmt_idf5.c <!-- IDF version >= 5.0.0 -->
131132
│  │ │ └── ws2812_spi
132133
| │ | ├── ws2812_spi.c
133134
| │ | └── ws2812_spi.h <!-- WS2812 灯珠的 SPI 驱动 -->

examples/display/led_pixels/components/pixel_renderer/CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ set(COMPONENT_ADD_INCLUDEDIRS "include"
66
set(COMPONENT_SRCS "pixel_renderer.c"
77
"pixel_coord.c"
88
"pixel_dev.c"
9-
"./led_driver/ws2812_spi/ws2812_spi.c"
10-
"./led_driver/ws2812_rmt/ws2812_rmt.c")
9+
"./led_driver/ws2812_spi/ws2812_spi.c")
10+
11+
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0")
12+
list(APPEND COMPONENT_SRCS "./led_driver/ws2812_rmt/ws2812_rmt.c")
13+
else()
14+
list(APPEND COMPONENT_SRCS "./led_driver/ws2812_rmt/ws2812_rmt_idf5.c")
15+
endif()
16+
1117
set(COMPONENT_REQUIRES utilis audio_sal)
1218

1319
register_component()

examples/display/led_pixels/components/pixel_renderer/led_driver/ws2812_rmt/ws2812_rmt.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,23 @@
2727
#include "driver/rmt.h"
2828
#include "ws2812_rmt.h"
2929

30-
#define WS2812_RMT_T0H_NS (350)
31-
#define WS2812_RMT_T0L_NS (1000)
32-
#define WS2812_RMT_T1H_NS (1000)
33-
#define WS2812_RMT_T1L_NS (350)
34-
#define WS2812_RMT_RESET_US (280)
30+
#define WS2812_RMT_T0H_NS (350)
31+
#define WS2812_RMT_T0L_NS (1000)
32+
#define WS2812_RMT_T1H_NS (1000)
33+
#define WS2812_RMT_T1L_NS (350)
34+
#define WS2812_RMT_RESET_US (280)
3535

3636
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0))
3737
static const char *TAG = "WS2812_RMT";
3838

3939
/**
40-
* @brief Default configuration for LED strip
40+
* @brief Default configuration for LED strip
4141
*
4242
*/
43-
#define WS2812_RMT_DEFAULT_CONFIG(number, dev_hdl) { \
44-
.max_leds = number, \
45-
.dev = dev_hdl, \
46-
}
43+
#define WS2812_RMT_DEFAULT_CONFIG(number, dev_hdl) { \
44+
.max_leds = number, \
45+
.dev = dev_hdl, \
46+
}
4747

4848
#define WS2812_RMT_CHECK(a, str, goto_tag, ret_value, ...) do { \
4949
if (!(a)) \
@@ -82,38 +82,38 @@ typedef struct {
8282
} ws2812_rmt_config_t;
8383

8484
/**
85-
* @brief Conver RGB data to RMT format.
85+
* @brief Convert RGB data to RMT format.
8686
*
87-
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
87+
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
8888
*
89-
* @param[in] src: Source data, to converted to RMT format
90-
* @param[in] dest: Place where to store the convert result
91-
* @param[in] src_size: Size of source data
92-
* @param[in] wanted_num: Number of RMT items that want to get
93-
* @param[out] translated_size: Number of source data that got converted
94-
* @param[out] item_num: Number of RMT items which are converted from source data
89+
* @param[in] src Source data, to converted to RMT format
90+
* @param[in] dest Place where to store the convert result
91+
* @param[in] src_size Size of source data
92+
* @param[in] wanted_num Number of RMT items that want to get
93+
* @param[out] translated_size Number of source data that got converted
94+
* @param[out] item_num Number of RMT items which are converted from source data
9595
*/
9696
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
97-
size_t wanted_num, size_t *translated_size, size_t *item_num)
97+
size_t wanted_num, size_t *translated_size, size_t *item_num)
9898
{
9999
if (src == NULL || dest == NULL) {
100100
*translated_size = 0;
101-
*item_num = 0;
101+
*item_num = 0;
102102
return;
103103
}
104-
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
105-
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
106-
size_t size = 0;
107-
size_t num = 0;
108-
uint8_t *psrc = (uint8_t *)src;
109-
rmt_item32_t *pdest = dest;
104+
const rmt_item32_t bit0 = {{{ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0}}}; // Logical 0
105+
const rmt_item32_t bit1 = {{{ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0}}}; // Logical 1
106+
size_t size = 0;
107+
size_t num = 0;
108+
uint8_t *psrc = (uint8_t *)src;
109+
rmt_item32_t *pdest = dest;
110110
while (size < src_size && num < wanted_num) {
111-
for (int i = 0; i < 8; i ++) {
111+
for (int i = 0; i < 8; i++) {
112112
// MSB first
113113
if (*psrc & (1 << (7 - i))) {
114-
pdest->val = bit1.val;
114+
pdest->val = bit1.val;
115115
} else {
116-
pdest->val = bit0.val;
116+
pdest->val = bit0.val;
117117
}
118118
num++;
119119
pdest++;
@@ -122,7 +122,7 @@ static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, si
122122
psrc++;
123123
}
124124
*translated_size = size;
125-
*item_num = num;
125+
*item_num = num;
126126
}
127127

128128
static esp_err_t _ws2812_rmt_set_pixel(ws2812_t *strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue)
@@ -131,7 +131,7 @@ static esp_err_t _ws2812_rmt_set_pixel(ws2812_t *strip, uint32_t index, uint8_t
131131
ws2812_rmt_dev_t *ws2812 = __containerof(strip, ws2812_rmt_dev_t, parent);
132132
WS2812_RMT_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
133133
uint32_t start = index * 3;
134-
// In thr order of GRB
134+
// In the order of GRB
135135
ws2812->buffer[start + 0] = green & 0xFF;
136136
ws2812->buffer[start + 1] = red & 0xFF;
137137
ws2812->buffer[start + 2] = blue & 0xFF;
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Espressif Modified MIT License
3+
*
4+
* Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD
5+
*
6+
* Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products.
7+
* This includes the right to use, copy, modify, merge, publish, distribute, and sublicense
8+
* the Software, subject to the following conditions:
9+
*
10+
* 1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS.
11+
* 2. The above copyright notice and this permission notice shall be included in all copies
12+
* or substantial portions of the Software.
13+
* 3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS
14+
* is strictly prohibited.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
18+
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
19+
* FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21+
* DEALINGS IN THE SOFTWARE.
22+
*
23+
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
24+
*/
25+
26+
#include <string.h>
27+
#include "esp_log.h"
28+
#include "freertos/FreeRTOS.h"
29+
#include "driver/rmt_tx.h"
30+
#include "driver/rmt_encoder.h"
31+
#include "ws2812_rmt.h"
32+
#include "esp_idf_version.h"
33+
34+
#define WS2812_RMT_RESOLUTION_HZ (10 * 1000 * 1000) // 10MHz resolution, 1 tick = 0.1us
35+
#define WS2812_RMT_T0H_NS (350)
36+
#define WS2812_RMT_T0L_NS (1000)
37+
#define WS2812_RMT_T1H_NS (1000)
38+
#define WS2812_RMT_T1L_NS (350)
39+
#define WS2812_RMT_RESET_US (280)
40+
41+
static const char *TAG = "WS2812_RMT";
42+
43+
/**
44+
* @brief Default configuration for LED strip
45+
*
46+
*/
47+
#define WS2812_RMT_DEFAULT_CONFIG(number, channel_handle) { \
48+
.max_leds = number, \
49+
.channel = channel_handle, \
50+
}
51+
52+
#define WS2812_RMT_CHECK(a, str, goto_tag, ret_value, ...) do { \
53+
if (!(a)) { \
54+
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
55+
ret = ret_value; \
56+
goto goto_tag; \
57+
} \
58+
} while (0)
59+
60+
typedef struct {
61+
ws2812_t parent;
62+
rmt_channel_handle_t rmt_channel;
63+
rmt_encoder_handle_t bytes_encoder;
64+
uint32_t strip_len;
65+
uint8_t buffer[0];
66+
} ws2812_rmt_dev_t;
67+
68+
/**
69+
* @brief LED Strip Configuration Type
70+
*
71+
*/
72+
typedef struct {
73+
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
74+
rmt_channel_handle_t channel; /*!< RMT channel handle */
75+
} ws2812_rmt_config_t;
76+
77+
static esp_err_t _ws2812_rmt_set_pixel(ws2812_t *strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue)
78+
{
79+
esp_err_t ret = ESP_OK;
80+
ws2812_rmt_dev_t *ws2812 = __containerof(strip, ws2812_rmt_dev_t, parent);
81+
WS2812_RMT_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
82+
uint32_t start = index * 3;
83+
// In the order of GRB
84+
ws2812->buffer[start + 0] = green;
85+
ws2812->buffer[start + 1] = red;
86+
ws2812->buffer[start + 2] = blue;
87+
return ESP_OK;
88+
err:
89+
return ret;
90+
}
91+
92+
static esp_err_t _ws2812_rmt_refresh(ws2812_t *strip, uint32_t timeout_ms)
93+
{
94+
esp_err_t ret = ESP_OK;
95+
ws2812_rmt_dev_t *ws2812 = __containerof(strip, ws2812_rmt_dev_t, parent);
96+
97+
rmt_transmit_config_t transmit_config = {
98+
.loop_count = 0, // No loop
99+
};
100+
101+
WS2812_RMT_CHECK(rmt_transmit(ws2812->rmt_channel, ws2812->bytes_encoder, ws2812->buffer,
102+
ws2812->strip_len * 3, &transmit_config)
103+
== ESP_OK,
104+
"transmit RMT samples failed", err, ESP_FAIL);
105+
106+
return rmt_tx_wait_all_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
107+
err:
108+
return ret;
109+
}
110+
111+
static esp_err_t _ws2812_rmt_clear(ws2812_t *strip, uint32_t timeout_ms)
112+
{
113+
ws2812_rmt_dev_t *ws2812 = __containerof(strip, ws2812_rmt_dev_t, parent);
114+
// Write zero to turn off all leds
115+
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
116+
return _ws2812_rmt_refresh(strip, timeout_ms);
117+
}
118+
119+
static esp_err_t _ws2812_rmt_del(ws2812_t *strip)
120+
{
121+
ws2812_rmt_dev_t *ws2812 = __containerof(strip, ws2812_rmt_dev_t, parent);
122+
123+
// Disable the channel first
124+
ESP_ERROR_CHECK(rmt_disable(ws2812->rmt_channel));
125+
126+
// Delete encoder and channel
127+
ESP_ERROR_CHECK(rmt_del_encoder(ws2812->bytes_encoder));
128+
ESP_ERROR_CHECK(rmt_del_channel(ws2812->rmt_channel));
129+
130+
free(ws2812);
131+
return ESP_OK;
132+
}
133+
134+
static ws2812_t *ws2812_rmt_new_ws2812(const ws2812_rmt_config_t *config)
135+
{
136+
ws2812_t *ret = NULL;
137+
ws2812_rmt_dev_t *ws2812 = NULL;
138+
WS2812_RMT_CHECK(config, "configuration can't be null", err, NULL);
139+
140+
// 24 bits per led
141+
uint32_t ws2812_size = sizeof(ws2812_rmt_dev_t) + config->max_leds * 3;
142+
143+
ws2812 = calloc(1, ws2812_size);
144+
WS2812_RMT_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
145+
146+
ws2812->rmt_channel = config->channel;
147+
ws2812->strip_len = config->max_leds;
148+
149+
// Create bytes encoder for WS2812
150+
rmt_bytes_encoder_config_t bytes_encoder_config = {
151+
.bit0 = {
152+
.level0 = 1,
153+
.duration0 = WS2812_RMT_T0H_NS / 100,
154+
.level1 = 0,
155+
.duration1 = WS2812_RMT_T0L_NS / 100,
156+
},
157+
.bit1 = {
158+
.level0 = 1,
159+
.duration0 = WS2812_RMT_T1H_NS / 100,
160+
.level1 = 0,
161+
.duration1 = WS2812_RMT_T1L_NS / 100,
162+
},
163+
.flags.msb_first = 1, // WS2812 uses MSB first
164+
};
165+
166+
WS2812_RMT_CHECK(rmt_new_bytes_encoder(&bytes_encoder_config, &ws2812->bytes_encoder) == ESP_OK,
167+
"create bytes encoder failed", err, NULL);
168+
169+
ws2812->parent.set_pixel = _ws2812_rmt_set_pixel;
170+
ws2812->parent.refresh = _ws2812_rmt_refresh;
171+
ws2812->parent.clear = _ws2812_rmt_clear;
172+
ws2812->parent.del = _ws2812_rmt_del;
173+
174+
return &ws2812->parent;
175+
err:
176+
if (ws2812) {
177+
free(ws2812);
178+
}
179+
return ret;
180+
}
181+
182+
ws2812_t *ws2812_rmt_init(uint8_t channel, uint8_t gpio, uint16_t led_num)
183+
{
184+
// New driver does not need to specify channel
185+
(void)channel;
186+
// Create RMT TX channel
187+
rmt_tx_channel_config_t tx_chan_config = {
188+
.clk_src = RMT_CLK_SRC_DEFAULT,
189+
.gpio_num = gpio,
190+
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
191+
.resolution_hz = WS2812_RMT_RESOLUTION_HZ,
192+
.trans_queue_depth = 1,
193+
};
194+
195+
rmt_channel_handle_t rmt_channel = NULL;
196+
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &rmt_channel));
197+
198+
// Enable the channel
199+
ESP_ERROR_CHECK(rmt_enable(rmt_channel));
200+
201+
// install ws2812 driver
202+
ws2812_rmt_config_t strip_config = WS2812_RMT_DEFAULT_CONFIG(led_num, rmt_channel);
203+
204+
ws2812_t *strip = ws2812_rmt_new_ws2812(&strip_config);
205+
206+
if (!strip) {
207+
ESP_LOGE(TAG, "install WS2812 driver failed");
208+
return NULL;
209+
}
210+
211+
// Clear LED strip (turn off all LEDs)
212+
ESP_ERROR_CHECK(strip->clear(strip, 100));
213+
214+
return strip;
215+
}
216+
217+
esp_err_t ws2812_rmt_deinit(ws2812_t *strip)
218+
{
219+
return strip->del(strip);
220+
}
221+

0 commit comments

Comments
 (0)