Merge branch 'bugfix/i2s_apll_clock_fix_v3.3' into 'release/v3.3'

driver/i2s: fix apll_clock_rate for different sample rates (v3.3)

See merge request espressif/esp-idf!5448
This commit is contained in:
Ivan Grokhotkov 2019-07-17 14:33:59 +08:00
commit de7a50dca0
3 changed files with 84 additions and 2 deletions

View file

@ -90,6 +90,7 @@ typedef struct {
bool use_apll; /*!< I2S use APLL clock */ bool use_apll; /*!< I2S use APLL clock */
bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */
int fixed_mclk; /*!< I2S fixed MLCK clock */ int fixed_mclk; /*!< I2S fixed MLCK clock */
double real_rate;
} i2s_obj_t; } i2s_obj_t;
static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0};
@ -176,6 +177,12 @@ esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num)
return ESP_OK; return ESP_OK;
} }
float i2s_get_clk(i2s_port_t i2s_num)
{
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
return p_i2s_obj[i2s_num]->real_rate;
}
static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle)
{ {
return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle); return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle);
@ -266,6 +273,16 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm
*odir = _odir; *odir = _odir;
} }
} }
min_diff = APLL_MAX_FREQ;
for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, *odir);
min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, *odir);
avg = (max_rate + min_rate)/2;
if (abs(avg - rate) < min_diff) {
min_diff = abs(avg - rate);
*sdm2 = _sdm2;
}
}
min_diff = APLL_MAX_FREQ; min_diff = APLL_MAX_FREQ;
for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) { for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
@ -453,6 +470,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
I2S[i2s_num]->sample_rate_conf.rx_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; I2S[i2s_num]->clkm_conf.clka_en = 1;
double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir);
p_i2s_obj[i2s_num]->real_rate = fi2s_rate/bits/channel/m_scale;
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", 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); rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0);
} else { } else {
@ -463,6 +481,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck; I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck;
I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck; I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck;
double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2);
p_i2s_obj[i2s_num]->real_rate = real_rate;
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", 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); rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals);
} }

View file

@ -503,6 +503,16 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num);
*/ */
esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch); esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch);
/**
* @brief get clock set on particular port number.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - actual clock set by i2s driver
*/
float i2s_get_clk(i2s_port_t i2s_num);
/** /**
* @brief Set built-in ADC mode for I2S DMA, this function will initialize ADC pad, * @brief Set built-in ADC mode for I2S DMA, this function will initialize ADC pad,
* and set ADC parameters. * and set ADC parameters.

View file

@ -9,6 +9,7 @@
#include "freertos/task.h" #include "freertos/task.h"
#include "driver/i2s.h" #include "driver/i2s.h"
#include "unity.h" #include "unity.h"
#include "math.h"
#define SAMPLE_RATE (36000) #define SAMPLE_RATE (36000)
#define SAMPLE_BITS (16) #define SAMPLE_BITS (16)
@ -18,6 +19,7 @@
#define SLAVE_WS_IO 26 #define SLAVE_WS_IO 26
#define DATA_IN_IO 21 #define DATA_IN_IO 21
#define DATA_OUT_IO 22 #define DATA_OUT_IO 22
#define PERCENT_DIFF 0.0001
/** /**
* i2s initialize test * i2s initialize test
@ -267,3 +269,54 @@ TEST_CASE("I2S memory leaking test", "[i2s]")
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT(initial_size == esp_get_free_heap_size()); TEST_ASSERT(initial_size == esp_get_free_heap_size());
} }
/*
* The I2S APLL clock variation test used to test the difference between the different sample rates, different bits per sample
* and the APLL clock generate for it. The TEST_CASE passes PERCENT_DIFF variation from the provided sample rate in APLL generated clock
* The percentage difference calculated as (mod((obtained clock rate - desired clock rate)/(desired clock rate))) * 100.
*/
TEST_CASE("I2S APLL clock variation test", "[i2s]")
{
i2s_pin_config_t pin_config = {
.bck_io_num = MASTER_BCK_IO,
.ws_io_num = MASTER_WS_IO,
.data_out_num = DATA_OUT_IO,
.data_in_num = -1
};
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = SAMPLE_BITS,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.dma_buf_count = 6,
.dma_buf_len = 60,
.use_apll = true,
.intr_alloc_flags = 0,
};
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL));
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &pin_config));
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
int initial_size = esp_get_free_heap_size();
uint32_t sample_rate_arr[8] = { 10675, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
int bits_per_sample_arr[3] = { 16, 24, 32 };
for (int i = 0; i < (sizeof(sample_rate_arr)/sizeof(sample_rate_arr[0])); i++) {
for (int j = 0; j < (sizeof(bits_per_sample_arr)/sizeof(bits_per_sample_arr[0])); j++) {
i2s_config.sample_rate = sample_rate_arr[i];
i2s_config.bits_per_sample = bits_per_sample_arr[j];
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL));
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &pin_config));
TEST_ASSERT((fabs((i2s_get_clk(I2S_NUM_0) - sample_rate_arr[i]))/(sample_rate_arr[i]))*100 < PERCENT_DIFF);
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
}
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}