Просмотр исходного кода

Merge branch 'feature/i2s_apll' into 'master'

driver/i2s: add support apll clock

See merge request !1115

Jiang Jiang Jian 8 лет назад
Родитель
Сommit
8e47c355fa

+ 173 - 13
components/driver/i2s.c

@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 #include <string.h>
+#include <math.h>
 #include <esp_types.h>
 
 #include "freertos/FreeRTOS.h"
@@ -22,6 +23,8 @@
 #include "soc/rtc_cntl_reg.h"
 #include "soc/rtc_io_reg.h"
 #include "soc/sens_reg.h"
+#include "soc/rtc.h"
+#include "soc/efuse_reg.h"
 #include "rom/lldesc.h"
 
 #include "driver/gpio.h"
@@ -45,7 +48,9 @@ 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_MAX_FREQ (500000000)
+#define APLL_I2S_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware
 /**
  * @brief DMA buffer object
  *
@@ -77,12 +82,28 @@ 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 */
 } i2s_obj_t;
 
 static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0};
 static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
 static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
 
+/**
+ * @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);
 static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num)
@@ -166,6 +187,125 @@ static esp_err_t i2s_isr_register(i2s_port_t i2s_num, uint8_t intr_alloc_flags,
     return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle);
 }
 
+
+static float i2s_get_apll_real_rate(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);
+    if (is_rev0) {
+        sdm0 = 0;
+        sdm1 = 0;
+    }
+    float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4);
+    if (fout < APLL_MIN_FREQ || fout > APLL_MAX_FREQ) {
+        return 9999999;
+    }
+    float fpll = fout / (2 * (odir+2)); //== fi2s (N=1, b=0, a=1)
+    return fpll/(8*4*bits_per_sample); //fbck = fi2s/bck_div
+}
+
+/**
+ * @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. 
+ *            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 350MHz<fout<500MHz, we limit the sdm2 from 4 to 9, 
+ *               Take average frequency close to the desired frequency, and select sdm2
+ *            2. Next, we look for sequences of less influential and more detailed parameters, 
+ *               also by taking the average of the largest and smallest frequencies closer to the desired frequency.
+ *            3. And finally, loop through all the most detailed of the parameters, finding the best desired frequency
+ *
+ * @param[in]  rate                  The sample rate
+ * @param[in]  bits_per_sample       The bits per sample
+ * @param[out]      sdm0             The sdm 0
+ * @param[out]      sdm1             The sdm 1
+ * @param[out]      sdm2             The sdm 2
+ * @param[out]      odir             The odir
+ *
+ * @return     ESP_FAIL or ESP_OK
+ */
+static esp_err_t i2s_apll_calculate(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir)
+{
+    int _odir, _sdm0, _sdm1, _sdm2, i;
+    float avg;
+    float min_rate, max_rate, min_diff;
+    if (rate < APLL_I2S_MIN_RATE) {
+        return ESP_FAIL;
+    }
+    //check pre-define apll parameters
+    i = 0;
+    while (apll_predefine[i][0]) {
+        if (apll_predefine[i][0] == bits_per_sample && apll_predefine[i][0] == rate) {
+            *sdm0 = apll_predefine[i][1];
+            *sdm1 = apll_predefine[i][2];
+            *sdm2 = apll_predefine[i][3];
+            *odir = apll_predefine[i][4];
+            return ESP_OK;
+        }
+        i++;
+    }
+    *sdm0 = 0;
+    *sdm1 = 0;
+    *sdm2 = 0;
+    *odir = 0;
+    min_diff = 99999;
+
+    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
+        max_rate = i2s_get_apll_real_rate(bits_per_sample, 255, 255, _sdm2, 0);
+        min_rate = i2s_get_apll_real_rate(bits_per_sample, 0, 0, _sdm2, 31);
+        avg = (max_rate + min_rate)/2;
+        if(abs(avg - rate) < min_diff) {
+            min_diff = abs(avg - rate);
+            *sdm2 = _sdm2;
+        }
+    }
+    min_diff = 99999;
+    for (_odir = 0; _odir < 32; _odir ++) {
+        max_rate = i2s_get_apll_real_rate(bits_per_sample, 255, 255, *sdm2, _odir);
+        min_rate = i2s_get_apll_real_rate(bits_per_sample, 0, 0, *sdm2, _odir);
+        avg = (max_rate + min_rate)/2;
+        if(abs(avg - rate) < min_diff) {
+            min_diff = abs(avg - rate);
+            *odir = _odir;
+        }
+    }
+
+    min_diff = 99999;
+    for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
+        max_rate = i2s_get_apll_real_rate(bits_per_sample, 255, _sdm1, *sdm2, *odir);
+        min_rate = i2s_get_apll_real_rate(bits_per_sample, 0, _sdm1, *sdm2, *odir);
+        avg = (max_rate + min_rate)/2;
+        if (abs(avg - rate) < min_diff) {
+            min_diff = abs(avg - rate);
+            *sdm1 = _sdm1;
+        }
+    }
+    
+    min_diff = 99999;
+    for (_sdm0 = 0; _sdm0 < 256; _sdm0 ++) {
+        avg = i2s_get_apll_real_rate(bits_per_sample, _sdm0, *sdm1, *sdm2, *odir);
+        if (abs(avg - rate) < min_diff) {
+            min_diff = abs(avg - rate);
+            *sdm0 = _sdm0;
+        }
+    }
+    
+    return ESP_OK;
+}
+
 esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch)
 {
     int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs)
