From a77d1c51b90b14291683f803b94a484cd54ba690 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Tue, 6 Aug 2024 11:09:33 +0800 Subject: [PATCH] feat(led_strip): Support RGB pixel order --- led_strip/CMakeLists.txt | 2 +- led_strip/idf_component.yml | 2 +- .../include/esp_private/led_strip_common.h | 43 +++++++++++++++++++ led_strip/include/led_strip_types.h | 2 + led_strip/src/led_strip_common.c | 37 ++++++++++++++++ led_strip/src/led_strip_rmt_dev.c | 38 ++++++++-------- led_strip/src/led_strip_rmt_dev_idf4.c | 26 +++++------ led_strip/src/led_strip_spi_dev.c | 40 ++++++++--------- 8 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 led_strip/include/esp_private/led_strip_common.h create mode 100644 led_strip/src/led_strip_common.c diff --git a/led_strip/CMakeLists.txt b/led_strip/CMakeLists.txt index 37950cf014..cdc423ac03 100644 --- a/led_strip/CMakeLists.txt +++ b/led_strip/CMakeLists.txt @@ -1,6 +1,6 @@ include($ENV{IDF_PATH}/tools/cmake/version.cmake) -set(srcs "src/led_strip_api.c") +set(srcs "src/led_strip_api.c" "src/led_strip_common.c") if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0") if(CONFIG_SOC_RMT_SUPPORTED) diff --git a/led_strip/idf_component.yml b/led_strip/idf_component.yml index deda1bc743..4928449549 100644 --- a/led_strip/idf_component.yml +++ b/led_strip/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.5.4" +version: "2.5.5" description: Driver for Addressable LED Strip (WS2812, etc) url: https://github.com/espressif/idf-extra-components/tree/master/led_strip dependencies: diff --git a/led_strip/include/esp_private/led_strip_common.h b/led_strip/include/esp_private/led_strip_common.h new file mode 100644 index 0000000000..96ecb8d1da --- /dev/null +++ b/led_strip/include/esp_private/led_strip_common.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "led_strip_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LED_PIXEL_FORMAT_3COLORS_MAX LED_PIXEL_FORMAT_RGB + +/** + * @brief LED strip pixel order index + */ +typedef enum { + LED_PIXEL_INDEX_RED, /*!< Red pixel index */ + LED_PIXEL_INDEX_GREEN, /*!< Green pixel index */ + LED_PIXEL_INDEX_BLUE, /*!< Blue pixel index */ + LED_PIXEL_INDEX_WHITE, /*!< White pixel index */ + LED_PIXEL_INDEX_MAX /*!< Max pixel index */ +} led_pixel_order_index_t; + +/** + * @brief Config LED pixel order + * + * @param led_pixel_offset Each pixel's offset + * @param led_pixel_format Input LED strip pixel format + * @return + * - ESP_OK: Config LED pixel order successfully + * - ESP_ERR_INVALID_ARG: Config LED pixel order failed because of invalid argument + * - ESP_FAIL: Config LED pixel order failed because some other error + */ +esp_err_t led_strip_config_pixel_order(uint8_t *led_pixel_offset, led_pixel_format_t led_pixel_format); + +#ifdef __cplusplus +} +#endif diff --git a/led_strip/include/led_strip_types.h b/led_strip/include/led_strip_types.h index 691f0bc394..f439dbef89 100644 --- a/led_strip/include/led_strip_types.h +++ b/led_strip/include/led_strip_types.h @@ -16,7 +16,9 @@ extern "C" { */ typedef enum { LED_PIXEL_FORMAT_GRB, /*!< Pixel format: GRB */ + LED_PIXEL_FORMAT_RGB, /*!< Pixel format: RGB */ LED_PIXEL_FORMAT_GRBW, /*!< Pixel format: GRBW */ + LED_PIXEL_FORMAT_RGBW, /*!< Pixel format: RGBW */ LED_PIXEL_FORMAT_INVALID /*!< Invalid pixel format */ } led_pixel_format_t; diff --git a/led_strip/src/led_strip_common.c b/led_strip/src/led_strip_common.c new file mode 100644 index 0000000000..c7311d8d22 --- /dev/null +++ b/led_strip/src/led_strip_common.c @@ -0,0 +1,37 @@ +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_private/led_strip_common.h" + +static const char *TAG = "led_strip_common"; +esp_err_t led_strip_config_pixel_order(uint8_t *led_pixel_offset, led_pixel_format_t led_pixel_format) +{ + ESP_RETURN_ON_FALSE(led_pixel_offset, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + switch (led_pixel_format) { + case LED_PIXEL_FORMAT_GRB: + led_pixel_offset[0] = 1; + led_pixel_offset[1] = 0; + led_pixel_offset[2] = 2; + break; + case LED_PIXEL_FORMAT_RGB: + led_pixel_offset[0] = 0; + led_pixel_offset[1] = 1; + led_pixel_offset[2] = 2; + break; + case LED_PIXEL_FORMAT_GRBW: + led_pixel_offset[0] = 0; + led_pixel_offset[1] = 2; + led_pixel_offset[2] = 1; + led_pixel_offset[3] = 3; + break; + case LED_PIXEL_FORMAT_RGBW: + led_pixel_offset[0] = 0; + led_pixel_offset[1] = 1; + led_pixel_offset[2] = 2; + led_pixel_offset[3] = 3; + break; + default: + ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid pixel format"); + } + return ESP_OK; +} diff --git a/led_strip/src/led_strip_rmt_dev.c b/led_strip/src/led_strip_rmt_dev.c index 1cbf0e45ae..702ec617c4 100644 --- a/led_strip/src/led_strip_rmt_dev.c +++ b/led_strip/src/led_strip_rmt_dev.c @@ -12,6 +12,7 @@ #include "led_strip.h" #include "led_strip_interface.h" #include "led_strip_rmt_encoder.h" +#include "esp_private/led_strip_common.h" #define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution #define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4 @@ -30,6 +31,7 @@ typedef struct { rmt_encoder_handle_t strip_encoder; uint32_t strip_len; uint8_t bytes_per_pixel; + uint8_t led_pixel_offset[LED_PIXEL_INDEX_MAX]; uint8_t pixel_buf[]; } led_strip_rmt_obj; @@ -38,12 +40,15 @@ static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uin 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; + uint8_t *pixel_buf = rmt_strip->pixel_buf; + uint8_t *offset = rmt_strip->led_pixel_offset; + // Support all kinds of pixel order + pixel_buf[start + offset[LED_PIXEL_INDEX_RED]] = red & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_GREEN]] = green & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_BLUE]] = blue & 0xFF; + if (rmt_strip->bytes_per_pixel > 3) { - rmt_strip->pixel_buf[start + 3] = 0; + pixel_buf[start + offset[LED_PIXEL_INDEX_WHITE]] = 0; } return ESP_OK; } @@ -53,12 +58,14 @@ static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index 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; + uint32_t start = index * rmt_strip->bytes_per_pixel; + uint8_t *pixel_buf = rmt_strip->pixel_buf; + uint8_t *offset = rmt_strip->led_pixel_offset; // SK6812 component order is GRBW - *buf_start = green & 0xFF; - *++buf_start = red & 0xFF; - *++buf_start = blue & 0xFF; - *++buf_start = white & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_RED]] = red & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_GREEN]] = green & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_BLUE]] = blue & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_WHITE]] = white & 0xFF; return ESP_OK; } @@ -100,14 +107,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l 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); - } + uint8_t bytes_per_pixel = led_config->led_pixel_format <= LED_PIXEL_FORMAT_3COLORS_MAX ? 3 : 4; 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; @@ -138,7 +138,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l .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"); - + ESP_GOTO_ON_ERROR(led_strip_config_pixel_order(rmt_strip->led_pixel_offset, led_config->led_pixel_format), err, TAG, "config pixel order failed"); rmt_strip->bytes_per_pixel = bytes_per_pixel; rmt_strip->strip_len = led_config->max_leds; diff --git a/led_strip/src/led_strip_rmt_dev_idf4.c b/led_strip/src/led_strip_rmt_dev_idf4.c index a1067cd7c4..a6d67d76a7 100644 --- a/led_strip/src/led_strip_rmt_dev_idf4.c +++ b/led_strip/src/led_strip_rmt_dev_idf4.c @@ -11,6 +11,7 @@ #include "driver/rmt.h" #include "led_strip.h" #include "led_strip_interface.h" +#include "esp_private/led_strip_common.h" static const char *TAG = "led_strip_rmt"; @@ -43,6 +44,7 @@ typedef struct { rmt_channel_t rmt_channel; uint32_t strip_len; uint8_t bytes_per_pixel; + uint8_t led_pixel_offset[LED_PIXEL_INDEX_MAX]; uint8_t buffer[0]; } led_strip_rmt_obj; @@ -83,12 +85,14 @@ static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uin 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; + uint8_t *pixel_buf = rmt_strip->buffer; + uint8_t *offset = rmt_strip->led_pixel_offset; + // Support all kinds of pixel order + pixel_buf[start + offset[LED_PIXEL_INDEX_RED]] = red & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_GREEN]] = green & 0xFF; + pixel_buf[start + offset[LED_PIXEL_INDEX_BLUE]] = blue & 0xFF; if (rmt_strip->bytes_per_pixel > 3) { - rmt_strip->buffer[start + 3] = 0; + rmt_strip->buffer[start + offset[LED_PIXEL_INDEX_WHITE]] = 0; } return ESP_OK; } @@ -125,16 +129,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l 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); - } - + uint8_t bytes_per_pixel = led_config->led_pixel_format <= LED_PIXEL_FORMAT_3COLORS_MAX ? 3 : 4; // 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"); @@ -174,6 +169,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l // adapter to translates the LES strip date frame into RMT symbols rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter); + ESP_GOTO_ON_ERROR(led_strip_config_pixel_order(rmt_strip->led_pixel_offset, led_config->led_pixel_format), err, TAG, "config pixel order failed"); rmt_strip->bytes_per_pixel = bytes_per_pixel; rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel; diff --git a/led_strip/src/led_strip_spi_dev.c b/led_strip/src/led_strip_spi_dev.c index 6c66d7e375..99dfba21a1 100644 --- a/led_strip/src/led_strip_spi_dev.c +++ b/led_strip/src/led_strip_spi_dev.c @@ -10,9 +10,10 @@ #include "esp_check.h" #include "esp_rom_gpio.h" #include "soc/spi_periph.h" +#include "hal/spi_hal.h" #include "led_strip.h" #include "led_strip_interface.h" -#include "hal/spi_hal.h" +#include "esp_private/led_strip_common.h" #define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution #define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4 @@ -28,6 +29,7 @@ typedef struct { spi_device_handle_t spi_device; uint32_t strip_len; uint8_t bytes_per_pixel; + uint8_t led_pixel_offset[LED_PIXEL_INDEX_MAX]; uint8_t pixel_buf[]; } led_strip_spi_obj; @@ -51,14 +53,16 @@ static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uin { 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) + // 3 pixels take 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]); + uint8_t *pixel_buf = spi_strip->pixel_buf; + uint8_t *offset = spi_strip->led_pixel_offset; + memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE); + __led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_RED]]); + __led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_GREEN]]); + __led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_BLUE]]); if (spi_strip->bytes_per_pixel > 3) { - __led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]); + __led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * LED_PIXEL_INDEX_WHITE]); } return ESP_OK; } @@ -70,12 +74,14 @@ static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index 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; + uint8_t *pixel_buf = spi_strip->pixel_buf; + uint8_t *offset = spi_strip->led_pixel_offset; // 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]); + memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE); + __led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_RED]]); + __led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_GREEN]]); + __led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_BLUE]]); + __led_strip_spi_bit(white, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * offset[LED_PIXEL_INDEX_WHITE]]); return ESP_OK; } @@ -125,14 +131,7 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l 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); - } + uint8_t bytes_per_pixel = led_config->led_pixel_format <= LED_PIXEL_FORMAT_3COLORS_MAX ? 3 : 4; uint32_t mem_caps = MALLOC_CAP_DEFAULT; if (spi_config->flags.with_dma) { // DMA buffer must be placed in internal SRAM @@ -186,6 +185,7 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l // 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); + ESP_GOTO_ON_ERROR(led_strip_config_pixel_order(spi_strip->led_pixel_offset, led_config->led_pixel_format), err, TAG, "config pixel order failed"); spi_strip->bytes_per_pixel = bytes_per_pixel; spi_strip->strip_len = led_config->max_leds;