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:
commit
de7a50dca0
3 changed files with 84 additions and 2 deletions
|
@ -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);
|
||||||
|
@ -251,7 +258,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm
|
||||||
max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0);
|
max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0);
|
||||||
min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31);
|
min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31);
|
||||||
avg = (max_rate + min_rate)/2;
|
avg = (max_rate + min_rate)/2;
|
||||||
if(abs(avg - rate) < min_diff) {
|
if (abs(avg - rate) < min_diff) {
|
||||||
min_diff = abs(avg - rate);
|
min_diff = abs(avg - rate);
|
||||||
*sdm2 = _sdm2;
|
*sdm2 = _sdm2;
|
||||||
}
|
}
|
||||||
|
@ -261,11 +268,21 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm
|
||||||
max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, *sdm2, _odir);
|
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);
|
min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, *sdm2, _odir);
|
||||||
avg = (max_rate + min_rate)/2;
|
avg = (max_rate + min_rate)/2;
|
||||||
if(abs(avg - rate) < min_diff) {
|
if (abs(avg - rate) < min_diff) {
|
||||||
min_diff = abs(avg - rate);
|
min_diff = abs(avg - rate);
|
||||||
*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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue