Esp32 i2s (#427)

* dynamic i2s bus numbers

* stabile and consistent shows

* faster to start send
This commit is contained in:
Michael Miller
2021-02-11 17:22:10 -08:00
committed by GitHub
parent 7a73ffc7b4
commit 13b7d2e377
4 changed files with 199 additions and 129 deletions

View File

@@ -166,6 +166,12 @@ NeoEsp8266BitBangLc8812InvertedMethod KEYWORD1
NeoEsp8266BitBangApa106InvertedMethod KEYWORD1 NeoEsp8266BitBangApa106InvertedMethod KEYWORD1
NeoEsp8266BitBang800KbpsInvertedMethod KEYWORD1 NeoEsp8266BitBang800KbpsInvertedMethod KEYWORD1
NeoEsp8266BitBang400KbpsInvertedMethod KEYWORD1 NeoEsp8266BitBang400KbpsInvertedMethod KEYWORD1
NeoEsp32I2sNWs2812xMethod KEYWORD1
NeoEsp32I2sNSk6812Method KEYWORD1
NeoEsp32I2sNTm1814Method KEYWORD1
NeoEsp32I2sN800KbpsMethod KEYWORD1
NeoEsp32I2sN400KbpsMethod KEYWORD1
NeoEsp32I2sNApa106Method KEYWORD1
NeoEsp32I2s0Ws2812xMethod KEYWORD1 NeoEsp32I2s0Ws2812xMethod KEYWORD1
NeoEsp32I2s0Sk6812Method KEYWORD1 NeoEsp32I2s0Sk6812Method KEYWORD1
NeoEsp32I2s0Tm1814Method KEYWORD1 NeoEsp32I2s0Tm1814Method KEYWORD1
@@ -178,6 +184,12 @@ NeoEsp32I2s1Tm1814Method KEYWORD1
NeoEsp32I2s1800KbpsMethod KEYWORD1 NeoEsp32I2s1800KbpsMethod KEYWORD1
NeoEsp32I2s1400KbpsMethod KEYWORD1 NeoEsp32I2s1400KbpsMethod KEYWORD1
NeoEsp32I2s1Apa106Method KEYWORD1 NeoEsp32I2s1Apa106Method KEYWORD1
NeoEsp32I2sNWs2812xInvertedMethod KEYWORD1
NeoEsp32I2sNSk6812InvertedMethod KEYWORD1
NeoEsp32I2sNTm1814InvertedMethod KEYWORD1
NeoEsp32I2sN800KbpsInvertedMethod KEYWORD1
NeoEsp32I2sN400KbpsInvertedMethod KEYWORD1
NeoEsp32I2sNApa106InvertedMethod KEYWORD1
NeoEsp32I2s0Ws2812xInvertedMethod KEYWORD1 NeoEsp32I2s0Ws2812xInvertedMethod KEYWORD1
NeoEsp32I2s0Sk6812InvertedMethod KEYWORD1 NeoEsp32I2s0Sk6812InvertedMethod KEYWORD1
NeoEsp32I2s0Tm1814InvertedMethod KEYWORD1 NeoEsp32I2s0Tm1814InvertedMethod KEYWORD1
@@ -585,3 +597,11 @@ NeoTopologyHint_InPanel LITERAL1
NeoTopologyHint_LastOnPanel LITERAL1 NeoTopologyHint_LastOnPanel LITERAL1
NeoTopologyHint_OutOfBounds LITERAL1 NeoTopologyHint_OutOfBounds LITERAL1
PixelIndex_OutOfBounds LITERAL1 PixelIndex_OutOfBounds LITERAL1
NeoBusChannel_0 LITERAL1
NeoBusChannel_1 LITERAL1
NeoBusChannel_2 LITERAL1
NeoBusChannel_3 LITERAL1
NeoBusChannel_4 LITERAL1
NeoBusChannel_5 LITERAL1
NeoBusChannel_6 LITERAL1
NeoBusChannel_7 LITERAL1

View File

@@ -52,9 +52,12 @@
#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr)))) #define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr))))
#define I2S_DMA_QUEUE_SIZE 16 #define I2S_DMA_BLOCK_COUNT_DEFAULT 16
// 24 bytes gives us enough time if we use single stage idle
#define I2S_DMA_SILENCE_LEN 256 // bytes // with the two stage idle we can use the minimum of 4 bytes
#define I2S_DMA_SILENCE_SIZE 4*1
#define I2S_DMA_SILENCE_BLOCK_COUNT 3 // two front, one back
#define I2S_DMA_QUEUE_COUNT 2
typedef struct i2s_dma_item_s { typedef struct i2s_dma_item_s {
uint32_t blocksize: 12; // datalen uint32_t blocksize: 12; // datalen
@@ -91,24 +94,29 @@ typedef struct {
size_t dma_count; size_t dma_count;
uint32_t dma_buf_len :12; uint32_t dma_buf_len :12;
uint32_t unused :20; uint32_t unused :20;
volatile uint32_t is_sending_data;
} i2s_bus_t; } i2s_bus_t;
static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_LEN]; // is_sending_data values
#define I2s_Is_Idle 0
#define I2s_Is_Pending 1
#define I2s_Is_Sending 2
static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_SIZE] = { 0 };
#if !defined(CONFIG_IDF_TARGET_ESP32S2) #if !defined(CONFIG_IDF_TARGET_ESP32S2)
// (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_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0}, {&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_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0} {&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}
}; };
#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_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0} {&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}
}; };
#endif #endif
void IRAM_ATTR i2sDmaISR(void* arg); void IRAM_ATTR i2sDmaISR(void* arg);
bool i2sInitDmaItems(uint8_t bus_num);
bool i2sInitDmaItems(uint8_t bus_num) { bool i2sInitDmaItems(uint8_t bus_num) {
if (bus_num >= I2S_NUM_MAX) { if (bus_num >= I2S_NUM_MAX) {
@@ -118,8 +126,10 @@ bool i2sInitDmaItems(uint8_t bus_num) {
return true; return true;
} }
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*)(malloc(I2S[bus_num].dma_count* sizeof(i2s_dma_item_t))); I2S[bus_num].dma_items = (i2s_dma_item_t*)(malloc(dmaCount * sizeof(i2s_dma_item_t)));
if (I2S[bus_num].dma_items == NULL) { if (I2S[bus_num].dma_items == NULL) {
log_e("MEM ERROR!"); log_e("MEM ERROR!");
return false; return false;
@@ -127,12 +137,15 @@ bool i2sInitDmaItems(uint8_t bus_num) {
} }
int i, i2, a; int i, i2, a;
i2s_dma_item_t* item; i2s_dma_item_t* item = NULL;
i2s_dma_item_t* itemPrev;
for(i=0; i<I2S[bus_num].dma_count; i++) { for(i=0; i< dmaCount; i++) {
i2 = (i+1) % I2S[bus_num].dma_count; itemPrev = item;
i2 = (i+1) % dmaCount;
item = &I2S[bus_num].dma_items[i]; item = &I2S[bus_num].dma_items[i];
item->eof = 1; item->eof = 0;
item->owner = 1; item->owner = 1;
item->sub_sof = 0; item->sub_sof = 0;
item->unused = 0; item->unused = 0;
@@ -141,23 +154,12 @@ bool i2sInitDmaItems(uint8_t bus_num) {
item->datalen = I2S[bus_num].silence_len; item->datalen = I2S[bus_num].silence_len;
item->next = &I2S[bus_num].dma_items[i2]; item->next = &I2S[bus_num].dma_items[i2];
item->free_ptr = NULL; item->free_ptr = NULL;
if (I2S[bus_num].dma_buf_len) {
item->buf = (uint8_t*)(malloc(I2S[bus_num].dma_buf_len));
if (item->buf == NULL) {
log_e("MEM ERROR!");
for(a=0; a<i; a++) {
free(I2S[bus_num].dma_items[i].buf);
}
free(I2S[bus_num].dma_items);
I2S[bus_num].dma_items = NULL;
return false;
}
} else {
item->buf = NULL; item->buf = NULL;
} }
} itemPrev->eof = 1;
item->eof = 1;
I2S[bus_num].tx_queue = xQueueCreate(I2S[bus_num].dma_count, sizeof(i2s_dma_item_t*)); I2S[bus_num].tx_queue = xQueueCreate(I2S_DMA_QUEUE_COUNT, sizeof(i2s_dma_item_t*));
if (I2S[bus_num].tx_queue == NULL) {// memory error if (I2S[bus_num].tx_queue == NULL) {// memory error
log_e("MEM ERROR!"); log_e("MEM ERROR!");
free(I2S[bus_num].dma_items); free(I2S[bus_num].dma_items);
@@ -167,14 +169,6 @@ bool i2sInitDmaItems(uint8_t bus_num) {
return true; return true;
} }
void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len) {
if (bus_num >= I2S_NUM_MAX || !data || !len) {
return;
}
I2S[bus_num].silence_buf = data;
I2S[bus_num].silence_len = len;
}
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) { 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) {
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) { if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
return ESP_FAIL; return ESP_FAIL;
@@ -205,37 +199,6 @@ esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t d
return ESP_OK; return ESP_OK;
} }
void i2sSetDac(uint8_t bus_num, bool right, bool left) {
if (bus_num >= I2S_NUM_MAX) {
return;
}
if (!right && !left) {
dac_output_disable(DAC_CHANNEL_1);
dac_output_disable(DAC_CHANNEL_2);
dac_i2s_disable();
I2S[bus_num].bus->conf2.lcd_en = 0;
I2S[bus_num].bus->conf.tx_right_first = 0;
I2S[bus_num].bus->conf2.camera_en = 0;
I2S[bus_num].bus->conf.tx_msb_shift = 1;// I2S signaling
return;
}
i2sSetPins(bus_num, -1, false);
I2S[bus_num].bus->conf2.lcd_en = 1;
I2S[bus_num].bus->conf.tx_right_first = 0;
I2S[bus_num].bus->conf2.camera_en = 0;
I2S[bus_num].bus->conf.tx_msb_shift = 0;
dac_i2s_enable();
if (right) {// DAC1, right channel, GPIO25
dac_output_enable(DAC_CHANNEL_1);
}
if (left) { // DAC2, left channel, GPIO26
dac_output_enable(DAC_CHANNEL_2);
}
}
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert) { void i2sSetPins(uint8_t bus_num, int8_t out, bool invert) {
if (bus_num >= I2S_NUM_MAX) { if (bus_num >= I2S_NUM_MAX) {
return; return;
@@ -274,15 +237,22 @@ bool i2sWriteDone(uint8_t bus_num) {
if (bus_num >= I2S_NUM_MAX) { if (bus_num >= I2S_NUM_MAX) {
return false; return false;
} }
return (I2S[bus_num].dma_items[I2S[bus_num].dma_count - 1].data == I2S[bus_num].silence_buf);
return (I2S[bus_num].is_sending_data == I2s_Is_Idle);
} }
void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len) { void i2sInit(uint8_t bus_num,
uint32_t bits_per_sample,
uint32_t sample_rate,
i2s_tx_chan_mod_t chan_mod,
i2s_tx_fifo_mod_t fifo_mod,
size_t dma_count,
size_t dma_len) {
if (bus_num >= I2S_NUM_MAX) { if (bus_num >= I2S_NUM_MAX) {
return; return;
} }
I2S[bus_num].dma_count = dma_count; I2S[bus_num].dma_count = dma_count + I2S_DMA_SILENCE_BLOCK_COUNT; // an extra two for looping silence
I2S[bus_num].dma_buf_len = dma_len & 0xFFF; I2S[bus_num].dma_buf_len = dma_len & 0xFFF;
if (!i2sInitDmaItems(bus_num)) { if (!i2sInitDmaItems(bus_num)) {
@@ -446,56 +416,76 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) {
return ESP_OK; return ESP_OK;
} }
void IRAM_ATTR i2sDmaISR(void* arg) void IRAM_ATTR i2sDmaISR(void* arg)
{ {
i2s_dma_item_t* dummy = NULL;
i2s_bus_t* dev = (i2s_bus_t*)(arg); i2s_bus_t* dev = (i2s_bus_t*)(arg);
portBASE_TYPE hpTaskAwoken = 0;
if (dev->bus->int_st.out_eof) { if (dev->bus->int_st.out_eof)
i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr); {
item->data = dev->silence_buf; // i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr);
item->blocksize = dev->silence_len; if (dev->is_sending_data == I2s_Is_Pending)
item->datalen = dev->silence_len; {
if (xQueueIsQueueFullFromISR(dev->tx_queue) == pdTRUE) { dev->is_sending_data = I2s_Is_Idle;
xQueueReceiveFromISR(dev->tx_queue, &dummy, &hpTaskAwoken);
} }
xQueueSendFromISR(dev->tx_queue, (void*)&item, &hpTaskAwoken); else if (dev->is_sending_data == I2s_Is_Sending)
{
// loop the silent items
i2s_dma_item_t* itemSilence = &dev->dma_items[1];
itemSilence->next = &dev->dma_items[0];
dev->is_sending_data = I2s_Is_Pending;
} }
}
dev->bus->int_clr.val = dev->bus->int_st.val; dev->bus->int_clr.val = dev->bus->int_st.val;
if (hpTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
} }
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) { size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) {
if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) { if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) {
return 0; return 0;
} }
size_t index = 0; size_t blockSize = len;
size_t toSend = len;
size_t limit = I2S_DMA_MAX_DATA_LEN;
i2s_dma_item_t* item = NULL;
while (len) { i2s_dma_item_t* itemPrev = NULL;
toSend = len; i2s_dma_item_t* item = &I2S[bus_num].dma_items[0];
if (toSend > limit) { size_t dataLeft = len;
toSend = limit; uint8_t* pos = data;
}
if (xQueueReceive(I2S[bus_num].tx_queue, &item, portMAX_DELAY) == pdFALSE) { // skip front two silent items
log_e("xQueueReceive failed\n"); item += 2;
break;
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 // data is constant. no need to copy
item->data = data + index; item->data = pos;
item->blocksize = toSend; item->blocksize = blockSize;
item->datalen = toSend; item->datalen = blockSize;
len -= toSend; itemPrev = item;
index += toSend; item++;
pos += blockSize;
} }
return index;
// 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;
xQueueReset(I2S[bus_num].tx_queue);
xQueueSend(I2S[bus_num].tx_queue, (void*)&I2S[bus_num].dma_items[0], 10);
return len;
} }

