mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Working implementationN
This commit is contained in:
parent
9a898b72e2
commit
f4418672ba
1 changed files with 146 additions and 89 deletions
|
@ -1,100 +1,157 @@
|
||||||
// Implements the RMT peripheral on Espressif SoCs
|
|
||||||
// Copyright (c) 2020 Lucian Copeland for Adafruit Industries
|
|
||||||
|
|
||||||
/* Uses code from Espressif RGB LED Strip demo and drivers
|
|
||||||
* Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "driver/rmt.h"
|
#include "driver/rmt_tx.h"
|
||||||
|
|
||||||
// This code is adapted from the ESP-IDF v3.4 RMT "led_strip" example, altered
|
// WS2812 timing parameters (in nanoseconds)
|
||||||
// to work with the Arduino version of the ESP-IDF (3.2)
|
#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
|
||||||
|
|
||||||
#define WS2812_T0H_NS (400)
|
static rmt_channel_handle_t led_chan = NULL;
|
||||||
#define WS2812_T0L_NS (850)
|
static rmt_encoder_handle_t led_encoder = NULL;
|
||||||
#define WS2812_T1H_NS (800)
|
|
||||||
#define WS2812_T1L_NS (450)
|
|
||||||
|
|
||||||
static uint32_t t0h_ticks = 0;
|
typedef struct {
|
||||||
static uint32_t t1h_ticks = 0;
|
rmt_encoder_t base;
|
||||||
static uint32_t t0l_ticks = 0;
|
rmt_encoder_t *bytes_encoder;
|
||||||
static uint32_t t1l_ticks = 0;
|
rmt_encoder_t *copy_encoder;
|
||||||
|
int state;
|
||||||
|
rmt_symbol_word_t reset_code;
|
||||||
|
} rmt_led_strip_encoder_t;
|
||||||
|
|
||||||
static void IRAM_ATTR ws2812_rmt_adapter(const void* src, rmt_item32_t* dest, size_t src_size, size_t wanted_num,
|
// Encoder function prototypes
|
||||||
size_t* translated_size, size_t* item_num) {
|
static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
|
||||||
if (src == NULL || dest == NULL) {
|
const void *primary_data, size_t data_size,
|
||||||
*translated_size = 0;
|
rmt_encode_state_t *ret_state);
|
||||||
*item_num = 0;
|
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder);
|
||||||
return;
|
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder);
|
||||||
}
|
|
||||||
const rmt_item32_t bit0 = {{{t0h_ticks, 1, t0l_ticks, 0}}}; //Logical 0
|
// Encoder implementation
|
||||||
const rmt_item32_t bit1 = {{{t1h_ticks, 1, t1l_ticks, 0}}}; //Logical 1
|
static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
|
||||||
size_t size = 0;
|
const void *primary_data, size_t data_size,
|
||||||
size_t num = 0;
|
rmt_encode_state_t *ret_state) {
|
||||||
uint8_t* psrc = (uint8_t*)src;
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
rmt_item32_t* pdest = dest;
|
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||||
while (size < src_size && num < wanted_num) {
|
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||||
for (int i = 0; i < 8; i++) {
|
rmt_encode_state_t session_state = 0;
|
||||||
// MSB first
|
rmt_encode_state_t state = 0;
|
||||||
if (*psrc & (1 << (7 - i))) {
|
size_t encoded_symbols = 0;
|
||||||
pdest->val = bit1.val;
|
|
||||||
} else {
|
switch (led_encoder->state) {
|
||||||
pdest->val = bit0.val;
|
case 0: // send RGB data
|
||||||
}
|
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||||
num++;
|
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||||
pdest++;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
size++;
|
out:
|
||||||
psrc++;
|
*ret_state = state;
|
||||||
}
|
return encoded_symbols;
|
||||||
*translated_size = size;
|
|
||||||
*item_num = num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool rmt_initialized = false;
|
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) {
|
void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) {
|
||||||
if (rmt_initialized == false) {
|
if (led_chan == NULL) {
|
||||||
// Reserve channel
|
rmt_tx_channel_config_t tx_chan_config = {
|
||||||
rmt_channel_t channel = 0;
|
.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_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel);
|
rmt_transmit_config_t tx_config = {
|
||||||
config.clk_div = 2;
|
.loop_count = 0,
|
||||||
|
};
|
||||||
rmt_config(&config);
|
ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config));
|
||||||
rmt_driver_install(config.channel, 0, 0);
|
ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
|
||||||
|
}
|
||||||
// Convert NS timings to ticks
|
|
||||||
uint32_t counter_clk_hz = 0;
|
|
||||||
|
|
||||||
rmt_get_counter_clock(channel, &counter_clk_hz);
|
|
||||||
|
|
||||||
// NS to tick converter
|
|
||||||
float ratio = (float)counter_clk_hz / 1e9;
|
|
||||||
|
|
||||||
t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
|
|
||||||
t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
|
|
||||||
t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
|
|
||||||
t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
|
|
||||||
|
|
||||||
// Initialize automatic timing translator
|
|
||||||
rmt_translator_init(0, ws2812_rmt_adapter);
|
|
||||||
rmt_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write and wait to finish
|
|
||||||
rmt_write_sample(0, pixels, (size_t)numBytes, false);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue