Skip to content

Commit a1d8045

Browse files
committed
main: add WS2812 support
1 parent dbd57e2 commit a1d8045

File tree

20 files changed

+624
-53
lines changed

20 files changed

+624
-53
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ idf.py flash monitor
7272

7373
<img src="docs/st7789log.png">
7474

75+
## VFX on WS2812 8x8 LED Panel (Rainbow)
76+
77+
<img src="docs/ws2812.png">
78+
7579
## VFX on CUBE0414 8x8x8 Music Light Cube
7680

7781
<img src="docs/cube0414.png">

components/led_strip/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(component_srcs "src/led_strip_rmt_ws2812.c")
2+
3+
idf_component_register(SRCS "${component_srcs}"
4+
INCLUDE_DIRS "include"
5+
PRIV_INCLUDE_DIRS ""
6+
PRIV_REQUIRES "driver"
7+
REQUIRES "")
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#pragma once
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
#include "esp_err.h"
21+
22+
/**
23+
* @brief LED Strip Type
24+
*
25+
*/
26+
typedef struct led_strip_s led_strip_t;
27+
28+
/**
29+
* @brief LED Strip Device Type
30+
*
31+
*/
32+
typedef void *led_strip_dev_t;
33+
34+
/**
35+
* @brief Declare of LED Strip Type
36+
*
37+
*/
38+
struct led_strip_s {
39+
/**
40+
* @brief Set RGB for a specific pixel
41+
*
42+
* @param strip: LED strip
43+
* @param index: index of pixel to set
44+
* @param red: red part of color
45+
* @param green: green part of color
46+
* @param blue: blue part of color
47+
*
48+
* @return
49+
* - ESP_OK: Set RGB for a specific pixel successfully
50+
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
51+
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
52+
*/
53+
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
54+
55+
/**
56+
* @brief Refresh memory colors to LEDs
57+
*
58+
* @param strip: LED strip
59+
* @param timeout_ms: timeout value for refreshing task
60+
*
61+
* @return
62+
* - ESP_OK: Refresh successfully
63+
* - ESP_ERR_TIMEOUT: Refresh failed because of timeout
64+
* - ESP_FAIL: Refresh failed because some other error occurred
65+
*
66+
* @note:
67+
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
68+
*/
69+
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
70+
71+
/**
72+
* @brief Clear LED strip (turn off all LEDs)
73+
*
74+
* @param strip: LED strip
75+
* @param timeout_ms: timeout value for clearing task
76+
*
77+
* @return
78+
* - ESP_OK: Clear LEDs successfully
79+
* - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
80+
* - ESP_FAIL: Clear LEDs failed because some other error occurred
81+
*/
82+
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
83+
84+
/**
85+
* @brief Free LED strip resources
86+
*
87+
* @param strip: LED strip
88+
*
89+
* @return
90+
* - ESP_OK: Free resources successfully
91+
* - ESP_FAIL: Free resources failed because error occurred
92+
*/
93+
esp_err_t (*del)(led_strip_t *strip);
94+
};
95+
96+
/**
97+
* @brief LED Strip Configuration Type
98+
*
99+
*/
100+
typedef struct {
101+
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
102+
led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
103+
} led_strip_config_t;
104+
105+
/**
106+
* @brief Default configuration for LED strip
107+
*
108+
*/
109+
#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
110+
{ \
111+
.max_leds = number, \
112+
.dev = dev_hdl, \
113+
}
114+
115+
/**
116+
* @brief Install a new ws2812 driver (based on RMT peripheral)
117+
*
118+
* @param config: LED strip configuration
119+
* @return
120+
* LED strip instance or NULL
121+
*/
122+
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
123+
124+
#ifdef __cplusplus
125+
}
126+
#endif
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <sys/cdefs.h>
17+
#include "esp_log.h"
18+
#include "esp_attr.h"
19+
#include "led_strip.h"
20+
#include "driver/rmt.h"
21+
22+
static const char *TAG = "ws2812";
23+
#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
24+
do \
25+
{ \
26+
if (!(a)) \
27+
{ \
28+
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
29+
ret = ret_value; \
30+
goto goto_tag; \
31+
} \
32+
} while (0)
33+
34+
#define WS2812_T0H_NS (350)
35+
#define WS2812_T0L_NS (1000)
36+
#define WS2812_T1H_NS (1000)
37+
#define WS2812_T1L_NS (350)
38+
#define WS2812_RESET_US (280)
39+
40+
static uint32_t ws2812_t0h_ticks = 0;
41+
static uint32_t ws2812_t1h_ticks = 0;
42+
static uint32_t ws2812_t0l_ticks = 0;
43+
static uint32_t ws2812_t1l_ticks = 0;
44+
45+
typedef struct {
46+
led_strip_t parent;
47+
rmt_channel_t rmt_channel;
48+
uint32_t strip_len;
49+
uint8_t buffer[0];
50+
} ws2812_t;
51+
52+
/**
53+
* @brief Conver RGB data to RMT format.
54+
*
55+
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
56+
*
57+
* @param[in] src: source data, to converted to RMT format
58+
* @param[in] dest: place where to store the convert result
59+
* @param[in] src_size: size of source data
60+
* @param[in] wanted_num: number of RMT items that want to get
61+
* @param[out] translated_size: number of source data that got converted
62+
* @param[out] item_num: number of RMT items which are converted from source data
63+
*/
64+
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
65+
size_t wanted_num, size_t *translated_size, size_t *item_num)
66+
{
67+
if (src == NULL || dest == NULL) {
68+
*translated_size = 0;
69+
*item_num = 0;
70+
return;
71+
}
72+
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
73+
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
74+
size_t size = 0;
75+
size_t num = 0;
76+
uint8_t *psrc = (uint8_t *)src;
77+
rmt_item32_t *pdest = dest;
78+
while (size < src_size && num < wanted_num) {
79+
for (int i = 0; i < 8; i++) {
80+
// MSB first
81+
if (*psrc & (1 << (7 - i))) {
82+
pdest->val = bit1.val;
83+
} else {
84+
pdest->val = bit0.val;
85+
}
86+
num++;
87+
pdest++;
88+
}
89+
size++;
90+
psrc++;
91+
}
92+
*translated_size = size;
93+
*item_num = num;
94+
}
95+
96+
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
97+
{
98+
esp_err_t ret = ESP_OK;
99+
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
100+
STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
101+
uint32_t start = index * 3;
102+
// In thr order of GRB
103+
ws2812->buffer[start + 0] = green & 0xFF;
104+
ws2812->buffer[start + 1] = red & 0xFF;
105+
ws2812->buffer[start + 2] = blue & 0xFF;
106+
return ESP_OK;
107+
err:
108+
return ret;
109+
}
110+
111+
static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
112+
{
113+
esp_err_t ret = ESP_OK;
114+
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
115+
STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
116+
"transmit RMT samples failed", err, ESP_FAIL);
117+
return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
118+
err:
119+
return ret;
120+
}
121+
122+
static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
123+
{
124+
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
125+
// Write zero to turn off all leds
126+
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
127+
return ws2812_refresh(strip, timeout_ms);
128+
}
129+
130+
static esp_err_t ws2812_del(led_strip_t *strip)
131+
{
132+
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
133+
free(ws2812);
134+
return ESP_OK;
135+
}
136+
137+
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
138+
{
139+
led_strip_t *ret = NULL;
140+
STRIP_CHECK(config, "configuration can't be null", err, NULL);
141+
142+
// 24 bits per led
143+
uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
144+
ws2812_t *ws2812 = calloc(1, ws2812_size);
145+
STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
146+
147+
uint32_t counter_clk_hz = 0;
148+
STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
149+
"get rmt counter clock failed", err, NULL);
150+
// ns -> ticks
151+
float ratio = (float)counter_clk_hz / 1e9;
152+
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
153+
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
154+
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
155+
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
156+
157+
// set ws2812 to rmt adapter
158+
rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
159+
160+
ws2812->rmt_channel = (rmt_channel_t)config->dev;
161+
ws2812->strip_len = config->max_leds;
162+
163+
ws2812->parent.set_pixel = ws2812_set_pixel;
164+
ws2812->parent.refresh = ws2812_refresh;
165+
ws2812->parent.clear = ws2812_clear;
166+
ws2812->parent.del = ws2812_del;
167+
168+
return &ws2812->parent;
169+
err:
170+
return ret;
171+
}

