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 #define RMT_TX_CHANNEL RMT_CHANNEL_0
23 
24 static const char *TAG = "ws2812";
25 #define STRIP_CHECK(a, str, goto_tag, ret_value, ...)                             \
26     do                                                                            \
27     {                                                                             \
28         if (!(a))                                                                 \
29         {                                                                         \
30             ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
31             ret = ret_value;                                                      \
32             goto goto_tag;                                                        \
33         }                                                                         \
34     } while (0)
35 
36 #define WS2812_T0H_NS (350)
37 #define WS2812_T0L_NS (1000)
38 #define WS2812_T1H_NS (1000)
39 #define WS2812_T1L_NS (350)
40 #define WS2812_RESET_US (280)
41 
42 static uint32_t ws2812_t0h_ticks = 0;
43 static uint32_t ws2812_t1h_ticks = 0;
44 static uint32_t ws2812_t0l_ticks = 0;
45 static uint32_t ws2812_t1l_ticks = 0;
46 
47 typedef struct {
48     led_strip_t parent;
49     rmt_channel_t rmt_channel;
50     uint32_t strip_len;
51     uint8_t buffer[0];
52 } ws2812_t;
53 
54 /**
55  * @brief Conver RGB data to RMT format.
56  *
57  * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
58  *
59  * @param[in] src: source data, to converted to RMT format
60  * @param[in] dest: place where to store the convert result
61  * @param[in] src_size: size of source data
62  * @param[in] wanted_num: number of RMT items that want to get
63  * @param[out] translated_size: number of source data that got converted
64  * @param[out] item_num: number of RMT items which are converted from source data
65  */
ws2812_rmt_adapter(const void * src,rmt_item32_t * dest,size_t src_size,size_t wanted_num,size_t * translated_size,size_t * item_num)66 static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
67         size_t wanted_num, size_t *translated_size, size_t *item_num)
68 {
69     if (src == NULL || dest == NULL) {
70         *translated_size = 0;
71         *item_num = 0;
72         return;
73     }
74     const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
75     const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
76     size_t size = 0;
77     size_t num = 0;
78     uint8_t *psrc = (uint8_t *)src;
79     rmt_item32_t *pdest = dest;
80     while (size < src_size && num < wanted_num) {
81         for (int i = 0; i < 8; i++) {
82             // MSB first
83             if (*psrc & (1 << (7 - i))) {
84                 pdest->val =  bit1.val;
85             } else {
86                 pdest->val =  bit0.val;
87             }
88             num++;
89             pdest++;
90         }
91         size++;
92         psrc++;
93     }
94     *translated_size = size;
95     *item_num = num;
96 }
97 
ws2812_set_pixel(led_strip_t * strip,uint32_t index,uint32_t red,uint32_t green,uint32_t blue)98 static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
99 {
100     esp_err_t ret = ESP_OK;
101     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
102     STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
103     uint32_t start = index * 3;
104     // In thr order of GRB
105     ws2812->buffer[start + 0] = green & 0xFF;
106     ws2812->buffer[start + 1] = red & 0xFF;
107     ws2812->buffer[start + 2] = blue & 0xFF;
108     return ESP_OK;
109 err:
110     return ret;
111 }
112 
ws2812_refresh(led_strip_t * strip,uint32_t timeout_ms)113 static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
114 {
115     esp_err_t ret = ESP_OK;
116     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
117     STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
118                 "transmit RMT samples failed", err, ESP_FAIL);
119     return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
120 err:
121     return ret;
122 }
123 
ws2812_clear(led_strip_t * strip,uint32_t timeout_ms)124 static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
125 {
126     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
127     // Write zero to turn off all leds
128     memset(ws2812->buffer, 0, ws2812->strip_len * 3);
129     return ws2812_refresh(strip, timeout_ms);
130 }
131 
ws2812_del(led_strip_t * strip)132 static esp_err_t ws2812_del(led_strip_t *strip)
133 {
134     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
135     free(ws2812);
136     return ESP_OK;
137 }
138 
led_strip_new_rmt_ws2812(const led_strip_config_t * config)139 led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
140 {
141     led_strip_t *ret = NULL;
142     STRIP_CHECK(config, "configuration can't be null", err, NULL);
143 
144     // 24 bits per led
145     uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
146     ws2812_t *ws2812 = calloc(1, ws2812_size);
147     STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
148 
149     uint32_t counter_clk_hz = 0;
150     STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
151                 "get rmt counter clock failed", err, NULL);
152     // ns -> ticks
153     float ratio = (float)counter_clk_hz / 1e9;
154     ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
155     ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
156     ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
157     ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
158 
159     // set ws2812 to rmt adapter
160     rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
161 
162     ws2812->rmt_channel = (rmt_channel_t)config->dev;
163     ws2812->strip_len = config->max_leds;
164 
165     ws2812->parent.set_pixel = ws2812_set_pixel;
166     ws2812->parent.refresh = ws2812_refresh;
167     ws2812->parent.clear = ws2812_clear;
168     ws2812->parent.del = ws2812_del;
169 
170     return &ws2812->parent;
171 err:
172     return ret;
173 }
174 
led_strip_init(uint8_t channel,uint8_t gpio,uint16_t led_num)175 led_strip_t * led_strip_init(uint8_t channel, uint8_t gpio, uint16_t led_num)
176 {
177     static led_strip_t *pStrip;
178 
179     rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, channel);
180     // set counter clock to 40MHz
181     config.clk_div = 2;
182 
183     ESP_ERROR_CHECK(rmt_config(&config));
184     ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
185 
186     // install ws2812 driver
187     led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(led_num, (led_strip_dev_t)config.channel);
188 
189     pStrip = led_strip_new_rmt_ws2812(&strip_config);
190 
191     if ( !pStrip ) {
192         ESP_LOGE(TAG, "install WS2812 driver failed");
193         return NULL;
194     }
195 
196     // Clear LED strip (turn off all LEDs)
197     ESP_ERROR_CHECK(pStrip->clear(pStrip, 100));
198 
199     return pStrip;
200 }
201 
led_strip_denit(led_strip_t * strip)202 esp_err_t led_strip_denit(led_strip_t *strip)
203 {
204     ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
205     ESP_ERROR_CHECK(rmt_driver_uninstall(ws2812->rmt_channel));
206     return strip->del(strip);
207 }
208