Esp32 i2s x (#630)

ESP32 x8 and X16 parallel support

---------

Co-authored-by: Awawa <69086569+awawa-dev@users.noreply.github.com>
This commit is contained in:
Michael Miller
2023-01-27 10:12:48 -08:00
committed by GitHub
parent baf3b6b918
commit d3bc27b9f0
21 changed files with 1316 additions and 263 deletions

View File

@@ -0,0 +1,54 @@
//
// NeoPixel_ESP32_I2sParallel -
// This sketch demonstrates the use of the I2S Parallel method allowing upto 8 hardware updated channels
// This example only works on the ESP32
//
// The key part of the method name is Esp32I2s1X8,
// E2p32 (platform specific method),
// I2s Channel 1 (most commonly available),
// X8 (8 parallel channel mode)
//
// In this example, it demonstrates different ColorFeatures, Method specification, and count per strip
//
#include <NeoPixelBus.h>
// Demonstrating the use of the first four channels, but the method used allows for eight
NeoPixelBus<NeoBgrFeature, NeoEsp32I2s1X8Ws2811Method> strip1(120, 15); // note: older WS2811 and longer strip
NeoPixelBus<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod> strip2(100, 2); // note: modern WS2812 with letter like WS2812b
NeoPixelBus<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xInvertedMethod> strip3(100, 4); // note: inverted
NeoPixelBus<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method> strip4(50, 16); // note: RGBW and Sk6812 and smaller strip
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial attach
Serial.println();
Serial.println("Initializing...");
Serial.flush();
// must call begin on all the strips
strip1.Begin();
strip2.Begin();
strip3.Begin();
strip4.Begin();
Serial.println();
Serial.println("Running...");
}
void loop() {
delay(1000);
// draw on the strips
strip1.SetPixelColor(0, RgbColor(255, 0, 0)); // red
strip2.SetPixelColor(0, RgbColor(0, 127, 0)); // green
strip3.SetPixelColor(0, RgbColor(0, 0, 53)); // blue
strip4.SetPixelColor(0, RgbwColor(0, 0, 128, 255)); // white channel with a little blue
// show them,
// only on the last show, no matter the order, will the data be sent
strip1.Show();
strip2.Show();
strip3.Show();
strip4.Show();
}

View File