components/ugfx/drivers/gdisp/CUBE0414/gdisp_lld_CUBE0414.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
132132
LLDSPEC void gdisp_lld_write_color(GDisplay *g) {
133133
uint16_t pos = write_y * g->g.Width + write_x;
134134
LLDCOLOR_TYPE c = gdispColor2Native(g->p.color);
135-
#ifdef CONFIG_CUBE0414_COLOR_GRB
135+
#ifdef CONFIG_LED_COLOR_GRB
136136
*((uint8_t *)g->priv + pos * 3 + 0) = c >> 8;
137137
*((uint8_t *)g->priv + pos * 3 + 1) = c >> 16;
138138
*((uint8_t *)g->priv + pos * 3 + 2) = c;
@@ -170,7 +170,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
170170
}
171171
LLDSPEC color_t gdisp_lld_read_color(GDisplay *g) {
172172
uint16_t pos = read_y * g->g.Width + read_x;
173-
#ifdef CONFIG_CUBE0414_COLOR_GRB
173+
#ifdef CONFIG_LED_COLOR_GRB
174174
LLDCOLOR_TYPE c = (*((uint8_t *)g->priv + pos * 3 + 0) << 8)
175175
| (*((uint8_t *)g->priv + pos * 3 + 1) << 16)
176176
| (*((uint8_t *)g->priv + pos * 3 + 2));
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
GFXINC += $(GFXLIB)/drivers/gdisp/WS2812
2+
GFXSRC += $(GFXLIB)/drivers/gdisp/WS2812/gdisp_lld_WS2812.c

0 commit comments

Comments
 (0)