mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 09:49:32 +02:00
157 lines
No EOL
5.8 KiB
C
157 lines
No EOL
5.8 KiB
C
#include <Arduino.h>
|
|
#include "driver/rmt_tx.h"
|
|
|
|
// WS2812 timing parameters (in nanoseconds)
|
|
#define WS2812_T0H_NS 350
|
|
#define WS2812_T0L_NS 1000
|
|
#define WS2812_T1H_NS 900
|
|
#define WS2812_T1L_NS 350
|
|
#define WS2812_RESET_US 280 // Recommended reset time
|
|
|
|
static rmt_channel_handle_t led_chan = NULL;
|
|
static rmt_encoder_handle_t led_encoder = NULL;
|
|
|
|
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;
|
|
|
|
// Encoder function prototypes
|
|
static size_t IRAM_ATTR 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);
|
|
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder);
|
|
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder);
|
|
|
|
// Encoder implementation
|
|
static size_t IRAM_ATTR 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;
|
|
}
|
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
|
state |= RMT_ENCODING_MEM_FULL;
|
|
goto out;
|
|
}
|
|
// 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;
|
|
state |= RMT_ENCODING_COMPLETE;
|
|
}
|
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
|
state |= RMT_ENCODING_MEM_FULL;
|
|
goto out;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) {
|
|
esp_err_t ret = ESP_OK;
|
|
rmt_led_strip_encoder_t *led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
|
if (!led_encoder) return ESP_ERR_NO_MEM;
|
|
|
|
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;
|
|
|
|
// Bytes encoder configuration
|
|
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
|
.bit0 = {
|
|
.level0 = 1,
|
|
.duration0 = (WS2812_T0H_NS + 50) / 100, // T0H in ticks (100ns resolution)
|
|
.level1 = 0,
|
|
.duration1 = (WS2812_T0L_NS + 50) / 100, // T0L in ticks
|
|
},
|
|
.bit1 = {
|
|
.level0 = 1,
|
|
.duration0 = (WS2812_T1H_NS + 50) / 100, // T1H in ticks
|
|
.level1 = 0,
|
|
.duration1 = (WS2812_T1L_NS + 50) / 100, // T1L in ticks
|
|
},
|
|
.flags.msb_first = 1 // WS2812 transfer bit order
|
|
};
|
|
if ((ret = rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder)) != ESP_OK) {
|
|
goto err;
|
|
}
|
|
|
|
rmt_copy_encoder_config_t copy_encoder_config = {};
|
|
if ((ret = rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder)) != ESP_OK) {
|
|
goto err;
|
|
}
|
|
|
|
// Reset code (280us of low signal)
|
|
led_encoder->reset_code = (rmt_symbol_word_t) {
|
|
.level0 = 0,
|
|
.duration0 = (WS2812_RESET_US * 1000) / 100, // Convert µs to ticks
|
|
.level1 = 0,
|
|
.duration1 = 0,
|
|
};
|
|
|
|
*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;
|
|
}
|
|
|
|
void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) {
|
|
if (led_chan == NULL) {
|
|
rmt_tx_channel_config_t tx_chan_config = {
|
|
.clk_src = RMT_CLK_SRC_DEFAULT,
|
|
.gpio_num = pin,
|
|
.mem_block_symbols = 64,
|
|
.resolution_hz = 10 * 1000 * 1000, // 10MHz, 1 tick = 100ns
|
|
.trans_queue_depth = 4,
|
|
};
|
|
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan));
|
|
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&led_encoder));
|
|
ESP_ERROR_CHECK(rmt_enable(led_chan));
|
|
}
|
|
|
|
rmt_transmit_config_t tx_config = {
|
|
.loop_count = 0,
|
|
};
|
|
ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config));
|
|
ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
|
|
} |