FastLED 3.9.15
Loading...
Searching...
No Matches
led_strip_spi_dev.c
Go to the documentation of this file.
1#ifdef ESP32
2
3#include "enabled.h"
4
5#if FASTLED_ESP32_HAS_CLOCKLESS_SPI
6/*
7 * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12#include <stdlib.h>
13#include <string.h>
14#include <sys/cdefs.h>
15#include "esp_log.h"
16#include "esp_check.h"
17#include "esp_rom_gpio.h"
18#include "soc/spi_periph.h"
19#include "led_strip.h"
20#include "led_strip_interface.h"
21#include "fl/unused.h"
22
23#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
24#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
25
26#define SPI_BYTES_PER_COLOR_BYTE 3
27#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
28
29// #define FASTLED_ESP32_SPI_HACK_1949 0
30
31// Work-around for: https://github.com/FastLED/FastLED/issues/1949
32// Looks like the SPI controller provided by espressif will attempt
33// to free memory it doesn't own. This hack fixes this but you'll
34// need to make sure you wait long enough for the transaction to complete.
35#ifndef FASTLED_ESP32_SPI_HACK_NO_TRANSACTION_WAIT
36#define FASTLED_ESP32_SPI_HACK_NO_TRANSACTION_WAIT 0
37#endif
38
39static const char *TAG = "led_strip_spi";
40
41typedef struct {
42 led_strip_t base;
43 spi_host_device_t spi_host;
44 spi_device_handle_t spi_device;
45 uint32_t strip_len;
46 uint8_t bytes_per_pixel;
47 led_color_component_format_t component_fmt;
48 spi_transaction_t tx_trans; // Transaction object stored in the controller
49 bool trans_pending; // Flag to track if transaction is pending
50 uint8_t pixel_buf[];
51} led_strip_spi_obj;
52
53// please make sure to zero-initialize the buf before calling this function
54static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
55{
56 // Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
57 // So a color byte occupies 3 bytes of SPI.
58 *(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
59 *(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
60 *(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
61 *(buf + 1) |= BIT(0);
62 *(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
63 *(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
64 *(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
65 *(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
66 *(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
67}
68
69static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
70{
71 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
72 ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
73 // 3 pixels take 72bits(9bytes)
74 uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
75 uint8_t *pixel_buf = spi_strip->pixel_buf;
76 led_color_component_format_t component_fmt = spi_strip->component_fmt;
77 memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
78
79 __led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
80 __led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
81 __led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
82 if (component_fmt.format.num_components > 3) {
83 __led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
84 }
85
86 return ESP_OK;
87}
88
89static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
90{
91 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
92 led_color_component_format_t component_fmt = spi_strip->component_fmt;
93 ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
94 ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
95
96 // LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
97 uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
98 uint8_t *pixel_buf = spi_strip->pixel_buf;
99 memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
100
101 __led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
102 __led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
103 __led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
104 __led_strip_spi_bit(white, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
105
106 return ESP_OK;
107}
108
109static esp_err_t spi_led_strip_refresh_async(led_strip_t *strip)
110{
111 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
112
113 // Wait for any pending transaction to complete before starting a new one
114 if (spi_strip->trans_pending) {
115 spi_transaction_t* trans_ptr;
116 spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
117 spi_strip->trans_pending = false;
118 }
119
120 // Initialize the transaction object stored in the controller
121 memset(&spi_strip->tx_trans, 0, sizeof(spi_strip->tx_trans));
122 spi_strip->tx_trans.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
123 spi_strip->tx_trans.tx_buffer = spi_strip->pixel_buf;
124 spi_strip->tx_trans.rx_buffer = NULL;
125
126 // Queue the transaction
127 esp_err_t ret = spi_device_queue_trans(spi_strip->spi_device, &spi_strip->tx_trans, portMAX_DELAY);
128 if (ret == ESP_OK) {
129 spi_strip->trans_pending = true;
130 }
131
132 return ret;
133}
134
135static esp_err_t spi_led_strip_refresh_wait_done(led_strip_t *strip)
136{
137 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
138
139 // Only wait if there's a pending transaction
140 if (spi_strip->trans_pending) {
141 spi_transaction_t* trans_ptr;
142 esp_err_t ret = spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
143 if (ret == ESP_OK) {
144 spi_strip->trans_pending = false;
145 }
146 return ret;
147 }
148
149 return ESP_OK;
150}
151
152static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
153{
154 ESP_RETURN_ON_ERROR(spi_led_strip_refresh_async(strip), TAG, "refresh async failed");
155 ESP_RETURN_ON_ERROR(spi_led_strip_refresh_wait_done(strip), TAG, "wait for done failed");
156 return ESP_OK;
157}
158
159
160static esp_err_t led_strip_spi_clear(led_strip_t *strip)
161{
162 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
163 //Write zero to turn off all leds
164 memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
165 uint8_t *buf = spi_strip->pixel_buf;
166 for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
167 __led_strip_spi_bit(0, buf);
168 buf += SPI_BYTES_PER_COLOR_BYTE;
169 }
170
171 return led_strip_spi_refresh(strip);
172}
173
174static esp_err_t led_strip_spi_del(led_strip_t *strip)
175{
176 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
177
178 // Wait for any pending transaction to complete before destroying the strip
179 if (spi_strip->trans_pending) {
180 spi_transaction_t* trans_ptr;
181 spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
182 spi_strip->trans_pending = false;
183 }
184
185 ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
186 ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
187
188 free(spi_strip);
189 return ESP_OK;
190}
191
192esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
193{
194 led_strip_spi_obj *spi_strip = NULL;
195 esp_err_t ret = ESP_OK;
196 FASTLED_UNUSED(ret);
197 ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
198 led_color_component_format_t component_fmt = led_config->color_component_format;
199 // If R/G/B order is not specified, set default GRB order as fallback
200 if (component_fmt.format_id == 0) {
201 component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
202 }
203 // check the validation of the color component format
204 uint8_t mask = 0;
205 if (component_fmt.format.num_components == 3) {
206 mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
207 // Check for invalid values
208 ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
209 } else if (component_fmt.format.num_components == 4) {
210 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);
211 // Check for invalid values
212 ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
213 } else {
214 ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
215 }
216 // TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component?
217 uint8_t bytes_per_pixel = component_fmt.format.num_components;
218 uint32_t mem_caps = MALLOC_CAP_DEFAULT;
219 if (spi_config->flags.with_dma) {
220 // DMA buffer must be placed in internal SRAM
221 mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
222 }
223 spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
224
225 ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
226
227 spi_strip->spi_host = spi_config->spi_bus;
228 // for backward compatibility, if the user does not set the clk_src, use the default value
229 spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
230 if (spi_config->clk_src) {
231 clk_src = spi_config->clk_src;
232 }
233
234 spi_bus_config_t spi_bus_cfg = {
235 .mosi_io_num = led_config->strip_gpio_num,
236 //Only use MOSI to generate the signal, set -1 when other pins are not used.
237 .miso_io_num = -1,
238 .sclk_io_num = -1,
239 .quadwp_io_num = -1,
240 .quadhd_io_num = -1,
241 .max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
242 };
243 ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
244
245 if (led_config->flags.invert_out == true) {
246 esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
247 }
248
249 spi_device_interface_config_t spi_dev_cfg = {
250 .clock_source = clk_src,
251 .command_bits = 0,
252 .address_bits = 0,
253 .dummy_bits = 0,
254 .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
255 .mode = 0,
256 //set -1 when CS is not used
257 .spics_io_num = -1,
258 .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
259 };
260
261 ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
262 //ensure the reset time is enough
263 esp_rom_delay_us(10);
264 int clock_resolution_khz = 0;
265 spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
266 // TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
267 // But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
268 // clock_resolution between 2.2MHz to 2.8MHz is supported
269 ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
270 TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
271
272 spi_strip->component_fmt = component_fmt;
273 spi_strip->bytes_per_pixel = bytes_per_pixel;
274 spi_strip->strip_len = led_config->max_leds;
275 spi_strip->trans_pending = false; // Initialize transaction pending flag
276 spi_strip->base.set_pixel = led_strip_spi_set_pixel;
277 spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
278 spi_strip->base.refresh = led_strip_spi_refresh;
279 spi_strip->base.refresh_async = spi_led_strip_refresh_async;
280 spi_strip->base.refresh_wait_done = spi_led_strip_refresh_wait_done;
281 spi_strip->base.clear = led_strip_spi_clear;
282 spi_strip->base.del = led_strip_spi_del;
283
284 *ret_strip = &spi_strip->base;
285 return ESP_OK;
286err:
287 if (spi_strip) {
288 if (spi_strip->spi_device) {
289 spi_bus_remove_device(spi_strip->spi_device);
290 }
291 if (spi_strip->spi_host) {
292 spi_bus_free(spi_strip->spi_host);
293 }
294 free(spi_strip);
295 }
296 return ret;
297}
298
299
300#endif // FASTLED_ESP_HAS_CLOCKLESS_SPI
301
302#endif // ESP32
LED strip interface definition.
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
Create LED strip based on SPI MOSI channel.
spi_host_device_t spi_bus
spi_clock_source_t clk_src
struct led_strip_spi_config_t::@276143246045101136044262336246252357033024237362 flags
LED Strip SPI specific configuration.
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB
Helper macros to set the color component format.
struct led_strip_t * led_strip_handle_t
Type of LED strip handle.
led_color_component_format_t color_component_format
struct led_strip_config_t::led_strip_extra_flags flags
struct led_color_component_format_t::format_layout format
LED color component format.
LED Strip common configurations The common configurations are not specific to any backend peripheral.
#define FASTLED_UNUSED(x)
Definition unused.h:4