@@ -97,6 +97,7 @@ License along with NeoPixel. If not, see
#include "internal/NeoEsp32RmtMethod.h" #include "internal/NeoEsp32RmtMethod.h"
#include "internal/NeoEspBitBangMethod.h" #include "internal/NeoEspBitBangMethod.h"
#include "internal/DotStarEsp32DmaSpiMethod.h" #include "internal/DotStarEsp32DmaSpiMethod.h"
#include "internal/NeoEsp32I2sXMethod.h"
#elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__ #elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__
@@ -195,7 +196,7 @@ public:
void Show(bool maintainBufferConsistency = true) void Show(bool maintainBufferConsistency = true)
{ {
if (!IsDirty()) if (!IsDirty() && !_method.AlwaysUpdate())
{ {
return; return;
} }

View File

@@ -177,6 +177,12 @@ public:
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data + _sizeStartFrame; return _data + _sizeStartFrame;

View File

@@ -104,6 +104,12 @@ public:
_wire.endTransaction(); _wire.endTransaction();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -6,7 +6,6 @@
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
@@ -40,6 +39,7 @@
#include "esp_intr.h" #include "esp_intr.h"
#endif #endif
#include "rom/lldesc.h"
#include "soc/gpio_reg.h" #include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
@@ -60,55 +60,36 @@
#include "Esp32_i2s.h" #include "Esp32_i2s.h"
#include "esp32-hal.h" #include "esp32-hal.h"
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample);
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_mode, size_t bytes_per_sample);
#define MATRIX_DETACH_OUT_SIG 0x100
#if ESP_IDF_VERSION_MAJOR<=4 #if ESP_IDF_VERSION_MAJOR<=4
#define I2S_BASE_CLK (160000000L) #define I2S_BASE_CLK (160000000L)
#endif #endif
#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr)))) #define I2S_DMA_BLOCK_COUNT_DEFAULT 0
// 20 bytes gives us enough time if we use single stage idle
// But it can't be longer due to non-parrallel mode and 50us reset time
// there just isn't enough silence at the end to fill more than 20 bytes
#define I2S_DMA_SILENCE_SIZE 20 // 4 byte increments
#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front
#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel
#define I2S_DMA_BLOCK_COUNT_DEFAULT 16 typedef struct
// 24 bytes gives us enough time if we use single stage idle {
// with the two stage idle we can use the minimum of 4 bytes i2s_dev_t* bus;
#define I2S_DMA_SILENCE_SIZE 4*1 int8_t ws;
#define I2S_DMA_SILENCE_BLOCK_COUNT 3 // two front, one back int8_t bck;
#define I2S_DMA_QUEUE_COUNT 2 int8_t out;
int8_t in;
typedef struct i2s_dma_item_s { intr_handle_t isr_handle;
uint32_t blocksize: 12; // datalen lldesc_t* dma_items;
uint32_t datalen : 12; // len*(bits_per_sample/8)*2 => max 2047*8bit/1023*16bit samples size_t dma_count;
uint32_t unused : 5; // 0
uint32_t sub_sof : 1; // 0
uint32_t eof : 1; // 1 => last?
uint32_t owner : 1; // 1
void* data; // malloc(datalen) volatile uint32_t is_sending_data;
struct i2s_dma_item_s* next;
// if this pointer is not null, it will be freed
void* free_ptr;
// if DMA buffers are preallocated
uint8_t* buf;
} i2s_dma_item_t;
typedef struct {
i2s_dev_t* bus;
int8_t ws;
int8_t bck;
int8_t out;
int8_t in;
uint32_t rate;
intr_handle_t isr_handle;
xQueueHandle tx_queue;
uint8_t* silence_buf;
size_t silence_len;
i2s_dma_item_t* dma_items;
size_t dma_count;
uint32_t dma_buf_len :12;
uint32_t unused :20;
volatile uint32_t is_sending_data;
} i2s_bus_t; } i2s_bus_t;
// is_sending_data values // is_sending_data values
@@ -116,103 +97,144 @@ typedef struct {
#define I2s_Is_Pending 1 #define I2s_Is_Pending 1
#define I2s_Is_Sending 2 #define I2s_Is_Sending 2
static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_SIZE] = { 0 };
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (I2S_NUM_MAX == 2) // (I2S_NUM_MAX == 2)
static i2s_bus_t I2S[I2S_NUM_MAX] = { static i2s_bus_t I2S[I2S_NUM_MAX] =
{&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}, {
{&I2S1, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle} {&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle},
{&I2S1, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
}; };
#else #else
static i2s_bus_t I2S[I2S_NUM_MAX] = { static i2s_bus_t I2S[I2S_NUM_MAX] =
{&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle} {
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
}; };
#endif #endif
void IRAM_ATTR i2sDmaISR(void* arg); void IRAM_ATTR i2sDmaISR(void* arg);
inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldesc_t* itemNext)
{
item->eof = 0;
item->owner = 1;
item->sosf = 0;
item->offset = 0;
item->buf = posData;
item->size = sizeData;
item->length = sizeData;
item->qe.stqe_next = itemNext;
}
bool i2sInitDmaItems(uint8_t bus_num) { bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize)
if (bus_num >= I2S_NUM_MAX) { {
if (bus_num >= I2S_NUM_MAX)
{
return false; return false;
} }
if (I2S[bus_num].tx_queue) {// already set
return true;
}
size_t dmaCount = I2S[bus_num].dma_count; size_t dmaCount = I2S[bus_num].dma_count;
if (I2S[bus_num].dma_items == NULL) { if (I2S[bus_num].dma_items == NULL)
I2S[bus_num].dma_items = (i2s_dma_item_t*)heap_caps_malloc(dmaCount * sizeof(i2s_dma_item_t), MALLOC_CAP_DMA); {
if (I2S[bus_num].dma_items == NULL) { I2S[bus_num].dma_items = (lldesc_t*)heap_caps_malloc(dmaCount * sizeof(lldesc_t), MALLOC_CAP_DMA);
if (I2S[bus_num].dma_items == NULL)
{
log_e("MEM ERROR!"); log_e("MEM ERROR!");
return false; return false;
} }
} }
int i, i2; lldesc_t* itemFirst = &I2S[bus_num].dma_items[0];
i2s_dma_item_t* item = NULL; lldesc_t* item = itemFirst;
i2s_dma_item_t* itemPrev = NULL; // lldesc_t* itemsEnd = itemFirst + I2S[bus_num].dma_count;
lldesc_t* itemNext = item + 1;
size_t dataLeft = dataSize;
uint8_t* pos = data;
// at the end of the data is the encoded silence reset
uint8_t* posSilence = data + dataSize - I2S_DMA_SILENCE_SIZE;
for(i=0; i< dmaCount; i++) { // front two are silent items used for looping to micmic single fire
itemPrev = item; // default to looping
dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemNext);
dmaItemInit(itemNext, posSilence, I2S_DMA_SILENCE_SIZE, item);
item = itemNext;
itemNext++;
i2 = (i+1) % dmaCount; // init blocks with avialable data
item = &I2S[bus_num].dma_items[i]; //
item->eof = 0; while (dataLeft)
item->owner = 1; {
item->sub_sof = 0; item = itemNext;
item->unused = 0; itemNext++;
item->data = I2S[bus_num].silence_buf;
item->blocksize = I2S[bus_num].silence_len; size_t blockSize = dataLeft;
item->datalen = I2S[bus_num].silence_len; if (blockSize > I2S_DMA_MAX_DATA_LEN)
item->next = &I2S[bus_num].dma_items[i2]; {
item->free_ptr = NULL; blockSize = I2S_DMA_MAX_DATA_LEN;
item->buf = NULL; }
dataLeft -= blockSize;
dmaItemInit(item, pos, blockSize, itemNext);
pos += blockSize;
} }
itemPrev->eof = 1;
// last data item is EOF to manage send state using EOF ISR
item->eof = 1; item->eof = 1;
I2S[bus_num].tx_queue = xQueueCreate(I2S_DMA_QUEUE_COUNT, sizeof(i2s_dma_item_t*)); // last block, the back silent item, loops to front
if (I2S[bus_num].tx_queue == NULL) {// memory error item = itemNext;
log_e("MEM ERROR!"); dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemFirst);
heap_caps_free(I2S[bus_num].dma_items);
I2S[bus_num].dma_items = NULL;
return false;
}
return true; return true;
} }
bool i2sDeinitDmaItems(uint8_t bus_num) { bool i2sDeinitDmaItems(uint8_t bus_num)
if (bus_num >= I2S_NUM_MAX) { {
if (bus_num >= I2S_NUM_MAX)
{
return false; return false;
} }
if (!I2S[bus_num].tx_queue) {
return false; // nothing to deinit
}
vQueueDelete(I2S[bus_num].tx_queue);
I2S[bus_num].tx_queue = NULL;
heap_caps_free(I2S[bus_num].dma_items); heap_caps_free(I2S[bus_num].dma_items);
I2S[bus_num].dma_items = NULL; I2S[bus_num].dma_items = NULL;
return true; return true;
} }
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits) { // normal 4, 10, 63, 12, 16
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
esp_err_t i2sSetClock(uint8_t bus_num,
uint8_t div_num, // 4 13
uint8_t div_b, // 10 20
uint8_t div_a, // 63 63
uint8_t bck, // 12 60 or 7
uint8_t bits) // 16 8
{
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63)
{
return ESP_FAIL; return ESP_FAIL;
} }
//log_i("i2sSetClock bus %u, clkm_div_num %u, clk_div_a %u, clk_div_b %u, bck_div_num %u, bits_mod %u",
// bus_num,
// div_num,
// div_a,
// div_b,
// bck,
// bits);
i2s_dev_t* i2s = I2S[bus_num].bus; i2s_dev_t* i2s = I2S[bus_num].bus;
typeof(i2s->clkm_conf) clkm_conf; typeof(i2s->clkm_conf) clkm_conf;
clkm_conf.val = 0; clkm_conf.val = 0;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
clkm_conf.clka_en = 0; #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
clkm_conf.clk_sel = 2; // APPL = 1 APB = 2
clkm_conf.clk_en = 1; // examples of i2s show this being set if sel is set to 2
#else #else
clkm_conf.clk_sel = 2; clkm_conf.clka_en = 0;
#endif #endif
clkm_conf.clkm_div_a = div_a; clkm_conf.clkm_div_a = div_a;
@@ -227,50 +249,101 @@ esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t d
sample_rate_conf.tx_bits_mod = bits; sample_rate_conf.tx_bits_mod = bits;
sample_rate_conf.rx_bits_mod = bits; sample_rate_conf.rx_bits_mod = bits;
i2s->sample_rate_conf.val = sample_rate_conf.val; i2s->sample_rate_conf.val = sample_rate_conf.val;
return ESP_OK; return ESP_OK;
} }
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert) void i2sSetPins(uint8_t bus_num,
int8_t out,
int8_t parallel,
int8_t busSampleSize,
bool invert)
{ {
if (bus_num >= I2S_NUM_MAX) if (bus_num >= I2S_NUM_MAX)
{ {
return; return;
} }
int8_t outOld = I2S[bus_num].out;
I2S[bus_num].out = out;
// disable old pin
if (outOld >= 0)
{
gpio_matrix_out(outOld, 0x100, false, false);
pinMode(outOld, INPUT);
}
if (out >= 0) if (out >= 0)
{ {
uint32_t i2sSignal;
pinMode(out, OUTPUT); pinMode(out, OUTPUT);
int i2sSignal; #if defined(CONFIG_IDF_TARGET_ESP32S2)
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (I2S_NUM_MAX == 2) // S2 only has one bus
if (bus_num == 1) //
{ // in parallel mode
i2sSignal = I2S1O_DATA_OUT23_IDX; // 8bit mode : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
} // 16bit mode : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
else // 24bit mode : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
#endif if (parallel == -1)
{ {
i2sSignal = I2S0O_DATA_OUT23_IDX; i2sSignal = I2S0O_DATA_OUT23_IDX;
} }
else if (busSampleSize == 1)
{
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
}
else if (busSampleSize == 2)
{
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel;
}
else
{
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel;
}
#else
if (bus_num == 0)
{
// in parallel mode
// 0-7 bits : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
// 8-15 bits : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
// 16-23 bits : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
if (parallel == -1)
{
i2sSignal = I2S0O_DATA_OUT23_IDX;
}
else if (parallel < 8)
{
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
}
else if (parallel < 16)
{
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel - 8;
}
else
{
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel - 16;
}
}
else
{
if (parallel == -1)
{
i2sSignal = I2S1O_DATA_OUT23_IDX;
}
else
{
i2sSignal = I2S1O_DATA_OUT0_IDX + parallel;
}
}
#endif
//log_i("i2sSetPins bus %u, i2sSignal %u, pin %u, mux %u",
// bus_num,
// i2sSignal,
// out,
// parallel);
gpio_matrix_out(out, i2sSignal, invert, false); gpio_matrix_out(out, i2sSignal, invert, false);
} }
} }
bool i2sWriteDone(uint8_t bus_num) { bool i2sWriteDone(uint8_t bus_num)
if (bus_num >= I2S_NUM_MAX) { {
if (bus_num >= I2S_NUM_MAX)
{
return false; return false;
} }
@@ -278,28 +351,36 @@ bool i2sWriteDone(uint8_t bus_num) {
} }
void i2sInit(uint8_t bus_num, void i2sInit(uint8_t bus_num,
uint32_t bits_per_sample, bool parallel_mode,
size_t bytes_per_sample,
uint32_t sample_rate, uint32_t sample_rate,
i2s_tx_chan_mod_t chan_mod, i2s_tx_chan_mod_t chan_mod,
i2s_tx_fifo_mod_t fifo_mod, i2s_tx_fifo_mod_t fifo_mod,
size_t dma_count, size_t dma_count,
size_t dma_len) { uint8_t* data,
if (bus_num >= I2S_NUM_MAX) { size_t dataSize)
{
if (bus_num >= I2S_NUM_MAX)
{
return; return;
} }
I2S[bus_num].dma_count = dma_count + I2S_DMA_SILENCE_BLOCK_COUNT; // an extra two for looping silence I2S[bus_num].dma_count = dma_count +
I2S[bus_num].dma_buf_len = dma_len & 0xFFF; I2S_DMA_SILENCE_BLOCK_COUNT_FRONT +
I2S_DMA_SILENCE_BLOCK_COUNT_BACK;
if (!i2sInitDmaItems(bus_num)) { if (!i2sInitDmaItems(bus_num, data, dataSize))
{
return; return;
} }
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (I2S_NUM_MAX == 2) // (I2S_NUM_MAX == 2)
if (bus_num) { if (bus_num)
{
periph_module_enable(PERIPH_I2S1_MODULE); periph_module_enable(PERIPH_I2S1_MODULE);
} else }
else
#endif #endif
{ {
periph_module_enable(PERIPH_I2S0_MODULE); periph_module_enable(PERIPH_I2S0_MODULE);
@@ -313,12 +394,6 @@ void i2sInit(uint8_t bus_num,
i2s->int_clr.val = 0xFFFFFFFF; i2s->int_clr.val = 0xFFFFFFFF;
i2s->fifo_conf.dscr_en = 0; i2s->fifo_conf.dscr_en = 0;
// reset fifo
i2s->conf.rx_fifo_reset = 1;
i2s->conf.rx_fifo_reset = 0;
i2s->conf.tx_fifo_reset = 1;
i2s->conf.tx_fifo_reset = 0;
// reset i2s // reset i2s
i2s->conf.tx_reset = 1; i2s->conf.tx_reset = 1;
i2s->conf.tx_reset = 0; i2s->conf.tx_reset = 0;
@@ -331,11 +406,29 @@ void i2sInit(uint8_t bus_num,
i2s->lc_conf.out_rst = 1; i2s->lc_conf.out_rst = 1;
i2s->lc_conf.out_rst = 0; i2s->lc_conf.out_rst = 0;
// reset fifo
i2s->conf.rx_fifo_reset = 1;
i2s->conf.rx_fifo_reset = 0;
i2s->conf.tx_fifo_reset = 1;
i2s->conf.tx_fifo_reset = 0;
// set parallel (LCD) mode
{
typeof(i2s->conf2) conf2;
conf2.val = 0;
conf2.lcd_en = parallel_mode;
conf2.lcd_tx_wrx2_en = 0; // parallel_mode; // ((parallel_mode) && (bytes_per_sample == 2));
i2s->conf2.val = conf2.val;
}
// Enable and configure DMA // Enable and configure DMA
typeof(i2s->lc_conf) lc_conf; {
lc_conf.val = 0; typeof(i2s->lc_conf) lc_conf;
lc_conf.out_eof_mode = 1; lc_conf.val = 0;
i2s->lc_conf.val = lc_conf.val; lc_conf.out_eof_mode = 1;
i2s->lc_conf.val = lc_conf.val;
}
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
i2s->pdm_conf.pcm2pdm_conv_en = 0; i2s->pdm_conf.pcm2pdm_conv_en = 0;
@@ -343,37 +436,62 @@ void i2sInit(uint8_t bus_num,
#endif #endif
// SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S); // SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S);
typeof(i2s->conf_chan) conf_chan; {
conf_chan.val = 0; typeof(i2s->fifo_conf) fifo_conf;
conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
i2s->conf_chan.val = conf_chan.val;
typeof(i2s->fifo_conf) fifo_conf; fifo_conf.val = 0;
fifo_conf.val = 0; fifo_conf.rx_fifo_mod_force_en = 1;
fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel fifo_conf.tx_fifo_mod_force_en = 1;
fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
i2s->fifo_conf.val = fifo_conf.val; fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
fifo_conf.rx_data_num = 32; //Thresholds.
fifo_conf.tx_data_num = 32;
typeof(i2s->conf) conf; i2s->fifo_conf.val = fifo_conf.val;
conf.val = 0; }
conf.tx_msb_shift = (bits_per_sample != 8);// 0:DAC/PCM, 1:I2S
conf.tx_right_first = (bits_per_sample == 8);
i2s->conf.val = conf.val;
typeof(i2s->conf2) conf2; // $REVIEW old code didn't set this
conf2.val = 0; {
conf2.lcd_en = (bits_per_sample == 8); typeof(i2s->conf1) conf1;
i2s->conf2.val = conf2.val; conf1.val = 0;
conf1.tx_stop_en = 0;
conf1.tx_pcm_bypass = 1;
i2s->conf1.val = conf1.val;
}
i2s->fifo_conf.tx_fifo_mod_force_en = 1; {
typeof(i2s->conf_chan) conf_chan;
conf_chan.val = 0;
conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
i2s->conf_chan.val = conf_chan.val;
}
{
typeof(i2s->conf) conf;
conf.val = 0;
conf.tx_msb_shift = !parallel_mode; // 0:DAC/PCM, 1:I2S
conf.tx_right_first = 0; // parallel_mode?;
i2s->conf.val = conf.val;
}
i2s->timing.val = 0;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
i2s->pdm_conf.rx_pdm_en = 0; i2s->pdm_conf.rx_pdm_en = 0;
i2s->pdm_conf.tx_pdm_en = 0; i2s->pdm_conf.tx_pdm_en = 0;
#endif #endif
i2sSetSampleRate(bus_num, sample_rate, parallel_mode, bytes_per_sample);
i2sSetSampleRate(bus_num, sample_rate, bits_per_sample); /* */
//Reset FIFO/DMA -> needed? Doesn't dma_reset/fifo_reset do this?
i2s->lc_conf.in_rst=1; i2s->lc_conf.out_rst=1; i2s->lc_conf.ahbm_rst=1; i2s->lc_conf.ahbm_fifo_rst=1;
i2s->lc_conf.in_rst=0; i2s->lc_conf.out_rst=0; i2s->lc_conf.ahbm_rst=0; i2s->lc_conf.ahbm_fifo_rst=0;
i2s->conf.tx_reset=1; i2s->conf.tx_fifo_reset=1; i2s->conf.rx_fifo_reset=1;
i2s->conf.tx_reset=0; i2s->conf.tx_fifo_reset=0; i2s->conf.rx_fifo_reset=0;
/* */
// enable intr in cpu // // enable intr in cpu //
int i2sIntSource; int i2sIntSource;
@@ -389,12 +507,21 @@ void i2sInit(uint8_t bus_num,
i2sIntSource = ETS_I2S0_INTR_SOURCE; i2sIntSource = ETS_I2S0_INTR_SOURCE;
} }
esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle); esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle);
// enable send intr // enable send intr
i2s->int_ena.out_eof = 1; i2s->int_ena.out_eof = 1;
i2s->int_ena.out_dscr_err = 1; i2s->int_ena.out_dscr_err = 1;
/* ??? */
// Enable and configure DMA
{
typeof(i2s->lc_conf) lc_conf;
lc_conf.val = 0;
lc_conf.out_data_burst_en = 1;
lc_conf.indscr_burst_en = 1;
i2s->lc_conf.val = lc_conf.val;
}
/* */
i2s->fifo_conf.dscr_en = 1;// enable dma i2s->fifo_conf.dscr_en = 1;// enable dma
i2s->out_link.start = 0; i2s->out_link.start = 0;
i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma
@@ -404,126 +531,103 @@ void i2sInit(uint8_t bus_num,
esp_intr_enable(I2S[bus_num].isr_handle); esp_intr_enable(I2S[bus_num].isr_handle);
} }
void i2sDeinit(uint8_t bus_num) { void i2sDeinit(uint8_t bus_num)
{
i2sDeinitDmaItems(bus_num); i2sDeinitDmaItems(bus_num);
} }
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) { esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, size_t bytes_per_sample)
if (bus_num >= I2S_NUM_MAX) { {
if (bus_num >= I2S_NUM_MAX)
{
return ESP_FAIL; return ESP_FAIL;
} }
if (I2S[bus_num].rate == rate) { uint8_t bck = 12;
return ESP_OK;
// parallel mode needs a higher sample rate
//
if (parallel_mode)
{
#if defined(CONFIG_IDF_TARGET_ESP32S2)
rate *= bytes_per_sample;
bck *= bytes_per_sample;
//rate /= bytes_per_sample;
//bck /= bytes_per_sample;
#else
rate *= bytes_per_sample;
#endif
} }
int clkmInteger, clkmDecimals, bck = 0; // 160,000,000L / (100,000 * 384)
double denom = (double)1 / 63; double clkmdiv = (double)I2S_BASE_CLK / ((rate * 384) + 1);
int channel = 2; if (clkmdiv > 256.0)
{
// double mclk;
double clkmdiv;
int factor;
if (bits == 8) {
factor = 120;
} else {
factor = (256 % bits) ? 384 : 256;
}
clkmdiv = (double)I2S_BASE_CLK / (rate* factor);
if (clkmdiv > 256) {
log_e("rate is too low"); log_e("rate is too low");
return ESP_FAIL; return ESP_FAIL;
} }
I2S[bus_num].rate = rate; else if (clkmdiv < 2.0)
{
clkmInteger = clkmdiv; log_e("rate is too fast, clkmdiv = %f (%u, %u, %u)",
clkmDecimals = ((clkmdiv - clkmInteger) / denom); clkmdiv,
rate,
if (bits == 8) { parallel_mode,
// mclk = rate* factor; bytes_per_sample);
bck = 60; return ESP_FAIL;
bits = 16;
} else {
// mclk = (double)clkmInteger + (denom* clkmDecimals);
bck = factor/(bits* channel);
} }
i2sSetClock(bus_num, clkmInteger, clkmDecimals, 63, bck, bits); // calc integer and franctional for more precise timing
//
uint8_t clkmInteger = clkmdiv;
uint8_t clkmFraction = (clkmdiv - clkmInteger) * 63.0;
i2sSetClock(bus_num, clkmInteger, clkmFraction, 63, bck, bytes_per_sample * 8);
return ESP_OK; return ESP_OK;
} }
void IRAM_ATTR i2sDmaISR(void* arg) void IRAM_ATTR i2sDmaISR(void* arg)
{ {
i2s_bus_t* dev = (i2s_bus_t*)(arg); i2s_bus_t* i2s = (i2s_bus_t*)(arg);
if (dev->bus->int_st.out_eof) if (i2s->bus->int_st.out_eof)
{ {
// i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr); // lldesc_t* item = (lldesc_t*)(i2s->bus->out_eof_des_addr);
if (dev->is_sending_data == I2s_Is_Pending) if (i2s->is_sending_data != I2s_Is_Idle)
{ {
dev->is_sending_data = I2s_Is_Idle; // the second item (last of the two front silent items) is
} // silent looping item
else if (dev->is_sending_data == I2s_Is_Sending) lldesc_t* itemLoop = &i2s->dma_items[0];
{ lldesc_t* itemLoopBreaker = itemLoop + 1;
// loop the silent items // set to loop on silent items
i2s_dma_item_t* itemSilence = &dev->dma_items[1]; itemLoopBreaker->qe.stqe_next = itemLoop;
itemSilence->next = &dev->dma_items[0];
dev->is_sending_data = I2s_Is_Pending; i2s->is_sending_data = I2s_Is_Idle;
} }
} }
dev->bus->int_clr.val = dev->bus->int_st.val; i2s->bus->int_clr.val = i2s->bus->int_st.val;
} }
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) { bool i2sWrite(uint8_t bus_num)
if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) { {
return 0; if (bus_num >= I2S_NUM_MAX)
} {
size_t blockSize = len; return false;
i2s_dma_item_t* item = &I2S[bus_num].dma_items[0];
size_t dataLeft = len;
uint8_t* pos = data;
// skip front two silent items
item += 2;
while (dataLeft) {
blockSize = dataLeft;
if (blockSize > I2S_DMA_MAX_DATA_LEN) {
blockSize = I2S_DMA_MAX_DATA_LEN;
}
dataLeft -= blockSize;
// data is constant. no need to copy
item->data = pos;
item->blocksize = blockSize;
item->datalen = blockSize;
item++;
pos += blockSize;
} }
// the second item (last of the two front silent items) is
// silent looping item
lldesc_t* itemLoopBreaker = &I2S[bus_num].dma_items[1];
lldesc_t* itemLoopNext = itemLoopBreaker + 1;
// set to NOT loop on silent items
itemLoopBreaker->qe.stqe_next = itemLoopNext;
// reset silence item to not loop
item = &I2S[bus_num].dma_items[1];
item->next = &I2S[bus_num].dma_items[2];
I2S[bus_num].is_sending_data = I2s_Is_Sending; I2S[bus_num].is_sending_data = I2s_Is_Sending;
xQueueReset(I2S[bus_num].tx_queue); return true;
xQueueSend(I2S[bus_num].tx_queue, (void*)&I2S[bus_num].dma_items[0], 10);
return len;
} }
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)

