ESP-IDF Firmware
Firmware architecture and call graph
Loading...
Searching...
No Matches
led_strip_spi_dev.c
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6#include <stdlib.h>
7#include <string.h>
8#include <sys/cdefs.h>
9#include "esp_heap_caps.h"
10#include "esp_log.h"
11#include "esp_check.h"
12#include "esp_rom_gpio.h"
13#include "soc/spi_periph.h"
14#include "led_strip.h"
15#include "led_strip_interface.h"
16#include "hal/spi_hal.h"
17
18#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
19#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
20
21#define SPI_BYTES_PER_COLOR_BYTE 3
22#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
23
24static const char *TAG = "led_strip_spi";
25
26typedef struct {
28 spi_host_device_t spi_host;
29 spi_device_handle_t spi_device;
30 uint32_t strip_len;
32 uint8_t pixel_buf[];
34
35// please make sure to zero-initialize the buf before calling this function
36static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
37{
38 // Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
39 // So a color byte occupies 3 bytes of SPI.
40 *(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
41 *(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
42 *(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
43 *(buf + 1) |= BIT(0);
44 *(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
45 *(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
46 *(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
47 *(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
48 *(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
49}
50
51static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
52{
53 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
54 ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
55 // LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
56 uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
57 memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
58 __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
60 __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
61 if (spi_strip->bytes_per_pixel > 3) {
62 __led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
63 }
64 return ESP_OK;
65}
66
67static 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)
68{
69 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
70 ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
71 ESP_RETURN_ON_FALSE(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
72 // LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
73 uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
74 // SK6812 component order is GRBW
75 memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
76 __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
78 __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
79 __led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
80
81 return ESP_OK;
82}
83
85{
86 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
87 spi_transaction_t tx_conf;
88 memset(&tx_conf, 0, sizeof(tx_conf));
89
90 tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
91 tx_conf.tx_buffer = spi_strip->pixel_buf;
92 tx_conf.rx_buffer = NULL;
93 ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
94
95 return ESP_OK;
96}
97
99{
100 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
101 //Write zero to turn off all leds
102 memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
103 uint8_t *buf = spi_strip->pixel_buf;
104 for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
105 __led_strip_spi_bit(0, buf);
107 }
108
109 return led_strip_spi_refresh(strip);
110}
111
113{
114 led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
115
116 ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
117 ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
118
119 free(spi_strip);
120 return ESP_OK;
121}
122
124{
125 led_strip_spi_obj *spi_strip = NULL;
126 esp_err_t ret = ESP_OK;
127 ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
128 ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
129 uint8_t bytes_per_pixel = 3;
130 if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
131 bytes_per_pixel = 4;
132 } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
133 bytes_per_pixel = 3;
134 } else {
135 assert(false);
136 }
137 uint32_t mem_caps = MALLOC_CAP_DEFAULT;
138 if (spi_config->flags.with_dma) {
139 // DMA buffer must be placed in internal SRAM
140 mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
141 }
142 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);
143
144 ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
145
146 spi_strip->spi_host = spi_config->spi_bus;
147 // for backward compatibility, if the user does not set the clk_src, use the default value
148 spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
149 if (spi_config->clk_src) {
150 clk_src = spi_config->clk_src;
151 }
152
153 spi_bus_config_t spi_bus_cfg = {
154 .mosi_io_num = led_config->strip_gpio_num,
155 //Only use MOSI to generate the signal, set -1 when other pins are not used.
156 .miso_io_num = -1,
157 .sclk_io_num = -1,
158 .quadwp_io_num = -1,
159 .quadhd_io_num = -1,
160 .max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
161 };
162 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");
163
164 if (led_config->flags.invert_out == true) {
165 esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
166 }
167
168 spi_device_interface_config_t spi_dev_cfg = {
169 .clock_source = clk_src,
170 .command_bits = 0,
171 .address_bits = 0,
172 .dummy_bits = 0,
173 .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
174 .mode = 0,
175 //set -1 when CS is not used
176 .spics_io_num = -1,
178 };
179
180 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");
181 //ensure the reset time is enough
182 esp_rom_delay_us(10);
183 int clock_resolution_khz = 0;
184 spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
185 // TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
186 // 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
187 // clock_resolution between 2.2MHz to 2.8MHz is supported
188 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,
189 TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
190
191 spi_strip->bytes_per_pixel = bytes_per_pixel;
192 spi_strip->strip_len = led_config->max_leds;
196 spi_strip->base.clear = led_strip_spi_clear;
197 spi_strip->base.del = led_strip_spi_del;
198
199 *ret_strip = &spi_strip->base;
200 return ESP_OK;
201err:
202 if (spi_strip) {
203 if (spi_strip->spi_device) {
204 spi_bus_remove_device(spi_strip->spi_device);
205 }
206 if (spi_strip->spi_host) {
207 spi_bus_free(spi_strip->spi_host);
208 }
209 free(spi_strip);
210 }
211 return ret;
212}
#define ESP_RETURN_ON_ERROR(x, log_tag, format,...)
#define ESP_GOTO_ON_ERROR(x, goto_tag, log_tag, format,...)
int esp_err_t
Definition esp_err.h:21
#define ESP_OK
Definition esp_err.h:23
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
static 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)
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
#define SPI_BITS_PER_COLOR_BYTE
#define SPI_BYTES_PER_COLOR_BYTE
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.
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
static esp_err_t led_strip_spi_del(led_strip_t *strip)
#define LED_STRIP_SPI_DEFAULT_RESOLUTION
@ LED_PIXEL_FORMAT_GRB
@ LED_PIXEL_FORMAT_INVALID
@ LED_PIXEL_FORMAT_GRBW
struct led_strip_t * led_strip_handle_t
LED strip handle.
static const char * TAG
Definition main/main.c:31
LED Strip Configuration.
led_pixel_format_t led_pixel_format
struct led_strip_config_t::@145177150267040163336221331365072267202117177200 flags
LED Strip SPI specific configuration.
spi_host_device_t spi_bus
struct led_strip_spi_config_t::@242107237043306072174300312177366152113164360267 flags
spi_clock_source_t clk_src
spi_device_handle_t spi_device
spi_host_device_t spi_host
LED strip interface definition.
esp_err_t(* refresh)(led_strip_t *strip)
Refresh memory colors to LEDs.
esp_err_t(* set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
Set RGB for a specific pixel.
esp_err_t(* del)(led_strip_t *strip)
Free LED strip resources.
esp_err_t(* set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
Set RGBW for a specific pixel. Similar to set_pixel but also set the white component.
esp_err_t(* clear)(led_strip_t *strip)
Clear LED strip (turn off all LEDs).
static float data[128 *2]
Definition test_fft2r.c:34