Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for WS2811 timing (IEC-194) #402

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -17,6 +17,7 @@ extern "C" {
typedef enum {
LED_PIXEL_FORMAT_GRB, /*!< Pixel format: GRB */
LED_PIXEL_FORMAT_GRBW, /*!< Pixel format: GRBW */
LED_PIXEL_FORMAT_RGB, /*!< Pixel format: RGB */
LED_PIXEL_FORMAT_INVALID /*!< Invalid pixel format */
} led_pixel_format_t;

Expand All @@ -27,6 +28,7 @@ typedef enum {
typedef enum {
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
LED_MODEL_INVALID /*!< Invalid LED strip model */
} led_model_t;

Expand Down
40 changes: 28 additions & 12 deletions led_strip/src/led_strip_rmt_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef struct {
rmt_encoder_handle_t strip_encoder;
uint32_t strip_len;
uint8_t bytes_per_pixel;
led_pixel_format_t led_pixel_format;
uint8_t pixel_buf[];
} led_strip_rmt_obj;

Expand All @@ -38,12 +39,22 @@ 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;
if (rmt_strip->bytes_per_pixel > 3) {
rmt_strip->pixel_buf[start + 3] = 0;
switch(rmt_strip->led_pixel_format) {
case LED_PIXEL_FORMAT_GRBW:
rmt_strip->pixel_buf[start + 3] = 0;
// fall through
case LED_PIXEL_FORMAT_GRB:
rmt_strip->pixel_buf[start + 0] = green & 0xFF;
rmt_strip->pixel_buf[start + 1] = red & 0xFF;
rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
break;
case LED_PIXEL_FORMAT_RGB:
rmt_strip->pixel_buf[start + 0] = red & 0xFF;
rmt_strip->pixel_buf[start + 1] = green & 0xFF;
rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
break;
default:
assert(false);
}
return ESP_OK;
}
Expand Down Expand Up @@ -101,12 +112,16 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l
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);
switch(led_config->led_pixel_format) {
case LED_PIXEL_FORMAT_GRBW:
bytes_per_pixel = 4;
break;
case LED_PIXEL_FORMAT_GRB:
case LED_PIXEL_FORMAT_RGB:
bytes_per_pixel = 3;
break;
default:
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");
Expand Down Expand Up @@ -141,6 +156,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l


rmt_strip->bytes_per_pixel = bytes_per_pixel;
rmt_strip->led_pixel_format = led_config->led_pixel_format;
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;
Expand Down
20 changes: 19 additions & 1 deletion led_strip/src/led_strip_rmt_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rm
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;
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accomodate WS2812B-V5
if (config->led_model == LED_MODEL_SK6812) {
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
.bit0 = {
Expand Down Expand Up @@ -116,14 +117,31 @@ esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rm
},
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
};
} else if (config->led_model == LED_MODEL_WS2811) {
// different led strip might have its own timing requirements, following parameter is for WS2811
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
.bit0 = {
.level0 = 1,
.duration0 = 0.5 * config->resolution / 1000000., // T0H=0.5us
.level1 = 0,
.duration1 = 2.0 * config->resolution / 1000000., // T0L=2.0us
},
.bit1 = {
.level0 = 1,
.duration0 = 1.2 * config->resolution / 1000000., // T1H=1.2us
.level1 = 0,
.duration1 = 1.3 * config->resolution / 1000000., // T1L=1.3us
},
.flags.msb_first = 1
};
reset_ticks = config->resolution / 1000000 * 50 / 2; // divide by 2... signal is sent twice
} 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(&copy_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,
Expand Down
40 changes: 29 additions & 11 deletions led_strip/src/led_strip_spi_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ typedef struct {
spi_device_handle_t spi_device;
uint32_t strip_len;
uint8_t bytes_per_pixel;
led_pixel_format_t led_pixel_format;
uint8_t pixel_buf[];
} led_strip_spi_obj;

Expand All @@ -54,12 +55,24 @@ static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uin
// 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]);
switch(spi_strip->led_pixel_format) {
case LED_PIXEL_FORMAT_GRBW:
__led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
// fall through
case LED_PIXEL_FORMAT_GRB:
__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]);
break;
case LED_PIXEL_FORMAT_RGB:
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start]);
__led_strip_spi_bit(green, &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]);
break;
default:
assert(false);
}

return ESP_OK;
}

Expand Down Expand Up @@ -126,12 +139,16 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l
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);
switch(led_config->led_pixel_format) {
case LED_PIXEL_FORMAT_GRBW:
bytes_per_pixel = 4;
break;
case LED_PIXEL_FORMAT_GRB:
case LED_PIXEL_FORMAT_RGB:
bytes_per_pixel = 3;
break;
default:
assert(false);
}
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
if (spi_config->flags.with_dma) {
Expand Down Expand Up @@ -188,6 +205,7 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);

spi_strip->bytes_per_pixel = bytes_per_pixel;
spi_strip->led_pixel_format = led_config->led_pixel_format;
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;
Expand Down