Skip to content

Commit

Permalink
feat(led_strip): Support RGB pixel order
Browse files Browse the repository at this point in the history
  • Loading branch information
Kainarx committed Aug 15, 2024
1 parent d9b31d9 commit a77d1c5
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 56 deletions.
2 changes: 1 addition & 1 deletion led_strip/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion led_strip/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
43 changes: 43 additions & 0 deletions led_strip/include/esp_private/led_strip_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include <stdint.h>
#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
2 changes: 2 additions & 0 deletions led_strip/include/led_strip_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
37 changes: 37 additions & 0 deletions led_strip/src/led_strip_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <stdbool.h>
#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;
}
38 changes: 19 additions & 19 deletions led_strip/src/led_strip_rmt_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
26 changes: 11 additions & 15 deletions led_strip/src/led_strip_rmt_dev_idf4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down
40 changes: 20 additions & 20 deletions led_strip/src/led_strip_spi_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit a77d1c5

Please sign in to comment.