View File

@@ -20,21 +20,21 @@ typedef enum {
} i2s_tx_fifo_mod_t; } i2s_tx_fifo_mod_t;
void i2sInit(uint8_t bus_num, void i2sInit(uint8_t bus_num,
uint32_t bits_per_sample, bool parallel_mode,
size_t bytes_per_sample,
uint32_t sample_rate, uint32_t sample_rate,
i2s_tx_chan_mod_t chan_mod, i2s_tx_chan_mod_t chan_mod,
i2s_tx_fifo_mod_t fifo_mod, i2s_tx_fifo_mod_t fifo_mod,
size_t dma_count, size_t dma_count,
size_t dma_len); uint8_t* data,
size_t dataSize);
void i2sDeinit(uint8_t bus_num); void i2sDeinit(uint8_t bus_num);
void i2sSetPins(uint8_t bus_num,
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert); int8_t out,
int8_t parallel,
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample); int8_t busSampleSize,
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, uint8_t bits_per_sample); bool invert);
bool i2sWrite(uint8_t bus_num);
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent);
bool i2sWriteDone(uint8_t bus_num); bool i2sWriteDone(uint8_t bus_num);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -100,6 +100,12 @@ public:
_wire.endTransaction(); _wire.endTransaction();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -100,6 +100,12 @@ public:
_wire.endTransaction(); _wire.endTransaction();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -91,6 +91,12 @@ public:
_endTime = micros(); _endTime = micros();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -187,6 +187,12 @@ public:
_endTime = micros(); _endTime = micros();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -170,7 +170,7 @@ public:
yield(); yield();
} }
i2sSetPins(_bus.I2sBusNumber, -1, false); i2sSetPins(_bus.I2sBusNumber, -1, -1, -1, false);
i2sDeinit(_bus.I2sBusNumber); i2sDeinit(_bus.I2sBusNumber);
free(_data); free(_data);
heap_caps_free(_i2sBuffer); heap_caps_free(_i2sBuffer);
@@ -186,13 +186,15 @@ public:
size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN; size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
i2sInit(_bus.I2sBusNumber, i2sInit(_bus.I2sBusNumber,
16, false,
2, // bytes per sample
T_SPEED::I2sSampleRate, T_SPEED::I2sSampleRate,
I2S_CHAN_STEREO, I2S_CHAN_STEREO,
I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_DUAL,
dmaBlockCount, dmaBlockCount,
0); _i2sBuffer,
i2sSetPins(_bus.I2sBusNumber, _pin, T_INVERT::Inverted); _i2sBufferSize);
i2sSetPins(_bus.I2sBusNumber, _pin, -1, -1, T_INVERT::Inverted);
} }
void Update(bool) void Update(bool)
@@ -205,7 +207,13 @@ public:
FillBuffers(); FillBuffers();
i2sWrite(_bus.I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false); i2sWrite(_bus.I2sBusNumber);
}
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
} }
uint8_t* getData() const uint8_t* getData() const
@@ -229,7 +237,7 @@ private:
uint8_t* _data; // Holds LED color values uint8_t* _data; // Holds LED color values
uint32_t _i2sBufferSize; // total size of _i2sBuffer size_t _i2sBufferSize; // total size of _i2sBuffer
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize) void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize)

