From 33a3112c934a70262607c92a2df2e37b2c946508 Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Wed, 27 Dec 2017 16:02:33 +0100 Subject: [PATCH 1/6] Fix I2S base frequency --- cores/esp8266/esp8266_peri.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/esp8266_peri.h b/cores/esp8266/esp8266_peri.h index 3610cb6934..01f2a417cd 100644 --- a/cores/esp8266/esp8266_peri.h +++ b/cores/esp8266/esp8266_peri.h @@ -754,7 +754,7 @@ extern uint8_t esp8266_gpioToFn[16]; #define i2c_bbpll_en_audio_clock_out_msb 7 #define i2c_bbpll_en_audio_clock_out_lsb 7 #define I2S_CLK_ENABLE() i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1) -#define I2SBASEFREQ (12000000L) +#define I2SBASEFREQ (160000000L) #define I2STXF ESP8266_REG(0xe00) //I2STXFIFO (32bit) #define I2SRXF ESP8266_REG(0xe04) //I2SRXFIFO (32bit) From f335b28ec9b9bc87aab1fe6af82107e510b28f9c Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Wed, 27 Dec 2017 16:11:08 +0100 Subject: [PATCH 2/6] Update frequency dividers for I2S Reference: https://github.com/espressif/ESP8266_MP3_DECODER/blob/master/mp3/driver/i2s_freertos.c --- cores/esp8266/core_esp8266_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index 119e8e78a6..559e62407d 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -218,7 +218,7 @@ void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ //!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); - I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((i2s_bck_div-1) << I2SBD) | ((i2s_clock_div-1) << I2SCD); + I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((i2s_bck_div) << I2SBD) | ((i2s_clock_div) << I2SCD); } void ICACHE_FLASH_ATTR i2s_begin(){ From 941e68e484d9ca0438b7ed94750f6e78d4d53f6e Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Thu, 18 Jan 2018 00:11:31 +0100 Subject: [PATCH 3/6] fixed i2s_set_rate, added i2s_get_real_rate() and i2s_set_dividers --- cores/esp8266/core_esp8266_i2s.c | 33 +++++++++++++++++++++++++++++--- cores/esp8266/i2s.h | 4 +++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index 559e62407d..bfc34b4d83 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -212,15 +212,42 @@ static uint32_t _i2s_sample_rate; void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ if(rate == _i2s_sample_rate) return; _i2s_sample_rate = rate; - uint32_t i2s_clock_div = (I2SBASEFREQ/(_i2s_sample_rate*32)) & I2SCDM; - uint8_t i2s_bck_div = (I2SBASEFREQ/(_i2s_sample_rate*i2s_clock_div*2)) & I2SBDM; + + uint32_t scaled_base_freq = I2SBASEFREQ/32; + float delta_best = scaled_base_freq; + + uint8_t sbd_div_best=1; + uint8_t scd_div_best=1; + for (uint8_t i=1; i<64; i++){ + for (uint8_t j=i; j<64; j++){ + if (fabs(((float)scaled_base_freq/i/j) - rate) < delta_best){ + delta_best = fabs(((float)scaled_base_freq/i/j) - rate); + sbd_div_best = i; + scd_div_best = j; + } + } + } + //os_printf("Rate %u Div %u Bck %u Frq %u\n", _i2s_sample_rate, i2s_clock_div, i2s_bck_div, I2SBASEFREQ/(i2s_clock_div*i2s_bck_div*2)); //!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); - I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((i2s_bck_div) << I2SBD) | ((i2s_clock_div) << I2SCD); + I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((sbd_div_best) << I2SBD) | ((scd_div_best) << I2SCD); +} + +void ICACHE_FLASH_ATTR i2s_set_dividers(uint8_t div1, uint8_t div2){ + div1 &= I2SBDM; + div2 &= I2SCDM; + + I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); + I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (div1 << I2SBD) | (div2 << I2SCD); } +float ICACHE_FLASH_ATTR i2s_get_real_rate(){ + return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM); +} + + void ICACHE_FLASH_ATTR i2s_begin(){ _i2s_sample_rate = 0; i2s_slc_begin(); diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h index fd8e0ab0e4..64e306d650 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/i2s.h @@ -43,6 +43,8 @@ extern "C" { void i2s_begin(); void i2s_end(); void i2s_set_rate(uint32_t rate);//Sample Rate in Hz (ex 44100, 48000) +void i2s_set_dividers(uint8_t div1, uint8_t div2);//Direct control over output rate +float i2s_get_real_rate();//The actual Sample Rate on output bool i2s_write_sample(uint32_t sample);//32bit sample with channels being upper and lower 16 bits (blocking when DMA is full) bool i2s_write_sample_nb(uint32_t sample);//same as above but does not block when DMA is full and returns false instead bool i2s_write_lr(int16_t left, int16_t right);//combines both channels and calls i2s_write_sample with the result @@ -54,4 +56,4 @@ int16_t i2s_available();// returns the number of samples than can be written bef } #endif -#endif \ No newline at end of file +#endif From b56bc495f3afe76639ef91a0803fbcbe93aba8b5 Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Thu, 18 Jan 2018 13:45:01 +0100 Subject: [PATCH 4/6] Minimise float calculations Less is more --- cores/esp8266/core_esp8266_i2s.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index bfc34b4d83..942a0e73b4 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -220,8 +220,9 @@ void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ uint8_t scd_div_best=1; for (uint8_t i=1; i<64; i++){ for (uint8_t j=i; j<64; j++){ - if (fabs(((float)scaled_base_freq/i/j) - rate) < delta_best){ - delta_best = fabs(((float)scaled_base_freq/i/j) - rate); + float new_delta = fabs(((float)scaled_base_freq/i/j) - rate); + if (new_delta < delta_best){ + delta_best = new_delta; sbd_div_best = i; scd_div_best = j; } From f2c98418cf771c86f4b17b42a39315d144c11bd6 Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Sun, 21 Jan 2018 01:52:23 +0100 Subject: [PATCH 5/6] added optional callback after every DMA interrupt for better timing control --- cores/esp8266/core_esp8266_i2s.c | 6 ++++++ cores/esp8266/i2s.h | 1 + 2 files changed, 7 insertions(+) diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index bfc34b4d83..2ee558103b 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -56,6 +56,7 @@ static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer static struct slc_queue_item i2s_slc_items[SLC_BUF_CNT]; //I2S DMA buffer descriptors static uint32_t *i2s_curr_slc_buf=NULL;//current buffer for writing static int i2s_curr_slc_buf_pos=0; //position in the current buffer +static void (*i2s_callback) (void)=0; bool ICACHE_FLASH_ATTR i2s_is_full(){ return (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) && (i2s_slc_queue_len == 0); @@ -92,10 +93,15 @@ void ICACHE_FLASH_ATTR i2s_slc_isr(void) { i2s_slc_queue_next_item(); //free space for finished_item } i2s_slc_queue[i2s_slc_queue_len++] = finished_item->buf_ptr; + if (i2s_callback) i2s_callback(); ETS_SLC_INTR_ENABLE(); } } +void ICACHE_FLASH_ATTR i2s_set_callback(void (*callback) (void)){ + i2s_callback = callback; +} + void ICACHE_FLASH_ATTR i2s_slc_begin(){ i2s_slc_queue_len = 0; int x, y; diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h index 64e306d650..925dfed509 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/i2s.h @@ -51,6 +51,7 @@ bool i2s_write_lr(int16_t left, int16_t right);//combines both channels and call bool i2s_is_full();//returns true if DMA is full and can not take more bytes (overflow) bool i2s_is_empty();//returns true if DMA is empty (underflow) int16_t i2s_available();// returns the number of samples than can be written before blocking +void i2s_set_callback(void (*callback) (void)); #ifdef __cplusplus } From aad44536beccbcc528a582969ab638a0cff0a908 Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Sun, 18 Mar 2018 14:14:15 +0100 Subject: [PATCH 6/6] Removed function from IRAM, added comment for better clarity --- cores/esp8266/core_esp8266_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index 5d0208f910..4251a7036a 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -56,7 +56,7 @@ static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer static struct slc_queue_item i2s_slc_items[SLC_BUF_CNT]; //I2S DMA buffer descriptors static uint32_t *i2s_curr_slc_buf=NULL;//current buffer for writing static int i2s_curr_slc_buf_pos=0; //position in the current buffer -static void (*i2s_callback) (void)=0; +static void (*i2s_callback) (void)=0; //Callback function should be defined as 'void ICACHE_FLASH_ATTR function_name()', placing the function in IRAM for faster execution. Avoid long computational tasks in this function, use it to set flags and process later. bool ICACHE_FLASH_ATTR i2s_is_full(){ return (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) && (i2s_slc_queue_len == 0); @@ -98,7 +98,7 @@ void ICACHE_FLASH_ATTR i2s_slc_isr(void) { } } -void ICACHE_FLASH_ATTR i2s_set_callback(void (*callback) (void)){ +void i2s_set_callback(void (*callback) (void)){ i2s_callback = callback; }