Battery-Emulator/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c
2025-08-16 18:11:52 +03:00

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(&copy_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));
}