forked from Makuna/NeoPixelBus
Esp32 i2s (#427)
* dynamic i2s bus numbers * stabile and consistent shows * faster to start send
This commit is contained in:
22
keywords.txt
22
keywords.txt
@@ -166,6 +166,12 @@ NeoEsp8266BitBangLc8812InvertedMethod KEYWORD1
|
||||
NeoEsp8266BitBangApa106InvertedMethod KEYWORD1
|
||||
NeoEsp8266BitBang800KbpsInvertedMethod KEYWORD1
|
||||
NeoEsp8266BitBang400KbpsInvertedMethod KEYWORD1
|
||||
NeoEsp32I2sNWs2812xMethod KEYWORD1
|
||||
NeoEsp32I2sNSk6812Method KEYWORD1
|
||||
NeoEsp32I2sNTm1814Method KEYWORD1
|
||||
NeoEsp32I2sN800KbpsMethod KEYWORD1
|
||||
NeoEsp32I2sN400KbpsMethod KEYWORD1
|
||||
NeoEsp32I2sNApa106Method KEYWORD1
|
||||
NeoEsp32I2s0Ws2812xMethod KEYWORD1
|
||||
NeoEsp32I2s0Sk6812Method KEYWORD1
|
||||
NeoEsp32I2s0Tm1814Method KEYWORD1
|
||||
@@ -178,6 +184,12 @@ NeoEsp32I2s1Tm1814Method KEYWORD1
|
||||
NeoEsp32I2s1800KbpsMethod KEYWORD1
|
||||
NeoEsp32I2s1400KbpsMethod KEYWORD1
|
||||
NeoEsp32I2s1Apa106Method KEYWORD1
|
||||
NeoEsp32I2sNWs2812xInvertedMethod KEYWORD1
|
||||
NeoEsp32I2sNSk6812InvertedMethod KEYWORD1
|
||||
NeoEsp32I2sNTm1814InvertedMethod KEYWORD1
|
||||
NeoEsp32I2sN800KbpsInvertedMethod KEYWORD1
|
||||
NeoEsp32I2sN400KbpsInvertedMethod KEYWORD1
|
||||
NeoEsp32I2sNApa106InvertedMethod KEYWORD1
|
||||
NeoEsp32I2s0Ws2812xInvertedMethod KEYWORD1
|
||||
NeoEsp32I2s0Sk6812InvertedMethod KEYWORD1
|
||||
NeoEsp32I2s0Tm1814InvertedMethod KEYWORD1
|
||||
@@ -584,4 +596,12 @@ NeoTopologyHint_FirstOnPanel LITERAL1
|
||||
NeoTopologyHint_InPanel LITERAL1
|
||||
NeoTopologyHint_LastOnPanel 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
|
||||
|
@@ -52,9 +52,12 @@
|
||||
|
||||
#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr))))
|
||||
|
||||
#define I2S_DMA_QUEUE_SIZE 16
|
||||
|
||||
#define I2S_DMA_SILENCE_LEN 256 // bytes
|
||||
#define I2S_DMA_BLOCK_COUNT_DEFAULT 16
|
||||
// 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
|
||||
#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 {
|
||||
uint32_t blocksize: 12; // datalen
|
||||
@@ -91,24 +94,29 @@ typedef struct {
|
||||
size_t dma_count;
|
||||
uint32_t dma_buf_len :12;
|
||||
uint32_t unused :20;
|
||||
volatile uint32_t is_sending_data;
|
||||
} 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)
|
||||
// (I2S_NUM_MAX == 2)
|
||||
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},
|
||||
{&I2S1, -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_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}
|
||||
};
|
||||
#else
|
||||
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
|
||||
|
||||
void IRAM_ATTR i2sDmaISR(void* arg);
|
||||
bool i2sInitDmaItems(uint8_t bus_num);
|
||||
|
||||
bool i2sInitDmaItems(uint8_t bus_num) {
|
||||
if (bus_num >= I2S_NUM_MAX) {
|
||||
@@ -118,8 +126,10 @@ bool i2sInitDmaItems(uint8_t bus_num) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t dmaCount = I2S[bus_num].dma_count;
|
||||
|
||||
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) {
|
||||
log_e("MEM ERROR!");
|
||||
return false;
|
||||
@@ -127,12 +137,15 @@ bool i2sInitDmaItems(uint8_t bus_num) {
|
||||
}
|
||||
|
||||
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++) {
|
||||
i2 = (i+1) % I2S[bus_num].dma_count;
|
||||
for(i=0; i< dmaCount; i++) {
|
||||
itemPrev = item;
|
||||
|
||||
i2 = (i+1) % dmaCount;
|
||||
item = &I2S[bus_num].dma_items[i];
|
||||
item->eof = 1;
|
||||
item->eof = 0;
|
||||
item->owner = 1;
|
||||
item->sub_sof = 0;
|
||||
item->unused = 0;
|
||||
@@ -141,23 +154,12 @@ bool i2sInitDmaItems(uint8_t bus_num) {
|
||||
item->datalen = I2S[bus_num].silence_len;
|
||||
item->next = &I2S[bus_num].dma_items[i2];
|
||||
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
|
||||
log_e("MEM ERROR!");
|
||||
free(I2S[bus_num].dma_items);
|
||||
@@ -167,14 +169,6 @@ bool i2sInitDmaItems(uint8_t bus_num) {
|
||||
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) {
|
||||
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (bus_num >= I2S_NUM_MAX) {
|
||||
return;
|
||||
@@ -274,15 +237,22 @@ bool i2sWriteDone(uint8_t bus_num) {
|
||||
if (bus_num >= I2S_NUM_MAX) {
|
||||
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) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IRAM_ATTR i2sDmaISR(void* arg)
|
||||
{
|
||||
i2s_dma_item_t* dummy = NULL;
|
||||
i2s_bus_t* dev = (i2s_bus_t*)(arg);
|
||||
portBASE_TYPE hpTaskAwoken = 0;
|
||||
|
||||
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;
|
||||
item->blocksize = dev->silence_len;
|
||||
item->datalen = dev->silence_len;
|
||||
if (xQueueIsQueueFullFromISR(dev->tx_queue) == pdTRUE) {
|
||||
xQueueReceiveFromISR(dev->tx_queue, &dummy, &hpTaskAwoken);
|
||||
if (dev->bus->int_st.out_eof)
|
||||
{
|
||||
// i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr);
|
||||
if (dev->is_sending_data == I2s_Is_Pending)
|
||||
{
|
||||
dev->is_sending_data = I2s_Is_Idle;
|
||||
}
|
||||
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;
|
||||
}
|
||||
xQueueSendFromISR(dev->tx_queue, (void*)&item, &hpTaskAwoken);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) {
|
||||
return 0;
|
||||
}
|
||||
size_t index = 0;
|
||||
size_t toSend = len;
|
||||
size_t limit = I2S_DMA_MAX_DATA_LEN;
|
||||
i2s_dma_item_t* item = NULL;
|
||||
size_t blockSize = len;
|
||||
|
||||
while (len) {
|
||||
toSend = len;
|
||||
if (toSend > limit) {
|
||||
toSend = limit;
|
||||
}
|
||||
i2s_dma_item_t* itemPrev = NULL;
|
||||
i2s_dma_item_t* item = &I2S[bus_num].dma_items[0];
|
||||
size_t dataLeft = len;
|
||||
uint8_t* pos = data;
|
||||
|
||||
if (xQueueReceive(I2S[bus_num].tx_queue, &item, portMAX_DELAY) == pdFALSE) {
|
||||
log_e("xQueueReceive failed\n");
|
||||
break;
|
||||
// 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 = data + index;
|
||||
item->blocksize = toSend;
|
||||
item->datalen = toSend;
|
||||
item->data = pos;
|
||||
item->blocksize = blockSize;
|
||||
item->datalen = blockSize;
|
||||
|
||||
len -= toSend;
|
||||
index += toSend;
|
||||
itemPrev = item;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -18,16 +18,19 @@ typedef enum {
|
||||
I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE
|
||||
} 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 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 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);
|
||||
bool i2sWriteDone(uint8_t bus_num);
|
||||
|
||||
|
@@ -87,15 +87,32 @@ public:
|
||||
class NeoEsp32I2sBusZero
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sBusZero() {};
|
||||
|
||||
const static uint8_t I2sBusNumber = 0;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sBusOne
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sBusOne() {};
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
@@ -115,24 +132,15 @@ public:
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
uint16_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize;
|
||||
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
|
||||
uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs;
|
||||
construct(pixelCount, elementSize, settingsSize);
|
||||
}
|
||||
|
||||
_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 it, it gets overwritten on every send
|
||||
NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_bus(channel)
|
||||
{
|
||||
construct(pixelCount, elementSize, settingsSize);
|
||||
}
|
||||
|
||||
~NeoEsp32I2sMethodBase()
|
||||
@@ -150,14 +158,21 @@ public:
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return (i2sWriteDone(T_BUS::I2sBusNumber));
|
||||
return (i2sWriteDone(_bus.I2sBusNumber));
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
size_t dmaCount = (_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);
|
||||
size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
||||
|
||||
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)
|
||||
@@ -170,7 +185,7 @@ public:
|
||||
|
||||
FillBuffers();
|
||||
|
||||
i2sWrite(T_BUS::I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false);
|
||||
i2sWrite(_bus.I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false);
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
@@ -186,12 +201,39 @@ public:
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer
|
||||
const uint8_t _pin; // output pin number
|
||||
|
||||
const T_BUS _bus; // holds instance for multi bus support
|
||||
|
||||
uint8_t* _data; // Holds LED color values
|
||||
|
||||
uint32_t _i2sBufferSize; // total size of _i2sBuffer
|
||||
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()
|
||||
{
|
||||
const uint16_t bitpatterns[16] =
|
||||
@@ -244,6 +286,21 @@ typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusOne, NeoEsp
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1400KbpsInvertedMethod;
|
||||
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
|
||||
|
||||
/* due to a core issue where requests to send aren't consistent, I2s is no longer the default
|
||||
|
Reference in New Issue
Block a user