瀏覽代碼

Merge branch 'bugfix/i2s_apll_clock_fix' into 'master'

driver/i2s: fix apll_clock_rate for different sample rates

See merge request idf/esp-idf!5159
Ivan Grokhotkov 6 年之前
父節點
當前提交
13d9c483b3
共有 3 個文件被更改,包括 84 次插入2 次删除
  1. 21 2
      components/driver/i2s.c
  2. 10 0
      components/driver/include/driver/i2s.h
  3. 53 0
      components/driver/test/test_i2s.c

+ 21 - 2
components/driver/i2s.c

@@ -89,6 +89,7 @@ typedef struct {
     bool use_apll;               /*!< I2S use APLL clock */
     bool tx_desc_auto_clear;    /*!< I2S auto clear tx descriptor on underflow */
     int fixed_mclk;             /*!< I2S fixed MLCK clock */
+    double real_rate;
 #ifdef CONFIG_PM_ENABLE
     esp_pm_lock_handle_t pm_lock;
 #endif
@@ -178,6 +179,12 @@ esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num)
     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)
 {
     return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle);
@@ -253,7 +260,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);
         min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31);
         avg = (max_rate + min_rate)/2;
-        if(abs(avg - rate) < min_diff) {
+        if (abs(avg - rate) < min_diff) {
             min_diff = abs(avg - rate);
             *sdm2 = _sdm2;
         }
@@ -263,11 +270,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);
         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) {
+        if (abs(avg - rate) < min_diff) {
             min_diff = abs(avg - rate);
             *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;
     for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
@@ -455,6 +472,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]->clkm_conf.clka_en = 1;
         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",
             rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0);
     } else {
@@ -465,6 +483,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.rx_bck_div_num = bck;
         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",
             rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals);
     }

+ 10 - 0
components/driver/include/driver/i2s.h

@@ -501,6 +501,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);
 
+/**
+ * @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,
  *        and set ADC parameters.

+ 53 - 0
components/driver/test/test_i2s.c

@@ -9,6 +9,7 @@
 #include "freertos/task.h"
 #include "driver/i2s.h"
 #include "unity.h"
+#include "math.h"
 
 #define SAMPLE_RATE     (36000)
 #define SAMPLE_BITS     (16)
@@ -18,6 +19,7 @@
 #define SLAVE_WS_IO 26
 #define DATA_IN_IO 21
 #define DATA_OUT_IO 22
+#define PERCENT_DIFF 0.0001
 
 /**
  * i2s initialize test
@@ -267,3 +269,54 @@ TEST_CASE("I2S memory leaking test", "[i2s]")
     vTaskDelay(100 / portTICK_PERIOD_MS);
     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());
+}