diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 9d1590f1af..39cd2fa264 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -49,7 +49,7 @@ static const char* I2S_TAG = "I2S"; #define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) #define I2S_FULL_DUPLEX_SLAVE_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) #define I2S_FULL_DUPLEX_MASTER_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) -#define APLL_MIN_FREQ (350000000) +#define APLL_MIN_FREQ (250000000) #define APLL_MAX_FREQ (500000000) #define APLL_I2S_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware /** @@ -83,8 +83,9 @@ typedef struct { int bytes_per_sample; /*!< Bytes per sample*/ int bits_per_sample; /*!< Bits per sample*/ i2s_mode_t mode; /*!< I2S Working mode*/ - int use_apll; /*!< I2S use APLL clock */ uint32_t sample_rate; /*!< I2S sample rate */ + bool use_apll; /*!< I2S use APLL clock */ + int fixed_mclk; /*!< I2S fixed MLCK clock */ } i2s_obj_t; static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; @@ -92,20 +93,6 @@ static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; static int _i2s_adc_unit = -1; static int _i2s_adc_channel = -1; -/** - * @brief Pre define APLL parameters, save compute time - * | bits_per_sample | rate | sdm0 | sdm1 | sdm2 | odir - */ -static const int apll_predefine[][6] = { - {16, 11025, 38, 80, 5, 31}, - {16, 16000, 147, 107, 5, 21}, - {16, 22050, 130, 152, 5, 15}, - {16, 32000, 129, 212, 5, 10}, - {16, 44100, 15, 8, 5, 6}, - {16, 48000, 136, 212, 5, 6}, - {16, 96000, 143, 212, 5, 2}, - {0, 0, 0, 0, 0, 0} -}; static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len); static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma); @@ -121,16 +108,16 @@ static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) return ESP_OK; } -inline static void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) +inline static void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) { //if pin = -1, do not need to configure if (gpio != -1) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); gpio_set_direction(gpio, GPIO_MODE_DEF_OUTPUT); gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv); - } -} -inline static void gpio_matrix_in_check(uint32_t gpio, uint32_t signal_idx, bool inv) + } +} +inline static void gpio_matrix_in_check(uint32_t gpio, uint32_t signal_idx, bool inv) { if (gpio != -1) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); @@ -191,7 +178,7 @@ static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void } -static float i2s_get_apll_real_rate(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) +static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) { int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; uint32_t is_rev0 = (GET_PERI_REG_BITS2(EFUSE_BLK0_RDATA3_REG, 1, 15) == 0); @@ -201,37 +188,37 @@ static float i2s_get_apll_real_rate(int bits_per_sample, int sdm0, int sdm1, int } float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4); if (fout < APLL_MIN_FREQ || fout > APLL_MAX_FREQ) { - return 9999999; + return APLL_MAX_FREQ; } float fpll = fout / (2 * (odir+2)); //== fi2s (N=1, b=0, a=1) - return fpll/(8*4*bits_per_sample); //fbck = fi2s/bck_div + return fpll/2; } /** * @brief APLL calculate function, was described by following: * APLL Output frequency is given by the formula: - * + * * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) * apll_freq = fout / ((o_div + 2) * 2) - * + * * The dividend in this expression should be in the range of 240 - 600 MHz. * In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. * * sdm0 frequency adjustment parameter, 0..255 * * sdm1 frequency adjustment parameter, 0..255 * * sdm2 frequency adjustment parameter, 0..63 * * o_div frequency divider, 0..31 - * - * The most accurate way to find the sdm0..2 and odir parameters is to loop through them all, - * then apply the above formula, finding the closest frequency to the desired one. + * + * The most accurate way to find the sdm0..2 and odir parameters is to loop through them all, + * then apply the above formula, finding the closest frequency to the desired one. * But 256*256*64*32 = 134.217.728 loops are too slow with ESP32 * 1. We will choose the parameters with the highest level of change, - * With 350MHzmode & I2S_MODE_RX) { save_rx = p_i2s_obj[i2s_num]->rx; - + p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, p_i2s_obj[i2s_num]->dma_buf_len); if (p_i2s_obj[i2s_num]->rx == NULL){ ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); @@ -417,7 +393,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b i2s_destroy_dma_queue(i2s_num, save_rx); } } - + } double mclk; @@ -453,18 +429,24 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b mclk = clkmInteger + denom * clkmDecimals; bck = factor/(bits * channel); } - int sdm0, sdm1, sdm2, odir; - if(p_i2s_obj[i2s_num]->use_apll && i2s_apll_calculate(rate, bits, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { + int sdm0, sdm1, sdm2, odir, m_scale = 8; + int fi2s_clk = rate*channel*bits*m_scale; + if(p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) { + fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; + m_scale = fi2s_clk/bits/rate/channel; + } + if(p_i2s_obj[i2s_num]->use_apll && i2s_apll_calculate_fi2s(fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { + ESP_LOGD(I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, odir); I2S[i2s_num]->clkm_conf.clkm_div_num = 1; I2S[i2s_num]->clkm_conf.clkm_div_b = 0; I2S[i2s_num]->clkm_conf.clkm_div_a = 1; - I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = 8; - I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = 8; + I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = m_scale; + I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = m_scale; I2S[i2s_num]->clkm_conf.clka_en = 1; - double real_rate = i2s_get_apll_real_rate(bits, sdm0, sdm1, sdm2, odir); - ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, real_rate, bits, 1, 8, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 1, 0); + double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); + ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0); } else { I2S[i2s_num]->clkm_conf.clka_en = 0; I2S[i2s_num]->clkm_conf.clkm_div_a = 63; @@ -476,10 +458,10 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b ESP_LOGI(I2S_TAG, "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals); } - + I2S[i2s_num]->sample_rate_conf.tx_bits_mod = bits; I2S[i2s_num]->sample_rate_conf.rx_bits_mod = bits; - + // wait all writing on-going finish if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); @@ -930,7 +912,7 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co I2S[i2s_num]->conf.rx_right_first = 0; I2S[i2s_num]->conf.rx_slave_mod = 0; // Master I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1; - + if (i2s_config->mode & I2S_MODE_SLAVE) { I2S[i2s_num]->conf.rx_slave_mod = 1;//RX Slave } @@ -1001,6 +983,7 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co } p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll; + p_i2s_obj[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; return ESP_OK; } diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index f8f6b2a7c2..ece69ae183 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -138,7 +138,8 @@ typedef struct { int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */ int dma_buf_count; /*!< I2S DMA Buffer Count */ int dma_buf_len; /*!< I2S DMA Buffer Length */ - int use_apll; /*!< I2S using APLL as main I2S clock, enable it to get accurate clock */ + bool use_apll; /*!< I2S using APLL as main I2S clock, enable it to get accurate clock */ + int fixed_mclk; /*!< I2S using fixed MCLK output. If use_apll = true and fixed_mclk > 0, then the clock output for i2s is fixed and equal to the fixed_mclk value.*/ } i2s_config_t; /** @@ -391,7 +392,7 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); /** * @brief Set clock & bit width used for I2S RX and TX. - * + * * Similar to i2s_set_sample_rates(), but also sets bit width. * * @param i2s_num I2S_NUM_0, I2S_NUM_1 @@ -401,7 +402,7 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); * @param bits I2S bit width (I2S_BITS_PER_SAMPLE_16BIT, I2S_BITS_PER_SAMPLE_24BIT, I2S_BITS_PER_SAMPLE_32BIT) * * @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO) - * + * * @return * - ESP_OK Success * - ESP_FAIL Parameter error diff --git a/docs/api-reference/peripherals/i2s.rst b/docs/api-reference/peripherals/i2s.rst index ccd817c55e..335d6cb2d9 100644 --- a/docs/api-reference/peripherals/i2s.rst +++ b/docs/api-reference/peripherals/i2s.rst @@ -10,7 +10,9 @@ The I2S peripheral supports DMA meaning it can stream sample data without requir I2S output can also be routed directly to the Digital/Analog Converter output channels (GPIO 25 & GPIO 26) to produce analog output directly, rather than via an external I2S codec. -.. note:: For high accuracy clock applications, APLL clock source can be used with `.use_apll = 1` and ESP32 will automatic caculate APLL parameter. +.. note:: For high accuracy clock applications, APLL clock source can be used with `.use_apll = true` and ESP32 will automatically calculate APLL parameter. + +.. note:: If `use_apll = true` and `fixed_mclk > 0`, then the Master clock output for I2S is fixed and equal to the fixed_mclk value. The audio clock rate (LRCK) is always the MCLK divisor and 0 < MCLK/LRCK/channels/bits_per_sample < 64 Application Example ------------------- @@ -37,7 +39,7 @@ Short example of I2S configuration: .intr_alloc_flags = 0, // default interrupt priority .dma_buf_count = 8, .dma_buf_len = 64, - .use_apll = 0 + .use_apll = false }; static const i2s_pin_config_t pin_config = { @@ -73,7 +75,7 @@ Short example configuring I2S to use internal DAC for analog output:: .intr_alloc_flags = 0, // default interrupt priority .dma_buf_count = 8, .dma_buf_len = 64, - .use_apll = 0 + .use_apll = false }; ... diff --git a/examples/peripherals/i2s/main/i2s_example_main.c b/examples/peripherals/i2s/main/i2s_example_main.c index 7bdd16c73f..cf49a0885e 100644 --- a/examples/peripherals/i2s/main/i2s_example_main.c +++ b/examples/peripherals/i2s/main/i2s_example_main.c @@ -87,7 +87,7 @@ void app_main() .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, .dma_buf_count = 6, .dma_buf_len = 60, - .use_apll = 0, + .use_apll = false, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 }; i2s_pin_config_t pin_config = {