View File

@@ -18,16 +18,19 @@ typedef enum {
I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE
} i2s_tx_fifo_mod_t; } i2s_tx_fifo_mod_t;
void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len); void i2sInit(uint8_t bus_num,
uint32_t bits_per_sample,
uint32_t sample_rate,
i2s_tx_chan_mod_t chan_mod,
i2s_tx_fifo_mod_t fifo_mod,
size_t dma_count,
size_t dma_len);
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert); void i2sSetPins(uint8_t bus_num, int8_t out, bool invert);
void i2sSetDac(uint8_t bus_num, bool right, bool left);
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 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, uint8_t bits_per_sample); esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, uint8_t bits_per_sample);
void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len);
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent); 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);

View File

@@ -87,15 +87,32 @@ public:
class NeoEsp32I2sBusZero class NeoEsp32I2sBusZero
{ {
public: public:
NeoEsp32I2sBusZero() {};
const static uint8_t I2sBusNumber = 0; const static uint8_t I2sBusNumber = 0;
}; };
class NeoEsp32I2sBusOne class NeoEsp32I2sBusOne
{ {
public: public:
NeoEsp32I2sBusOne() {};
const static uint8_t I2sBusNumber = 1; const static uint8_t I2sBusNumber = 1;
}; };
// dynamic channel support
class NeoEsp32I2sBusN
{
public:
NeoEsp32I2sBusN(NeoBusChannel channel) :
I2sBusNumber(static_cast<uint8_t>(channel))
{
}
NeoEsp32I2sBusN() = delete; // no default constructor
const uint8_t I2sBusNumber;
};
class NeoEsp32I2sNotInverted class NeoEsp32I2sNotInverted
{ {
public: public:
@@ -115,24 +132,15 @@ public:
_sizeData(pixelCount * elementSize + settingsSize), _sizeData(pixelCount * elementSize + settingsSize),
_pin(pin) _pin(pin)
{ {
uint16_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize; construct(pixelCount, elementSize, settingsSize);
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs;
_i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize + resetSize;
// must have a 4 byte aligned buffer for i2s
uint32_t alignment = _i2sBufferSize % 4;
if (alignment)
{
_i2sBufferSize += 4 - alignment;
} }
_data = static_cast<uint8_t*>(malloc(_sizeData)); NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
// data cleared later in Begin() _sizeData(pixelCount * elementSize + settingsSize),
_pin(pin),
_i2sBuffer = static_cast<uint8_t*>(malloc(_i2sBufferSize)); _bus(channel)
// no need to initialize it, it gets overwritten on every send {
construct(pixelCount, elementSize, settingsSize);
} }
~NeoEsp32I2sMethodBase() ~NeoEsp32I2sMethodBase()
@@ -150,14 +158,21 @@ public:
bool IsReadyToUpdate() const bool IsReadyToUpdate() const
{ {
return (i2sWriteDone(T_BUS::I2sBusNumber)); return (i2sWriteDone(_bus.I2sBusNumber));
} }
void Initialize() void Initialize()
{ {
size_t dmaCount = (_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(T_BUS::I2sBusNumber, 16, T_SPEED::I2sSampleRate, I2S_CHAN_STEREO, I2S_FIFO_16BIT_DUAL, dmaCount, 0);
i2sSetPins(T_BUS::I2sBusNumber, _pin, T_INVERT::Inverted); i2sInit(_bus.I2sBusNumber,
16,
T_SPEED::I2sSampleRate,
I2S_CHAN_STEREO,
I2S_FIFO_16BIT_DUAL,
dmaBlockCount,
0);
i2sSetPins(_bus.I2sBusNumber, _pin, T_INVERT::Inverted);
} }
void Update(bool) void Update(bool)
@@ -170,7 +185,7 @@ public:
FillBuffers(); FillBuffers();
i2sWrite(T_BUS::I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false); i2sWrite(_bus.I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false);
} }
uint8_t* getData() const uint8_t* getData() const
@@ -186,12 +201,39 @@ public:
private: private:
const size_t _sizeData; // Size of '_data' buffer const size_t _sizeData; // Size of '_data' buffer
const uint8_t _pin; // output pin number const uint8_t _pin; // output pin number
const T_BUS _bus; // holds instance for multi bus support
uint8_t* _data; // Holds LED color values uint8_t* _data; // Holds LED color values
uint32_t _i2sBufferSize; // total size of _i2sBuffer uint32_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)
{
ESP_ERROR_CHECK(pixelCount >= 2 ? ESP_OK : ESP_ERR_INVALID_ARG);
uint16_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize;
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs;
_i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize + resetSize;
// must have a 4 byte aligned buffer for i2s
uint32_t alignment = _i2sBufferSize % 4;
if (alignment)
{
_i2sBufferSize += 4 - alignment;
}
_data = static_cast<uint8_t*>(malloc(_sizeData));
// data cleared later in Begin()
_i2sBuffer = static_cast<uint8_t*>(malloc(_i2sBufferSize));
// no need to initialize all of it, but since it contains
// "reset" bits that don't latter get overwritten we just clear it all
memset(_i2sBuffer, 0x00, _i2sBufferSize);
}
void FillBuffers() void FillBuffers()
{ {
const uint16_t bitpatterns[16] = const uint16_t bitpatterns[16] =
@@ -244,6 +286,21 @@ typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusOne, NeoEsp
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1400KbpsInvertedMethod; typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1400KbpsInvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Apa106InvertedMethod; typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Apa106InvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNWs2812xMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNSk6812Method;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNTm1814Method;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sN800KbpsMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sN400KbpsMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNApa106Method;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNWs2812xInvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNSk6812InvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNTm1814InvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sN800KbpsInvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sN400KbpsInvertedMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNApa106InvertedMethod;
#endif #endif
/* due to a core issue where requests to send aren't consistent, I2s is no longer the default /* due to a core issue where requests to send aren't consistent, I2s is no longer the default