forked from Makuna/NeoPixelBus
Esp32 i2s (#427)
* dynamic i2s bus numbers * stabile and consistent shows * faster to start send
This commit is contained in:
20
keywords.txt
20
keywords.txt
@@ -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
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user