diff --git a/extras/debug/esp32/I2sDump/I2sDump.ino b/extras/debug/esp32/I2sDump/I2sDump.ino new file mode 100644 index 0000000..07a980c --- /dev/null +++ b/extras/debug/esp32/I2sDump/I2sDump.ino @@ -0,0 +1,26 @@ +#include + +#if !defined(ARDUINO_ARCH_ESP32) +@error ESP32 Supported Only +#endif + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach +} + +void loop() +{ + Serial.println(); + Serial.println(); + Serial.println("I2S0: "); + i2sDump(0); + + Serial.println(); + Serial.println(); + Serial.println("I2S1: "); + i2sDump(1); + + delay(10000); +} diff --git a/src/internal/methods/Esp32_i2s.c b/src/internal/methods/Esp32_i2s.c index 5893d19..76293c8 100644 --- a/src/internal/methods/Esp32_i2s.c +++ b/src/internal/methods/Esp32_i2s.c @@ -70,10 +70,13 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_ #endif #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 +// 50us reset will calculate out as a 16 byte silence/reset buffer +// The latest DMA buffer model this library uses will allow the +// silence blocks to as small as they can be (4 bytes) +// this also allows the primary data to send less of that silence since +// the silence state control blocks (2 front, 1 back) will consume 12 bytes +// of that reset time already +#define I2S_DMA_SILENCE_SIZE 4 // must be in 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 @@ -125,13 +128,14 @@ inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldes item->qe.stqe_next = itemNext; } -bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize) +bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize, bool parallel_mode, size_t bytes_per_sample) { if (bus_num >= I2S_NUM_MAX) { return false; } + size_t silenceSize = parallel_mode ? I2S_DMA_SILENCE_SIZE * 8 * bytes_per_sample : I2S_DMA_SILENCE_SIZE; size_t dmaCount = I2S[bus_num].dma_count; if (I2S[bus_num].dma_items == NULL) @@ -148,15 +152,21 @@ bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize) lldesc_t* item = itemFirst; // lldesc_t* itemsEnd = itemFirst + I2S[bus_num].dma_count; lldesc_t* itemNext = item + 1; - size_t dataLeft = dataSize; + // The primary data to map will excludes the time to process through the + // 3 silence control blocks. + // That data at the end is already silent reset time and no need to send it twice as + // part of the data and the control blocks + // makes the reset more accurate + size_t dataLeft = dataSize - (silenceSize * + (I2S_DMA_SILENCE_BLOCK_COUNT_FRONT + I2S_DMA_SILENCE_BLOCK_COUNT_BACK)); uint8_t* pos = data; - // at the end of the data is the encoded silence reset - uint8_t* posSilence = data + dataSize - I2S_DMA_SILENCE_SIZE; + // at the end of the data is the encoded silence reset, use it + uint8_t* posSilence = data + dataSize - silenceSize; // front two are silent items used for looping to micmic single fire // default to looping - dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemNext); - dmaItemInit(itemNext, posSilence, I2S_DMA_SILENCE_SIZE, item); + dmaItemInit(item, posSilence, silenceSize, itemNext); + dmaItemInit(itemNext, posSilence, silenceSize, item); item = itemNext; itemNext++; @@ -184,7 +194,7 @@ bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize) // last block, the back silent item, loops to front item = itemNext; - dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemFirst); + dmaItemInit(item, posSilence, silenceSize, itemFirst); return true; } @@ -202,14 +212,12 @@ bool i2sDeinitDmaItems(uint8_t bus_num) return true; } -// normal 4, 10, 63, 12, 16 - 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 + 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) { @@ -234,6 +242,7 @@ esp_err_t i2sSetClock(uint8_t bus_num, 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 + clkm_conf.clk_en = 1; clkm_conf.clka_en = 0; #endif @@ -405,7 +414,7 @@ void i2sInit(uint8_t bus_num, I2S_DMA_SILENCE_BLOCK_COUNT_FRONT + I2S_DMA_SILENCE_BLOCK_COUNT_BACK; - if (!i2sInitDmaItems(bus_num, data, dataSize)) + if (!i2sInitDmaItems(bus_num, data, dataSize, parallel_mode, bytes_per_sample)) { return; } @@ -476,7 +485,7 @@ void i2sInit(uint8_t bus_num, typeof(i2s->fifo_conf) fifo_conf; fifo_conf.val = 0; - fifo_conf.rx_fifo_mod_force_en = 1; + fifo_conf.rx_fifo_mod_force_en = 0; fifo_conf.tx_fifo_mod_force_en = 1; fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel @@ -486,10 +495,13 @@ void i2sInit(uint8_t bus_num, i2s->fifo_conf.val = fifo_conf.val; } - // $REVIEW old code didn't set this { typeof(i2s->conf1) conf1; conf1.val = 0; + + conf1.tx_pcm_conf = 1; + conf1.rx_pcm_bypass = 1; + conf1.tx_stop_en = 0; conf1.tx_pcm_bypass = 1; i2s->conf1.val = conf1.val; @@ -534,7 +546,8 @@ void i2sInit(uint8_t bus_num, #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // (I2S_NUM_MAX == 2) - if (bus_num == 1) { + if (bus_num == 1) + { i2sIntSource = ETS_I2S1_INTR_SOURCE; } else @@ -553,8 +566,8 @@ void i2sInit(uint8_t bus_num, { typeof(i2s->lc_conf) lc_conf; lc_conf.val = 0; - lc_conf.out_data_burst_en = 1; - lc_conf.indscr_burst_en = 1; + lc_conf.out_data_burst_en = 0; // ? + lc_conf.indscr_burst_en = 0; // ? i2s->lc_conf.val = lc_conf.val; } /* */ @@ -572,32 +585,147 @@ void i2sDeinit(uint8_t bus_num) i2sDeinitDmaItems(bus_num); } -esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, size_t bytes_per_sample) +void i2sUnitDecimalToFractionClks(uint8_t* resultN, + uint8_t* resultD, + double unitDecimal, + double accuracy) +{ + if (unitDecimal <= accuracy) + { + // no fractional + *resultN = 0; + *resultD = 1; + return; + } + else if (unitDecimal <= (1.0 / 63.0)) + { + // lowest fractional + *resultN = 0; + *resultD = 2; + return; + } + else if (unitDecimal >= (62.0 / 63.0)) + { + // highest fractional + *resultN = 2; + *resultD = 2; + return; + } + +// printf("\nSearching for %f\n", unitDecimal); + + // The lower fraction is 0 / 1 + uint16_t lowerN = 0; + uint16_t lowerD = 1; + double lowerDelta = unitDecimal; + + // The upper fraction is 1 / 1 + uint16_t upperN = 1; + uint16_t upperD = 1; + double upperDelta = 1.0 - unitDecimal; + + uint16_t closestN = 0; + uint16_t closestD = 1; + double closestDelta = lowerDelta; + + for (;;) + { + // The middle fraction is + // (lowerN + upperN) / (lowerD + upperD) + uint16_t middleN = lowerN + upperN; + uint16_t middleD = lowerD + upperD; + double middleUnit = (double)middleN / middleD; + + if (middleD > 63) + { + // exceeded our clock bits so break out + // and use closest we found so far + break; + } + + if (middleD * (unitDecimal + accuracy) < middleN) + { + // middle is our new upper + upperN = middleN; + upperD = middleD; + upperDelta = middleUnit - unitDecimal; + } + else if (middleN < (unitDecimal - accuracy) * middleD) + { + // middle is our new lower + lowerN = middleN; + lowerD = middleD; + lowerDelta = unitDecimal - middleUnit; + } + else + { + // middle is our best fraction + *resultN = middleN; + *resultD = middleD; + +// printf(" Match %d/%d = %f (%f)\n", middleN, middleD, middleUnit, unitDecimal - middleUnit); + return; + } + + // track the closest fraction so far (ONLY THE UPPER, so allow only slower Kbps) + // + //if (upperDelta < lowerDelta) + { + if (upperDelta < closestDelta) + { + closestN = upperN; + closestD = upperD; + closestDelta = upperDelta; + +// printf(" Upper %d/%d = %f (%f)\n", closestN, closestD, middleUnit, closestDelta); + } + } + /* + else + { + if (lowerDelta < closestDelta) + { + closestN = lowerN; + closestD = lowerD; + closestDelta = lowerDelta; + + printf(" Lower %d/%d = %f (%f)\n", closestN, closestD, middleUnit, closestDelta); + } + } + */ + } + +// printf(" Closest %d/%d = %f (%f)\n\n", closestN, closestD, (double)closestN / closestD, closestDelta); + // no perfect match, use the closest we found + // + *resultN = closestN; + *resultD = closestD; +} + +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) { return ESP_FAIL; } - uint8_t bck = 12; + uint8_t bck = 8; // parallel mode needs a higher sample rate // if (parallel_mode) { + rate *= bytes_per_sample; #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 } - // 160,000,000L / (100,000 * 384) - double clkmdiv = (double)I2S_BASE_CLK / ((rate * 384) + 1); + double clkmdiv = (double)I2S_BASE_CLK / (rate * 256.0); + if (clkmdiv > 256.0) { log_e("rate is too low"); @@ -616,9 +744,18 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, s // calc integer and franctional for more precise timing // uint8_t clkmInteger = clkmdiv; - uint8_t clkmFraction = (clkmdiv - clkmInteger) * 63.0; + double clkmFraction = (clkmdiv - clkmInteger); + uint8_t divB = 0; + uint8_t divA = 0; - i2sSetClock(bus_num, clkmInteger, clkmFraction, 63, bck, bytes_per_sample * 8); + i2sUnitDecimalToFractionClks(&divB, &divA, clkmFraction, 0.000001); + + i2sSetClock(bus_num, + clkmInteger, + divB, + divA, + bck, + bytes_per_sample * 8); return ESP_OK; } @@ -629,7 +766,6 @@ void IRAM_ATTR i2sDmaISR(void* arg) if (i2s->bus->int_st.out_eof) { - // lldesc_t* item = (lldesc_t*)(i2s->bus->out_eof_des_addr); if (i2s->is_sending_data != I2s_Is_Idle) { // the second item (last of the two front silent items) is @@ -666,6 +802,235 @@ bool i2sWrite(uint8_t bus_num) return true; } +#ifdef NEOPIXELBUS_I2S_DEBUG +void DumpI2sPrimary(const char* label, uint32_t val) +{ + printf("%s %08x\n", label, val); +} + +void DumpI2sSecondary(const char* label, uint32_t val) +{ + printf(" %s %u\n", label, val); +} + +void DumpI2s_sample_rate_conf(const char* label, i2s_dev_t* bus) +{ + typeof(bus->sample_rate_conf) val; + + val.val = bus->sample_rate_conf.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("tx_bck_div_num : ", val.tx_bck_div_num); + DumpI2sSecondary("rx_bck_div_num : ", val.rx_bck_div_num); + DumpI2sSecondary("tx_bits_mod : ", val.tx_bits_mod); + DumpI2sSecondary("rx_bits_mod : ", val.rx_bits_mod); +} + +void DumpI2s_conf1(const char* label, i2s_dev_t* bus) +{ + typeof(bus->conf1) val; + val.val = bus->conf1.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("tx_pcm_conf : ", val.tx_pcm_conf); + DumpI2sSecondary("tx_pcm_bypass : ", val.tx_pcm_bypass); + DumpI2sSecondary("rx_pcm_conf : ", val.rx_pcm_conf); + DumpI2sSecondary("rx_pcm_bypass : ", val.rx_pcm_bypass); + DumpI2sSecondary("tx_stop_en : ", val.tx_stop_en); + DumpI2sSecondary("tx_zeros_rm_en : ", val.tx_zeros_rm_en); +} + +void DumpI2s_clkm_conf(const char* label, i2s_dev_t* bus) +{ + typeof(bus->clkm_conf) val; + val.val = bus->clkm_conf.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("clkm_div_num : ", val.clkm_div_num); + DumpI2sSecondary("clkm_div_b : ", val.clkm_div_b); + DumpI2sSecondary("clkm_div_a : ", val.clkm_div_a); + DumpI2sSecondary("clk_en : ", val.clk_en); + DumpI2sSecondary("clka_en : ", val.clka_en); +} + +void DumpI2s_lc_conf(const char* label, i2s_dev_t* bus) +{ + typeof(bus->lc_conf) val; + val.val = bus->lc_conf.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("out_auto_wrback : ", val.out_auto_wrback); + DumpI2sSecondary("out_no_restart_clr : ", val.out_no_restart_clr); + DumpI2sSecondary("out_eof_mode : ", val.out_eof_mode); + DumpI2sSecondary("outdscr_burst_en : ", val.outdscr_burst_en); + DumpI2sSecondary("indscr_burst_en : ", val.indscr_burst_en); + DumpI2sSecondary("out_data_burst_en : ", val.out_data_burst_en); + DumpI2sSecondary("check_owner : ", val.check_owner); + DumpI2sSecondary("mem_trans_en : ", val.mem_trans_en); +} + +void DumpI2s_conf(const char* label, i2s_dev_t* bus) +{ + typeof(bus->conf) val; + val.val = bus->conf.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("tx_slave_mod : ", val.tx_slave_mod); + DumpI2sSecondary("rx_slave_mod : ", val.rx_slave_mod); + DumpI2sSecondary("tx_right_first : ", val.tx_right_first); + DumpI2sSecondary("rx_right_first : ", val.rx_right_first); + DumpI2sSecondary("tx_msb_shift : ", val.tx_msb_shift); + DumpI2sSecondary("rx_msb_shift : ", val.rx_msb_shift); + DumpI2sSecondary("tx_short_sync : ", val.tx_short_sync); + DumpI2sSecondary("rx_short_sync : ", val.rx_short_sync); + DumpI2sSecondary("tx_mono : ", val.tx_mono); + DumpI2sSecondary("rx_mono : ", val.rx_mono); + DumpI2sSecondary("tx_msb_right : ", val.tx_msb_right); + DumpI2sSecondary("rx_msb_right : ", val.rx_msb_right); + DumpI2sSecondary("sig_loopback : ", val.sig_loopback); +} + +void DumpI2s_fifo_conf(const char* label, i2s_dev_t* bus) +{ + typeof(bus->fifo_conf) val; + val.val = bus->fifo_conf.val; + + DumpI2sPrimary(label, val.val); + + DumpI2sSecondary("rx_data_num : ", val.rx_data_num); + DumpI2sSecondary("tx_data_num : ", val.tx_data_num); + DumpI2sSecondary("dscr_en : ", val.dscr_en); + DumpI2sSecondary("tx_fifo_mod : ", val.tx_fifo_mod); + DumpI2sSecondary("rx_fifo_mod : ", val.rx_fifo_mod); + DumpI2sSecondary("tx_fifo_mod_force_en : ", val.tx_fifo_mod_force_en); + DumpI2sSecondary("rx_fifo_mod_force_en : ", val.rx_fifo_mod_force_en); +} + +bool i2sDump(uint8_t bus_num) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + i2s_dev_t* i2s = I2S[bus_num].bus; + + DumpI2sPrimary("fifo_wr: ", i2s->fifo_wr); + DumpI2sPrimary("fifo_rd: ", i2s->fifo_rd); + DumpI2s_conf("conf: ", i2s); + DumpI2sPrimary("int_raw: ", i2s->int_raw.val); + DumpI2sPrimary("int_st: ", i2s->int_st.val); + DumpI2sPrimary("int_ena: ", i2s->int_ena.val); + DumpI2sPrimary("int_clr: ", i2s->int_clr.val); + DumpI2sPrimary("timing: ", i2s->timing.val); + DumpI2s_fifo_conf("fifo_conf: ", i2s); + DumpI2sPrimary("rx_eof_num: ", i2s->rx_eof_num); + DumpI2sPrimary("conf_single_data: ", i2s->conf_single_data); + DumpI2sPrimary("conf_chan: ", i2s->conf_chan.val); + DumpI2sPrimary("out_link: ", i2s->out_link.val); + DumpI2sPrimary("in_link: ", i2s->in_link.val); + DumpI2sPrimary("out_eof_des_addr: ", i2s->out_eof_des_addr); + DumpI2sPrimary("in_eof_des_addr: ", i2s->in_eof_des_addr); + DumpI2sPrimary("out_eof_bfr_des_addr: ", i2s->out_eof_bfr_des_addr); + DumpI2sPrimary("ahb_test: ", i2s->ahb_test.val); + + DumpI2sPrimary("in_link_dscr: ", i2s->in_link_dscr); + DumpI2sPrimary("in_link_dscr_bf0: ", i2s->in_link_dscr_bf0); + DumpI2sPrimary("in_link_dscr_bf1: ", i2s->in_link_dscr_bf1); + DumpI2sPrimary("out_link_dscr: ", i2s->out_link_dscr); + DumpI2sPrimary("out_link_dscr_bf0: ", i2s->out_link_dscr_bf0); + DumpI2sPrimary("out_link_dscr_bf1: ", i2s->out_link_dscr_bf1); + + DumpI2s_lc_conf("lc_conf: ", i2s); + + DumpI2sPrimary("out_fifo_push: ", i2s->out_fifo_push.val); + DumpI2sPrimary("in_fifo_pop: ", i2s->in_fifo_pop.val); + + DumpI2sPrimary("lc_state0: ", i2s->lc_state0); + DumpI2sPrimary("lc_state1: ", i2s->lc_state1); + DumpI2sPrimary("lc_hung_conf: ", i2s->lc_hung_conf.val); + + DumpI2sPrimary("reserved_78: ", i2s->reserved_78); + DumpI2sPrimary("reserved_7c: ", i2s->reserved_7c); + + DumpI2sPrimary("cvsd_conf0: ", i2s->cvsd_conf0.val); + DumpI2sPrimary("cvsd_conf1: ", i2s->cvsd_conf1.val); + DumpI2sPrimary("cvsd_conf2: ", i2s->cvsd_conf2.val); + + DumpI2sPrimary("plc_conf0: ", i2s->plc_conf0.val); + DumpI2sPrimary("plc_conf1: ", i2s->plc_conf1.val); + DumpI2sPrimary("plc_conf2: ", i2s->plc_conf2.val); + + DumpI2sPrimary("esco_conf0: ", i2s->esco_conf0.val); + DumpI2sPrimary("sco_conf0: ", i2s->sco_conf0.val); + + DumpI2s_conf1("conf1: ", i2s); + + DumpI2sPrimary("pd_conf: ", i2s->pd_conf.val); + DumpI2sPrimary("conf2: ", i2s->conf2.val); + + + DumpI2s_clkm_conf("clkm_conf: ", i2s); + DumpI2s_sample_rate_conf("sample_rate_conf: ", i2s); + + DumpI2sPrimary("pdm_conf: ", i2s->pdm_conf.val); + DumpI2sPrimary("pdm_freq_conf: ", i2s->pdm_freq_conf.val); + + DumpI2sPrimary("state: ", i2s->state.val); + + /* + uint32_t reserved_c0; + uint32_t reserved_c4; + uint32_t reserved_c8; + uint32_t reserved_cc; + uint32_t reserved_d0; + uint32_t reserved_d4; + uint32_t reserved_d8; + uint32_t reserved_dc; + uint32_t reserved_e0; + uint32_t reserved_e4; + uint32_t reserved_e8; + uint32_t reserved_ec; + uint32_t reserved_f0; + uint32_t reserved_f4; + uint32_t reserved_f8; + */ + DumpI2sPrimary("date: ", i2s->date); + + return true; +} + +bool i2sGetClks(uint8_t bus_num, + uint8_t* clkm_div_num, + uint8_t* clkm_div_b, + uint8_t* clkm_div_a) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + if (!clkm_div_num || !clkm_div_b || !clkm_div_a) + { + return false; + } + + i2s_dev_t* i2s = I2S[bus_num].bus; + + typeof(i2s->clkm_conf) val; + val.val = i2s->clkm_conf.val; + + *clkm_div_num = val.clkm_div_num; + *clkm_div_b = val.clkm_div_b; + *clkm_div_a = val.clkm_div_a; + + return true; +} +#endif + #endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #endif // defined(ARDUINO_ARCH_ESP32) diff --git a/src/internal/methods/Esp32_i2s.h b/src/internal/methods/Esp32_i2s.h index 9f961d3..8de14ec 100644 --- a/src/internal/methods/Esp32_i2s.h +++ b/src/internal/methods/Esp32_i2s.h @@ -12,11 +12,18 @@ extern "C" { #define I2S_DMA_MAX_DATA_LEN 4092// maximum bytes in one dma item typedef enum { - I2S_CHAN_STEREO, I2S_CHAN_RIGHT_TO_LEFT, I2S_CHAN_LEFT_TO_RIGHT, I2S_CHAN_RIGHT_ONLY, I2S_CHAN_LEFT_ONLY + I2S_CHAN_STEREO, + I2S_CHAN_RIGHT_TO_LEFT, + I2S_CHAN_LEFT_TO_RIGHT, + I2S_CHAN_RIGHT_ONLY, + I2S_CHAN_LEFT_ONLY } i2s_tx_chan_mod_t; 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; void i2sInit(uint8_t bus_num, @@ -41,6 +48,15 @@ void i2sSetClkWsPins(uint8_t bus_num, bool invertWs); bool i2sWrite(uint8_t bus_num); bool i2sWriteDone(uint8_t bus_num); +#ifdef NEOPIXELBUS_I2S_DEBUG +bool i2sDump(uint8_t bus_num); +bool i2sGetClks(uint8_t bus_num, uint8_t* clkm_div_num, uint8_t* clkm_div_b, uint8_t* clkm_div_a ); + +void i2sUnitDecimalToFractionClks(uint8_t* resultN, + uint8_t* resultD, + double unitDecimal, + double accuracy); +#endif #ifdef __cplusplus } diff --git a/src/internal/methods/NeoEsp32I2sMethod.h b/src/internal/methods/NeoEsp32I2sMethod.h index 5ebbd74..d4bf629 100644 --- a/src/internal/methods/NeoEsp32I2sMethod.h +++ b/src/internal/methods/NeoEsp32I2sMethod.h @@ -96,7 +96,7 @@ public: class NeoEsp32I2sSpeedApa106 { public: - const static uint32_t I2sSampleRate = 76000; + const static uint32_t I2sSampleRate = 72960; // 588,235 hz / 8 = 73,529 sample rate const static uint16_t ByteSendTimeUs = 14; const static uint16_t ResetTimeUs = 50; }; @@ -147,19 +147,19 @@ template class NeoEsp32I2sM public: typedef NeoNoSettings SettingsObject; - NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : - _sizeData(pixelCount * elementSize + settingsSize), + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t pixelSize, size_t settingsSize) : + _sizeData(pixelCount * pixelSize + settingsSize), _pin(pin) { - construct(pixelCount, elementSize, settingsSize); + construct(pixelCount, pixelSize, settingsSize); } - NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : - _sizeData(pixelCount * elementSize + settingsSize), + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t pixelSize, size_t settingsSize, NeoBusChannel channel) : + _sizeData(pixelCount * pixelSize + settingsSize), _pin(pin), _bus(channel) { - construct(pixelCount, elementSize, settingsSize); + construct(pixelCount, pixelSize, settingsSize); } ~NeoEsp32I2sMethodBase() @@ -242,30 +242,21 @@ private: size_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) + void construct(uint16_t pixelCount, size_t pixelSize, size_t settingsSize) { - // DMA is too fast to support a single pixel and maintain consistency - if (pixelCount < 2) - { - pixelCount = 2; - } - - 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(malloc(_sizeData)); // data cleared later in Begin() + // must have a 4 byte aligned buffer for i2s + // since the reset/silence at the end is used for looping + // it also needs to 4 byte aligned + size_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize; + size_t dmaPixelSize = c_dmaBytesPerPixelBytes * pixelSize; + size_t resetSize = NeoUtil::RoundUp(c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs, 4); + + _i2sBufferSize = NeoUtil::RoundUp(pixelCount * dmaPixelSize + dmaSettingsSize, 4) + + resetSize; + _i2sBuffer = static_cast(heap_caps_malloc(_i2sBufferSize, MALLOC_CAP_DMA)); // no need to initialize all of it, but since it contains // "reset" bits that don't latter get overwritten we just clear it all diff --git a/src/internal/methods/PixieStreamMethod.h b/src/internal/methods/PixieStreamMethod.h index 22cc3b8..e1d7ddb 100644 --- a/src/internal/methods/PixieStreamMethod.h +++ b/src/internal/methods/PixieStreamMethod.h @@ -40,7 +40,10 @@ class PixieStreamMethod public: typedef NeoNoSettings SettingsObject; - PixieStreamMethod(uint16_t pixelCount, size_t elementSize, size_t settingsSize, Stream* pixieStream) : + PixieStreamMethod(uint16_t pixelCount, + size_t elementSize, + size_t settingsSize, + Stream* pixieStream) : _sizeData(pixelCount * elementSize + settingsSize), _stream(pixieStream), _usEndTime(0)