View File

@@ -0,0 +1,796 @@
#pragma once
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp32.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
extern "C"
{
#include <Arduino.h>
#include "Esp32_i2s.h"
}
#pragma once
// ESP32 Endian Map
// uint16_t
// 1234
// 3412
// uint32_t
// 12345678
// 78563412
// uint64_t
// 0123456789abcdef
// efcdab8967452301
//
// true size of mux channel, 8 bit
//
class NeoEspI2sMuxBusSize8Bit
{
public:
NeoEspI2sMuxBusSize8Bit() {};
const static size_t MuxBusDataSize = 1;
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
{
#if defined(CONFIG_IDF_TARGET_ESP32S2)
// 1234 - order
// 3412 = actual due to endianness
// 00000001
const uint32_t EncodedZeroBit = 0x00000100;
// 00010101
const uint32_t EncodedOneBit = 0x01000101;
#else
// 8 channel bits layout for DMA 32bit value
// note, right to left
// mux bus bit/id 76543210 76543210 76543210 76543210
// encode bit # 3 2 1 0
// value zero 0 0 0 1
// value one 0 1 1 1
//
// due to indianness between peripheral and cpu, bytes within the words are swapped in the const
// 1234 - order
// 3412 = actual due to endianness
// 00000001
const uint32_t EncodedZeroBit = 0x00010000;
// 00010101
const uint32_t EncodedOneBit = 0x01010001;
#endif
uint32_t* pDma = reinterpret_cast<uint32_t*>(dmaBuffer);
const uint8_t* pEnd = data + sizeData;
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
{
uint8_t value = *pPixel;
for (uint8_t bit = 0; bit < 8; bit++)
{
uint32_t dma = *(pDma);
dma |= (((value & 0x80) ? EncodedOneBit : EncodedZeroBit) << (muxId));
*(pDma++) = dma;
value <<= 1;
}
}
}
};
//
// true size of mux channel, 16 bit
//
class NeoEspI2sMuxBusSize16Bit
{
public:
NeoEspI2sMuxBusSize16Bit() {};
const static size_t MuxBusDataSize = 2;
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
{
#if defined(CONFIG_IDF_TARGET_ESP32S2)
// 1234 5678 - order
// 3412 7856 = actual due to endianness
// not swap 0000000000000001
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
// no swap 0000000100010001
const uint64_t EncodedOneBit64 = 0x0001000000010001;
// can be shifted by 8!
Fillx16(dmaBuffer,
data,
sizeData,
muxId,
EncodedZeroBit64,
EncodedOneBit64);
#else
// 16 channel bits layout for DMA 64bit value
// note, right to left, destination is 32bit chunks
// due to indianness between peripheral and cpu,
// bytes within the words are swapped and words within dwords
// in the literal constants
// { } { }
// 0123 4567 89ab cdef - order of bytes in literal constant
// efcd ab89 6745 2301 - order of memory on ESP32 due to Endianness
// 6745 2301 efcd ab89 - 32bit dest means only map using 32bits so swap upper and lower
//
// Due to final bit locations, can't shift encoded one bit
// either left more than 7 or right more than 7 so we have to
// split the updates and use different encodings
if (muxId < 8)
{
// endian + dest swap 0000000000000001
const uint64_t EncodedZeroBit64 = 0x0000000001000000;
// endian + dest swap 0000000100010001
const uint64_t EncodedOneBit64 = 0x0100000001000100;
// cant be shifted by 8!
Fillx16(dmaBuffer,
data,
sizeData,
muxId,
EncodedZeroBit64,
EncodedOneBit64);
}
else
{
// endian + dest swap 0000000000000001
// then pre shift by 8 0000000000000100
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
// endian + dest swap 0000000100010001
// then pre shift by 8 0000010001000100
const uint64_t EncodedOneBit64 = 0x0001000000010001;
Fillx16(dmaBuffer,
data,
sizeData,
muxId - 8, // preshifted
EncodedZeroBit64,
EncodedOneBit64);
}
#endif
}
protected:
static void Fillx16(uint8_t* dmaBuffer,
const uint8_t* data,
size_t sizeData,
uint8_t muxShift,
const uint64_t EncodedZeroBit64,
const uint64_t EncodedOneBit64)
{
uint64_t* pDma64 = reinterpret_cast<uint64_t*>(dmaBuffer);
const uint8_t* pEnd = data + sizeData;
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
{
uint8_t value = *pPixel;
for (uint8_t bit = 0; bit < 8; bit++)
{
uint64_t dma64 = *(pDma64);
dma64 |= (((value & 0x80) ? EncodedOneBit64 : EncodedZeroBit64) << (muxShift));
*(pDma64++) = dma64;
value <<= 1;
}
}
}
};
//
// tracks mux channels used and if updated
//
// T_FLAG - type used to store bit flags, UINT8_t for 8 channels, UINT16_t for 16
// T_MUXSIZE - true size of mux channel = NeoEspI2sMuxBusSize8Bit or NeoEspI2sMuxBusSize16Bit
//
template<typename T_FLAG, typename T_MUXSIZE> class NeoEspI2sMuxMap : public T_MUXSIZE
{
public:
const static uint8_t InvalidMuxId = -1;
const static size_t BusMaxCount = sizeof(T_FLAG) * 8;
size_t MaxBusDataSize; // max size of stream data from any single mux bus
T_FLAG UpdateMap; // bitmap flags of mux buses to track update state
T_FLAG UpdateMapMask; // mask to used bits in s_UpdateMap
T_FLAG BusCount; // count of mux buses
// as a static instance, all members get initialized to zero
// and the constructor is called at inconsistent time to other globals
// so its not useful to have or rely on,
// but without it presence they get zeroed far too late
NeoEspI2sMuxMap()
// //:
// //MaxBusDataSize(0),
// //UpdateMap(0),
// //UpdateMapMask(0),
// //BusCount(0)
{
}
uint8_t RegisterNewMuxBus(const size_t dataSize)
{
// find first available bus id
uint8_t muxId = 0;
while (muxId < BusMaxCount)
{
T_FLAG muxIdField = (1 << muxId);
if ((UpdateMapMask & muxIdField) == 0)
{
// complete registration
BusCount++;
UpdateMapMask |= muxIdField;
if (dataSize > MaxBusDataSize)
{
MaxBusDataSize = dataSize;
}
break;
}
muxId++;
}
if (muxId == BusMaxCount)
{
log_e("exceded channel limit of %u on bus", BusMaxCount);
}
return muxId;
}
bool DeregisterMuxBus(uint8_t muxId)
{
T_FLAG muxIdField = (1 << muxId);
if (UpdateMapMask & muxIdField)
{
// complete deregistration
BusCount--;
UpdateMapMask &= ~muxIdField;
if (UpdateMapMask == 0)
{
return true;
}
}
return false;
}
bool IsAllMuxBusesUpdated()
{
return (UpdateMap == UpdateMapMask);
}
bool IsNoMuxBusesUpdate()
{
return (UpdateMap == 0);
}
void MarkMuxBusUpdated(uint8_t muxId)
{
UpdateMap |= (1 << muxId);
}
void ResetMuxBusesUpdated()
{
UpdateMap = 0;
}
void Reset()
{
MaxBusDataSize = 0;
UpdateMap = 0;
UpdateMapMask = 0;
BusCount = 0;
}
};
//
// Implementation of a Double Buffered version of a I2sContext
// Manages the underlying I2S details including the buffer(s)
// This creates a front buffer that can be filled while actively sending
// the back buffer, thus improving async operation of the i2s DMA.
// Note that the back buffer must be DMA memory, a limited resource, so
// the front buffer uses normal memory and copies rather than swap pointers
//
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
//
template<typename T_MUXMAP> class NeoEspI2sDblBuffContext
{
public:
const static size_t DmaBitsPerPixelBit = 4;
size_t I2sBufferSize; // total size of I2sBuffer
uint8_t* I2sBuffer; // holds the DMA buffer that is referenced by I2sBufDesc
uint8_t* I2sEditBuffer; // hold a editable buffer that is copied to I2sBuffer
T_MUXMAP MuxMap;
// as a static instance, all members get initialized to zero
// and the constructor is called at inconsistent time to other globals
// so its not useful to have or rely on,
// but without it presence they get zeroed far too late
NeoEspI2sDblBuffContext()
// //:
// //I2sBufferSize(0),
// //I2sBuffer(nullptr),
// //I2sEditBuffer(nullptr),
// //MuxMap()
{
}
void Construct(const uint8_t busNumber, uint32_t i2sSampleRate)
{
// construct only once on first time called
if (I2sBuffer == nullptr)
{
// MuxMap.MaxBusDataSize = max size in bytes of a single channel
// DmaBitsPerPixelBit = how many dma bits/byte are needed for each source (pixel) bit/byte
// T_MUXMAP::MuxBusDataSize = the true size of data for selected mux mode (not exposed size as i2s0 only supports 16bit mode)
I2sBufferSize = MuxMap.MaxBusDataSize * 8 * DmaBitsPerPixelBit * T_MUXMAP::MuxBusDataSize;
// must have a 4 byte aligned buffer for i2s
uint32_t alignment = I2sBufferSize % 4;
if (alignment)
{
I2sBufferSize += 4 - alignment;
}
size_t dmaBlockCount = (I2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
I2sBuffer = static_cast<uint8_t*>(heap_caps_malloc(I2sBufferSize, MALLOC_CAP_DMA));
if (I2sBuffer == nullptr)
{
log_e("send buffer memory allocation failure (size %u)",
I2sBufferSize);
}
memset(I2sBuffer, 0x00, I2sBufferSize);
I2sEditBuffer = static_cast<uint8_t*>(malloc(I2sBufferSize));
if (I2sEditBuffer == nullptr)
{
log_e("edit buffer memory allocation failure (size %u)",
I2sBufferSize);
}
memset(I2sEditBuffer, 0x00, I2sBufferSize);
i2sInit(busNumber,
true,
T_MUXMAP::MuxBusDataSize,
i2sSampleRate,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
// using these modes on ESP32S2 actually allows it to function
// in both x8 and x16
I2S_CHAN_STEREO,
I2S_FIFO_16BIT_DUAL,
#else
// but they won't work on ESP32 in parallel mode, but these will
I2S_CHAN_RIGHT_TO_LEFT,
I2S_FIFO_16BIT_SINGLE,
#endif
dmaBlockCount,
I2sBuffer,
I2sBufferSize);
}
}
void Destruct(const uint8_t busNumber)
{
if (I2sBuffer == nullptr)
{
return;
}
i2sSetPins(busNumber, -1, -1, -1, false);
i2sDeinit(busNumber);
free(I2sEditBuffer);
heap_caps_free(I2sBuffer);
I2sBufferSize = 0;
I2sBuffer = nullptr;
I2sEditBuffer = nullptr;
MuxMap.Reset();
}
};
//
// Implementation of the low level interface into i2s mux bus
//
// T_BUSCONTEXT - the context to use, currently only NeoEspI2sDblBuffContext but there is
// a plan to provide one that doesn't implement the front buffer but would be less
// async as it would have to wait until the last frame was completely sent before
// updating and new data
// T_BUS - the bus id, NeoEsp32I2sBusZero, NeoEsp32I2sBusOne
//
template<typename T_BUSCONTEXT, typename T_BUS> class NeoEsp32I2sMuxBus
{
public:
NeoEsp32I2sMuxBus() :
_muxId(s_context.MuxMap.InvalidMuxId)
{
}
void RegisterNewMuxBus(size_t dataSize)
{
_muxId = s_context.MuxMap.RegisterNewMuxBus(dataSize);
}
void Initialize(uint8_t pin, uint32_t i2sSampleRate, bool invert)
{
s_context.Construct(T_BUS::I2sBusNumber, i2sSampleRate);
i2sSetPins(T_BUS::I2sBusNumber, pin, _muxId, s_context.MuxMap.MuxBusDataSize, invert);
}
void DeregisterMuxBus()
{
if (s_context.MuxMap.DeregisterMuxBus(_muxId))
{
s_context.Destruct(T_BUS::I2sBusNumber);
}
// disconnect muxed pin?
_muxId = s_context.MuxMap.InvalidMuxId;
}
void StartWrite()
{
if (s_context.MuxMap.IsAllMuxBusesUpdated())
{
s_context.MuxMap.ResetMuxBusesUpdated();
// wait for not actively sending data
while (!IsWriteDone())
{
yield();
}
// copy edit buffer to sending buffer
memcpy(s_context.I2sBuffer, s_context.I2sEditBuffer, s_context.I2sBufferSize);
i2sWrite(T_BUS::I2sBusNumber);
}
}
bool IsWriteDone()
{
return i2sWriteDone(T_BUS::I2sBusNumber);
}
void FillBuffers(const uint8_t* data, size_t sizeData)
{
if (s_context.MuxMap.IsNoMuxBusesUpdate())
{
// clear all the data in preperation for each mux channel to add
memset(s_context.I2sEditBuffer, 0x00, s_context.I2sBufferSize);
}
s_context.MuxMap.EncodeIntoDma(s_context.I2sEditBuffer,
data,
sizeData,
_muxId );
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
}
void MarkUpdated()
{
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
}
private:
static T_BUSCONTEXT s_context;
uint8_t _muxId;
};
template<typename T_BUSCONTEXT, typename T_BUS> T_BUSCONTEXT NeoEsp32I2sMuxBus<T_BUSCONTEXT, T_BUS>::s_context = T_BUSCONTEXT();
//
// wrapping layer of the i2s mux bus as a NeoMethod
//
// T_SPEED - NeoEsp32I2sSpeed* (ex NeoEsp32I2sSpeedWs2812x) used to define output signal form
// T_BUS - NeoEsp32I2sMuxBus, the bus to use
// T_INVERT - NeoEsp32I2sNotInverted or NeoEsp32I2sInverted, will invert output signal
//
template<typename T_SPEED, typename T_BUS, typename T_INVERT> class NeoEsp32I2sXMethodBase
{
public:
typedef NeoNoSettings SettingsObject;
NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
_sizeData(pixelCount * elementSize + settingsSize),
_pin(pin),
_bus()
{
_bus.RegisterNewMuxBus(_sizeData + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs);
}
~NeoEsp32I2sXMethodBase()
{
while (!_bus.IsWriteDone())
{
yield();
}
_bus.DeregisterMuxBus();
free(_data);
}
bool IsReadyToUpdate() const
{
return _bus.IsWriteDone();
}
void Initialize()
{
_bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted);
_data = static_cast<uint8_t*>(malloc(_sizeData));
if (_data == nullptr)
{
log_e("front buffer memory allocation failure");
}
// data cleared later in Begin()
}
void Update(bool)
{
_bus.FillBuffers(_data, _sizeData);
_bus.StartWrite(); // only triggers actual write after all mux busses have updated
}
bool AlwaysUpdate()
{
// this method requires update to be called even if no changes to method buffer
// as edit buffer is always cleared and then copied to send buffer and all
// mux bus needs to included
return true;
}
uint8_t* getData() const
{
return _data;
};
size_t getDataSize() const
{
return _sizeData;
}
void applySettings([[maybe_unused]] const SettingsObject& settings)
{
}
private:
const size_t _sizeData; // Size of '_data' buffer
const uint8_t _pin; // output pin number
T_BUS _bus; // holds instance for mux bus support
uint8_t* _data; // Holds LED color values
};
#if defined(CONFIG_IDF_TARGET_ESP32S2)
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
#else
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
#endif
// NORMAL
//
// I2s0x8
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Ws2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Sk6812Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1814Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1829Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1914Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8800KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8400KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Apa106Method;
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2813Method;
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2812dMethod;
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2811Method;
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2816Method;
typedef NeoEsp32I2s0X8800KbpsMethod NeoEsp32I2s0X8Ws2812Method;
typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Sk6812Method;
typedef NeoEsp32I2s0X8Tm1814Method NeoEsp32I2s0X8Tm1814Method;
typedef NeoEsp32I2s0X8Tm1829Method NeoEsp32I2s0X8Tm1829Method;
typedef NeoEsp32I2s0X8Tm1914Method NeoEsp32I2s0X8Tm1914Method;
typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Lc8812Method;
typedef NeoEsp32I2s0X8Apa106Method NeoEsp32I2s0X8Apa106Method;
// I2s0x16
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Ws2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Sk6812Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1814Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1829Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1914Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16800KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16400KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Apa106Method;
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2813Method;
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2812dMethod;
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2811Method;
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2816Method;
typedef NeoEsp32I2s0X16800KbpsMethod NeoEsp32I2s0X16Ws2812Method;
typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Sk6812Method;
typedef NeoEsp32I2s0X16Tm1814Method NeoEsp32I2s0X16Tm1814Method;
typedef NeoEsp32I2s0X16Tm1829Method NeoEsp32I2s0X16Tm1829Method;
typedef NeoEsp32I2s0X16Tm1914Method NeoEsp32I2s0X16Tm1914Method;
typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
typedef NeoEsp32I2s0X16Apa106Method NeoEsp32I2s0X16Apa106Method;
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
// I2s1x8
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1814Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1829Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1914Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8800KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8400KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Apa106Method;
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2813Method;
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2812dMethod;
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2811Method;
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2816Method;
typedef NeoEsp32I2s1X8800KbpsMethod NeoEsp32I2s1X8Ws2812Method;
typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Sk6812Method;
typedef NeoEsp32I2s1X8Tm1814Method NeoEsp32I2s1X8Tm1814Method;
typedef NeoEsp32I2s1X8Tm1829Method NeoEsp32I2s1X8Tm1829Method;
typedef NeoEsp32I2s1X8Tm1914Method NeoEsp32I2s1X8Tm1914Method;
typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Lc8812Method;
typedef NeoEsp32I2s1X8Apa106Method NeoEsp32I2s1X8Apa106Method;
// I2s1x16
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Ws2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Sk6812Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1814Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1829Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1914Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16800KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16400KbpsMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Apa106Method;
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2813Method;
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2812dMethod;
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2811Method;
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2816Method;
typedef NeoEsp32I2s1X16800KbpsMethod NeoEsp32I2s1X16Ws2812Method;
typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Sk6812Method;
typedef NeoEsp32I2s1X16Tm1814Method NeoEsp32I2s1X16Tm1814Method;
typedef NeoEsp32I2s1X16Tm1829Method NeoEsp32I2s1X16Tm1829Method;
typedef NeoEsp32I2s1X16Tm1914Method NeoEsp32I2s1X16Tm1914Method;
typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Lc8812Method;
typedef NeoEsp32I2s1X16Apa106Method NeoEsp32I2s1X16Apa106Method;
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
// INVERTED
//
// I2s0x8 INVERTED
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Ws2812xInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Sk6812InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1814InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1829InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1914InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8800KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8400KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Apa106InvertedMethod;
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2813InvertedMethod;
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2812xInvertedMethod;
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2811InvertedMethod;
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2816InvertedMethod;
typedef NeoEsp32I2s0X8800KbpsInvertedMethod NeoEsp32I2s0X8Ws2812InvertedMethod;
typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Sk6812InvertedMethod;
typedef NeoEsp32I2s0X8Tm1814InvertedMethod NeoEsp32I2s0X8Tm1814InvertedMethod;
typedef NeoEsp32I2s0X8Tm1829InvertedMethod NeoEsp32I2s0X8Tm1829InvertedMethod;
typedef NeoEsp32I2s0X8Tm1914InvertedMethod NeoEsp32I2s0X8Tm1914InvertedMethod;
typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Lc8812InvertedMethod;
typedef NeoEsp32I2s0X8Apa106InvertedMethod NeoEsp32I2s0X8Apa106InvertedMethod;
// I2s0x16 INVERTED
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Ws2812xInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Sk6812InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1814InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1829InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1914InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16800KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16400KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Apa106InvertedMethod;
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2813InvertedMethod;
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2812xInvertedMethod;
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2811InvertedMethod;
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2816InvertedMethod;
typedef NeoEsp32I2s0X16800KbpsInvertedMethod NeoEsp32I2s0X16Ws2812InvertedMethod;
typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Sk6812InvertedMethod;
typedef NeoEsp32I2s0X16Tm1814InvertedMethod NeoEsp32I2s0X16Tm1814InvertedMethod;
typedef NeoEsp32I2s0X16Tm1829InvertedMethod NeoEsp32I2s0X16Tm1829InvertedMethod;
typedef NeoEsp32I2s0X16Tm1914InvertedMethod NeoEsp32I2s0X16Tm1914InvertedMethod;
typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Lc8812InvertedMethod;
typedef NeoEsp32I2s0X16Apa106InvertedMethod NeoEsp32I2s0X16Apa106InvertedMethod;
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
// I2s1x8 INVERTED
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Ws2812xInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Sk6812InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1814InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1829InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1914InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8800KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8400KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Apa106InvertedMethod;
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2813InvertedMethod;
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2812xInvertedMethod;
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2811InvertedMethod;
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2816InvertedMethod;
typedef NeoEsp32I2s1X8800KbpsInvertedMethod NeoEsp32I2s1X8Ws2812InvertedMethod;
typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Sk6812InvertedMethod;
typedef NeoEsp32I2s1X8Tm1814InvertedMethod NeoEsp32I2s1X8Tm1814InvertedMethod;
typedef NeoEsp32I2s1X8Tm1829InvertedMethod NeoEsp32I2s1X8Tm1829InvertedMethod;
typedef NeoEsp32I2s1X8Tm1914InvertedMethod NeoEsp32I2s1X8Tm1914InvertedMethod;
typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Lc8812InvertedMethod;
typedef NeoEsp32I2s1X8Apa106InvertedMethod NeoEsp32I2s1X8Apa106InvertedMethod;
// I2s1x16 INVERTED
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Ws2812xInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Sk6812InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1814InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1829InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1914InvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16800KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16400KbpsInvertedMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Apa106InvertedMethod;
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2813InvertedMethod;
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2812xInvertedMethod;
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2811InvertedMethod;
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2816InvertedMethod;
typedef NeoEsp32I2s1X16800KbpsInvertedMethod NeoEsp32I2s1X16Ws2812InvertedMethod;
typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Sk6812InvertedMethod;
typedef NeoEsp32I2s1X16Tm1814InvertedMethod NeoEsp32I2s1X16Tm1814InvertedMethod;
typedef NeoEsp32I2s1X16Tm1829InvertedMethod NeoEsp32I2s1X16Tm1829InvertedMethod;
typedef NeoEsp32I2s1X16Tm1914InvertedMethod NeoEsp32I2s1X16Tm1914InvertedMethod;
typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Lc8812InvertedMethod;
typedef NeoEsp32I2s1X16Apa106InvertedMethod NeoEsp32I2s1X16Apa106InvertedMethod;
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
#endif // defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)

