diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index 3a7ca505ed..6e03e325ee 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -1,11 +1,11 @@ -/* +/* i2s.c - Software I2S library for esp8266 - + Code taken and reworked from espessif's I2S example - + Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -30,9 +30,6 @@ extern void ets_wdt_enable(void); extern void ets_wdt_disable(void); -#define SLC_BUF_CNT (8) //Number of buffers in the I2S circular buffer -#define SLC_BUF_LEN (64) //Length of one buffer, in 32-bit words. - //We use a queue to keep track of the DMA buffers that are empty. The ISR will push buffers to the back of the queue, //the mp3 decode will pull them from the front and fill them. For ease, the queue will contain *pointers* to the DMA //buffers, not the data itself. The queue depth is one smaller than the amount of buffers we have, because there's @@ -40,21 +37,24 @@ extern void ets_wdt_disable(void); //simultaneously. struct slc_queue_item { - uint32 blocksize:12; - uint32 datalen:12; - uint32 unused:5; - uint32 sub_sof:1; - uint32 eof:1; - uint32 owner:1; - uint32 buf_ptr; - uint32 next_link_ptr; + uint32_t blocksize:12; + uint32_t datalen:12; + uint32_t unused:5; + uint32_t sub_sof:1; + uint32_t eof:1; + uint32_t owner:1; + uint32_t * buf_ptr; + struct slc_queue_item * next_link_ptr; }; -static uint32_t i2s_slc_queue[SLC_BUF_CNT-1]; +static size_t SLC_BUF_CNT = 0; //Number of buffers in the I2S circular buffer +static size_t SLC_BUF_LEN = 0; //Length of one buffer, in 32-bit words. + +static uint32_t ** i2s_slc_queue; static uint8_t i2s_slc_queue_len; -static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer data -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 uint32_t * i2s_slc_buf_pntr=NULL; // Pointer to the I2S DMA buffer data +static struct slc_queue_item * i2s_slc_items; //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 bool ICACHE_FLASH_ATTR i2s_is_full(){ @@ -65,9 +65,9 @@ bool ICACHE_FLASH_ATTR i2s_is_empty(){ return (i2s_slc_queue_len >= SLC_BUF_CNT-1); } -uint32_t ICACHE_FLASH_ATTR i2s_slc_queue_next_item(){ //pop the top off the queue +uint32_t * ICACHE_FLASH_ATTR i2s_slc_queue_next_item(){ //pop the top off the queue uint8_t i; - uint32_t item = i2s_slc_queue[0]; + uint32_t * item = i2s_slc_queue[0]; i2s_slc_queue_len--; for(i=0;i5 for I2S data + // I2S_CLKM_DIV_NUM - 5 - 63 + // I2S_BCK_DIV_NUM - 2 - 63 + for (int bckdiv = 2; bckdiv < 64; bckdiv++) { + for (int clkmdiv = 5; clkmdiv < 64; clkmdiv++) { + uint32_t testfreq = basefreq / (bckdiv * clkmdiv * 32); + if (abs(_i2s_sample_rate - testfreq) < abs(_i2s_sample_rate - bestfreq)) { + bestfreq = testfreq; + i2s_clkm_div = clkmdiv; + i2s_bck_div = bckdiv; + } + } + } + + // Apply the sample rate + // ~I2S_TRANS_SLAVE_MOD (TX master mode) + // ~I2S_BITS_MOD + // I2S_RIGHT_FIRST + // I2S_MSB_RIGHT + // I2S_RECE_SLAVE_MOD (RX slave mode) + // I2S_RECE_MSB_SHIFT (??) + // I2S_TRANS_MSB_SHIFT (??) + + // !trans master, !bits mod, + // rece slave mod, rece msb shift, right first, msb right + I2SC &= ~( I2STSM | // TX master mode + I2STMS | // TX LSB first + (I2SBMM << I2SBM) | // clear bits mode + (I2SBDM << I2SBD) | // clear bck_div + (I2SCDM << I2SCD)); // clear clkm_div + I2SC |= I2SRF | // right first + I2SMR | // MSB first + I2SRSM | // RX slave mode + I2SRMS | // receive MSB shift + // bits_mode == 0 (16bit) + ((i2s_bck_div & I2SBDM) << I2SBD) | // set bck_div + ((i2s_clkm_div & I2SCDM) << I2SCD); // set clkm_div } void ICACHE_FLASH_ATTR i2s_begin(){ - _i2s_sample_rate = 0; i2s_slc_begin(); - + pinMode(2, FUNCTION_1); //I2SO_WS (LRCK) pinMode(3, FUNCTION_1); //I2SO_DATA (SDIN) pinMode(15, FUNCTION_1); //I2SO_BCK (SCLK) - + I2S_CLK_ENABLE(); I2SIC = 0x3F; I2SIE = 0; - + //Reset I2S I2SC &= ~(I2SRST); I2SC |= I2SRST; I2SC &= ~(I2SRST); - + I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); //Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) I2SFC |= I2SDE; //Enable DMA I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); //Set RX/TX CHAN_MOD=0 - i2s_set_rate(44100); + + // defaults to 44100 if unset + i2s_set_rate(_i2s_sample_rate == 0 ? 44100 : _i2s_sample_rate); + I2SC |= I2STXS; //Start transmission } diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h index f248d590c4..c3db74863b 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/i2s.h @@ -1,9 +1,9 @@ -/* +/* i2s.h - Software I2S library for esp8266 Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -40,12 +40,17 @@ speed. extern "C" { #endif +void i2s_init(uint32_t count, uint32_t length); // allocate `count` of `length` buffers. total `count` * `length` * `sizeof(uint32_t)` bytes. +void i2s_deinit(); // free the allocated buffers void i2s_begin(); void i2s_end(); void i2s_set_rate(uint32_t rate);//Sample Rate in Hz (ex 44100, 48000) 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 +bool i2s_write_lr_nb(int16_t left, int16_t right);//same as above but does not block when DMA is full and returns false instead +uint32_t * i2s_get_buffer(); // buffer available for writing, nullptr if not applicatable. +void i2s_put_buffer(); // yup you guessed it. 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) @@ -53,4 +58,4 @@ bool i2s_is_empty();//returns true if DMA is empty (underflow) } #endif -#endif \ No newline at end of file +#endif