Blink Working
This commit is contained in:
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip";
|
||||
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
|
||||
uint32_t rgb_max = value;
|
||||
uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f;
|
||||
|
||||
uint32_t i = hue / 60;
|
||||
uint32_t diff = hue % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
red = rgb_max;
|
||||
green = rgb_min + rgb_adj;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
red = rgb_max - rgb_adj;
|
||||
green = rgb_max;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
red = rgb_min;
|
||||
green = rgb_max;
|
||||
blue = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
red = rgb_min;
|
||||
green = rgb_max - rgb_adj;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
red = rgb_min + rgb_adj;
|
||||
green = rgb_min;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
default:
|
||||
red = rgb_max;
|
||||
green = rgb_min;
|
||||
blue = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->refresh(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->clear(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->del(strip);
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||
#endif
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_handle_t rmt_chan;
|
||||
rmt_encoder_handle_t strip_encoder;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
// In thr order of GRB, as LED strip like WS2812 sends out pixels in this order
|
||||
rmt_strip->pixel_buf[start + 0] = green & 0xFF;
|
||||
rmt_strip->pixel_buf[start + 1] = red & 0xFF;
|
||||
rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
|
||||
if (rmt_strip->bytes_per_pixel > 3) {
|
||||
rmt_strip->pixel_buf[start + 3] = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||
uint8_t *buf_start = rmt_strip->pixel_buf + index * 4;
|
||||
// SK6812 component order is GRBW
|
||||
*buf_start = green & 0xFF;
|
||||
*++buf_start = red & 0xFF;
|
||||
*++buf_start = blue & 0xFF;
|
||||
*++buf_start = white & 0xFF;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
rmt_transmit_config_t tx_conf = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all leds
|
||||
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
if (rmt_config->clk_src) {
|
||||
clk_src = rmt_config->clk_src;
|
||||
}
|
||||
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
// override the default value if the user sets it
|
||||
if (rmt_config->mem_block_symbols) {
|
||||
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||
}
|
||||
rmt_tx_channel_config_t rmt_chan_config = {
|
||||
.clk_src = clk_src,
|
||||
.gpio_num = led_config->strip_gpio_num,
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.resolution_hz = resolution,
|
||||
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
.flags.with_dma = rmt_config->flags.with_dma,
|
||||
.flags.invert_out = led_config->flags.invert_out,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||
|
||||
led_strip_encoder_config_t strip_encoder_conf = {
|
||||
.resolution = resolution,
|
||||
.led_model = led_config->led_model
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||
|
||||
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
if (rmt_strip->rmt_chan) {
|
||||
rmt_del_channel(rmt_strip->rmt_chan);
|
||||
}
|
||||
if (rmt_strip->strip_encoder) {
|
||||
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||
}
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
#define WS2812_T0H_NS (300)
|
||||
#define WS2812_T0L_NS (900)
|
||||
#define WS2812_T1H_NS (900)
|
||||
#define WS2812_T1L_NS (300)
|
||||
|
||||
#define SK6812_T0H_NS (300)
|
||||
#define SK6812_T0L_NS (900)
|
||||
#define SK6812_T1H_NS (600)
|
||||
#define SK6812_T1L_NS (600)
|
||||
|
||||
#define LED_STRIP_RESET_MS (10)
|
||||
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||
#endif
|
||||
|
||||
static uint32_t led_t0h_ticks = 0;
|
||||
static uint32_t led_t1h_ticks = 0;
|
||||
static uint32_t led_t0l_ticks = 0;
|
||||
static uint32_t led_t1l_ticks = 0;
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_t rmt_channel;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t buffer[0];
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static void IRAM_ATTR 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)
|
||||
{
|
||||
if (src == NULL || dest == NULL) {
|
||||
*translated_size = 0;
|
||||
*item_num = 0;
|
||||
return;
|
||||
}
|
||||
const rmt_item32_t bit0 = {{{ led_t0h_ticks, 1, led_t0l_ticks, 0 }}}; //Logical 0
|
||||
const rmt_item32_t bit1 = {{{ led_t1h_ticks, 1, led_t1l_ticks, 0 }}}; //Logical 1
|
||||
size_t size = 0;
|
||||
size_t num = 0;
|
||||
uint8_t *psrc = (uint8_t *)src;
|
||||
rmt_item32_t *pdest = dest;
|
||||
while (size < src_size && num < wanted_num) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// MSB first
|
||||
if (*psrc & (1 << (7 - i))) {
|
||||
pdest->val = bit1.val;
|
||||
} else {
|
||||
pdest->val = bit0.val;
|
||||
}
|
||||
num++;
|
||||
pdest++;
|
||||
}
|
||||
size++;
|
||||
psrc++;
|
||||
}
|
||||
*translated_size = size;
|
||||
*item_num = num;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of the maximum number of leds");
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
// In thr order of GRB
|
||||
rmt_strip->buffer[start + 0] = green & 0xFF;
|
||||
rmt_strip->buffer[start + 1] = red & 0xFF;
|
||||
rmt_strip->buffer[start + 2] = blue & 0xFF;
|
||||
if (rmt_strip->bytes_per_pixel > 3) {
|
||||
rmt_strip->buffer[start + 3] = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_write_sample(rmt_strip->rmt_channel, rmt_strip->buffer, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, true), TAG,
|
||||
"transmit RMT samples failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(LED_STRIP_RESET_MS));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all LEDs
|
||||
memset(rmt_strip->buffer, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_strip->rmt_channel), TAG, "uninstall RMT driver failed");
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *dev_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(led_config && dev_config && ret_strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid led_pixel_format");
|
||||
ESP_RETURN_ON_FALSE(dev_config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA is not supported");
|
||||
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// allocate memory for led_strip object
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||
ESP_RETURN_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, TAG, "request memory for les_strip failed");
|
||||
|
||||
// install RMT channel driver
|
||||
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(led_config->strip_gpio_num, dev_config->rmt_channel);
|
||||
// set the minimal clock division because the LED strip needs a high clock resolution
|
||||
config.clk_div = 2;
|
||||
|
||||
uint8_t mem_block_num = 2;
|
||||
// override the default value if the user specify the mem block size
|
||||
if (dev_config->mem_block_symbols) {
|
||||
mem_block_num = (dev_config->mem_block_symbols + LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS / 2) / LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
}
|
||||
config.mem_block_num = mem_block_num;
|
||||
|
||||
ESP_GOTO_ON_ERROR(rmt_config(&config), err, TAG, "RMT config failed");
|
||||
ESP_GOTO_ON_ERROR(rmt_driver_install(config.channel, 0, 0), err, TAG, "RMT install failed");
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
rmt_get_counter_clock((rmt_channel_t)dev_config->rmt_channel, &counter_clk_hz);
|
||||
// ns -> ticks
|
||||
float ratio = (float)counter_clk_hz / 1e9;
|
||||
if (led_config->led_model == LED_MODEL_WS2812) {
|
||||
led_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
|
||||
led_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
|
||||
led_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
|
||||
led_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
|
||||
} else if (led_config->led_model == LED_MODEL_SK6812) {
|
||||
led_t0h_ticks = (uint32_t)(ratio * SK6812_T0H_NS);
|
||||
led_t0l_ticks = (uint32_t)(ratio * SK6812_T0L_NS);
|
||||
led_t1h_ticks = (uint32_t)(ratio * SK6812_T1H_NS);
|
||||
led_t1l_ticks = (uint32_t)(ratio * SK6812_T1L_NS);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// adapter to translates the LES strip date frame into RMT symbols
|
||||
rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter);
|
||||
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
static const char *TAG = "led_rmt_encoder";
|
||||
|
||||
typedef struct {
|
||||
rmt_encoder_t base;
|
||||
rmt_encoder_t *bytes_encoder;
|
||||
rmt_encoder_t *copy_encoder;
|
||||
int state;
|
||||
rmt_symbol_word_t reset_code;
|
||||
} rmt_led_strip_encoder_t;
|
||||
|
||||
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||
rmt_encode_state_t session_state = 0;
|
||||
rmt_encode_state_t state = 0;
|
||||
size_t encoded_symbols = 0;
|
||||
switch (led_encoder->state) {
|
||||
case 0: // send RGB data
|
||||
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
// fall-through
|
||||
case 1: // send reset code
|
||||
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||
sizeof(led_encoder->reset_code), &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 0; // back to the initial encoding session
|
||||
state |= RMT_ENCODING_COMPLETE;
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
}
|
||||
out:
|
||||
*ret_state = state;
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
free(led_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||
led_encoder->state = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||
led_encoder->base.encode = rmt_encode_led_strip;
|
||||
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config;
|
||||
if (config->led_model == LED_MODEL_SK6812) {
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
|
||||
},
|
||||
.flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||
},
|
||||
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||
};
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accomodate WS2812B-V5
|
||||
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||
.level0 = 0,
|
||||
.duration0 = reset_ticks,
|
||||
.level1 = 0,
|
||||
.duration1 = reset_ticks,
|
||||
};
|
||||
*ret_encoder = &led_encoder->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (led_encoder) {
|
||||
if (led_encoder->bytes_encoder) {
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
}
|
||||
if (led_encoder->copy_encoder) {
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
}
|
||||
free(led_encoder);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of led strip encoder configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||
led_model_t led_model; /*!< LED model */
|
||||
} led_strip_encoder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
|
||||
*
|
||||
* @param[in] config Encoder configuration
|
||||
* @param[out] ret_encoder Returned encoder handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||
* - ESP_OK if creating encoder successfully
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "hal/spi_hal.h"
|
||||
|
||||
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
|
||||
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
|
||||
#define SPI_BYTES_PER_COLOR_BYTE 3
|
||||
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
|
||||
|
||||
static const char *TAG = "led_strip_spi";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
spi_host_device_t spi_host;
|
||||
spi_device_handle_t spi_device;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_spi_obj;
|
||||
|
||||
// please make sure to zero-initialize the buf before calling this function
|
||||
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
|
||||
{
|
||||
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
|
||||
// So a color byte occupies 3 bytes of SPI.
|
||||
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
|
||||
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
|
||||
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
|
||||
*(buf + 1) |= BIT(0);
|
||||
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
|
||||
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
|
||||
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
|
||||
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
|
||||
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
// LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||
if (spi_strip->bytes_per_pixel > 3) {
|
||||
__led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
// SK6812 component order is GRBW
|
||||
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||
__led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
spi_transaction_t tx_conf;
|
||||
memset(&tx_conf, 0, sizeof(tx_conf));
|
||||
|
||||
tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
|
||||
tx_conf.tx_buffer = spi_strip->pixel_buf;
|
||||
tx_conf.rx_buffer = NULL;
|
||||
ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
//Write zero to turn off all leds
|
||||
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
uint8_t *buf = spi_strip->pixel_buf;
|
||||
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
|
||||
__led_strip_spi_bit(0, buf);
|
||||
buf += SPI_BYTES_PER_COLOR_BYTE;
|
||||
}
|
||||
|
||||
return led_strip_spi_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
|
||||
|
||||
free(spi_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
|
||||
if (spi_config->flags.with_dma) {
|
||||
// DMA buffer must be placed in internal SRAM
|
||||
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||
}
|
||||
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);
|
||||
|
||||
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
|
||||
|
||||
spi_strip->spi_host = spi_config->spi_bus;
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
|
||||
if (spi_config->clk_src) {
|
||||
clk_src = spi_config->clk_src;
|
||||
}
|
||||
|
||||
spi_bus_config_t spi_bus_cfg = {
|
||||
.mosi_io_num = led_config->strip_gpio_num,
|
||||
//Only use MOSI to generate the signal, set -1 when other pins are not used.
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
|
||||
};
|
||||
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");
|
||||
|
||||
if (led_config->flags.invert_out == true) {
|
||||
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
|
||||
}
|
||||
|
||||
spi_device_interface_config_t spi_dev_cfg = {
|
||||
.clock_source = clk_src,
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
|
||||
.mode = 0,
|
||||
//set -1 when CS is not used
|
||||
.spics_io_num = -1,
|
||||
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
};
|
||||
|
||||
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");
|
||||
//ensure the reset time is enough
|
||||
esp_rom_delay_us(10);
|
||||
int clock_resolution_khz = 0;
|
||||
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
|
||||
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
|
||||
// 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
|
||||
// clock_resolution between 2.2MHz to 2.8MHz is supported
|
||||
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,
|
||||
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||
|
||||
spi_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
spi_strip->strip_len = led_config->max_leds;
|
||||
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
|
||||
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
|
||||
spi_strip->base.refresh = led_strip_spi_refresh;
|
||||
spi_strip->base.clear = led_strip_spi_clear;
|
||||
spi_strip->base.del = led_strip_spi_del;
|
||||
|
||||
*ret_strip = &spi_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (spi_strip) {
|
||||
if (spi_strip->spi_device) {
|
||||
spi_bus_remove_device(spi_strip->spi_device);
|
||||
}
|
||||
if (spi_strip->spi_host) {
|
||||
spi_bus_free(spi_strip->spi_host);
|
||||
}
|
||||
free(spi_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user