View File

@@ -587,6 +587,12 @@ public:
} }
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _dataEditing; return _dataEditing;

View File

@@ -261,6 +261,12 @@ public:
_dmaState = NeoDmaState_Pending; _dmaState = NeoDmaState_Pending;
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -198,6 +198,12 @@ public:
_dmaState = NeoDmaState_Pending; _dmaState = NeoDmaState_Pending;
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data + T_SPEED::HeaderSize; return _data + T_SPEED::HeaderSize;

View File

@@ -396,6 +396,12 @@ public:
this->UpdateUart(maintainBufferConsistency); this->UpdateUart(maintainBufferConsistency);
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return this->_data; return this->_data;

View File

@@ -308,6 +308,12 @@ public:
_endTime = micros(); _endTime = micros();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -400,6 +400,12 @@ public:
dmaStart(); dmaStart();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -96,6 +96,12 @@ public:
_wire.endTransaction(); _wire.endTransaction();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -164,6 +164,12 @@ public:
digitalWrite(_pinOutputEnable, LOW); digitalWrite(_pinOutputEnable, LOW);
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;

View File

@@ -102,6 +102,12 @@ public:
_endTime = micros(); _endTime = micros();
} }
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
uint8_t* getData() const uint8_t* getData() const
{ {
return _data; return _data;