141 lines
5.0 KiB
C
141 lines
5.0 KiB
C
|
/*
|
||
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
|
||
|
#include "led_strip_encoder.h"
|
||
|
|
||
|
#include "esp_check.h"
|
||
|
|
||
|
static const char *TAG = "led_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");
|
||
|
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;
|
||
|
// different led strip might have its own timing requirements, following
|
||
|
// parameter is for WS2812
|
||
|
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||
|
.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
|
||
|
};
|
||
|
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 * 50 /
|
||
|
2; // reset code duration defaults to 50us
|
||
|
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;
|
||
|
}
|