@@ -310,19 +450,33 @@ 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);
     }
-
-    I2S[i2s_num]->clkm_conf.clka_en = 0;
-    I2S[i2s_num]->clkm_conf.clkm_div_a = 63;
-    I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals;
-    I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger;
-    I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck;
-    I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck;
+    int sdm0, sdm1, sdm2, odir;
+    if(p_i2s_obj[i2s_num]->use_apll && i2s_apll_calculate(rate, bits, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) {
+        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]->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);
+    } else {
+        I2S[i2s_num]->clkm_conf.clka_en = 0;
+        I2S[i2s_num]->clkm_conf.clkm_div_a = 63;
+        I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals;
+        I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger;
+        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);
+        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;
-    double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2);
-    ESP_LOGI(I2S_TAG, "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);
-
+    
     // 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);
@@ -535,7 +689,7 @@ esp_err_t i2s_stop(i2s_port_t i2s_num)
 esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode)
 {
     I2S_CHECK((dac_mode < I2S_DAC_CHANNEL_MAX), "i2s dac mode error", ESP_ERR_INVALID_ARG);
-    if(dac_mode == I2S_DAC_CHANNEL_DISABLE) {
+    if (dac_mode == I2S_DAC_CHANNEL_DISABLE) {
         dac_output_disable(DAC_CHANNEL_1);
         dac_output_disable(DAC_CHANNEL_2);
         dac_i2s_disable();
@@ -813,6 +967,8 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co
             I2S[i2s_num]->conf.rx_slave_mod = 1;    //RX Slave
         }
     }
+
+    p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll;
     return ESP_OK;
 }
 
@@ -914,6 +1070,10 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
         p_i2s_obj[i2s_num]->i2s_queue = NULL;
     }
 
+    if(p_i2s_obj[i2s_num]->use_apll) {
+        rtc_clk_apll_enable(0, 0, 0, 0, 0);
+    }
+
     free(p_i2s_obj[i2s_num]);
     p_i2s_obj[i2s_num] = NULL;
 

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

@@ -122,6 +122,8 @@ typedef enum {
     I2S_MODE_PDM = 64,
 } i2s_mode_t;
 
+
+
 /**
  * @brief I2S configuration parameters for i2s_param_config function
  *
@@ -135,6 +137,7 @@ 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 */
 } i2s_config_t;
 
 /**

+ 5 - 1
docs/api-reference/peripherals/i2s.rst

@@ -10,6 +10,8 @@ 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. 
+
 Application Example
 -------------------
 
@@ -34,7 +36,9 @@ Short example of I2S configuration:
          .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
          .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
          .dma_buf_count = 8,
-         .dma_buf_len = 64
+         .dma_buf_len = 64,
+         .use_apll = 0,
+         .apll_param = I2S_APLL_NONE
     };
 
     static const i2s_pin_config_t pin_config = {

+ 2 - 1
examples/peripherals/i2s/main/i2s_example_main.c

@@ -86,7 +86,8 @@ void app_main()
         .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
         .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
         .dma_buf_count = 6,
-        .dma_buf_len = 60,                                                      //
+        .dma_buf_len = 60,
+        .use_apll = 0,
         .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1                                //Interrupt level 1
     };
     i2s_pin_config_t pin_config = {