Преглед изворни кода

i2s: public bclk_div and add a default config for PDM TX DAC

laokaiyao пре 2 година
родитељ
комит
bf8419fd6e

+ 4 - 2
components/driver/i2s/i2s_pdm.c

@@ -30,6 +30,7 @@ static const char *TAG = "i2s_pdm";
   ---------------------------------------------------------------*/
 
 #if SOC_I2S_SUPPORTS_PDM_TX
+#define I2S_PDM_TX_BCLK_DIV_MIN    8  /*!< The minimum bclk_div for PDM TX mode */
 static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
 {
     uint32_t rate = clk_cfg->sample_rate_hz;
@@ -38,7 +39,7 @@ static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_
     // Over sampling ratio (integer, mostly should be 1 or 2)
     uint32_t over_sample_ratio = pdm_tx_clk->up_sample_fp / pdm_tx_clk->up_sample_fs;
     clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * over_sample_ratio;
-    clk_info->bclk_div = 8;
+    clk_info->bclk_div = clk_cfg->bclk_div < I2S_PDM_TX_BCLK_DIV_MIN ? I2S_PDM_TX_BCLK_DIV_MIN : clk_cfg->bclk_div;
     clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
     clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
     clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
@@ -318,13 +319,14 @@ err:
   ---------------------------------------------------------------*/
 
 #if SOC_I2S_SUPPORTS_PDM_RX
+#define I2S_PDM_RX_BCLK_DIV_MIN    8  /*!< The minimum bclk_div for PDM RX mode */
 static esp_err_t i2s_pdm_rx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
 {
     uint32_t rate = clk_cfg->sample_rate_hz;
     i2s_pdm_rx_clk_config_t *pdm_rx_clk = (i2s_pdm_rx_clk_config_t *)clk_cfg;
 
     clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * (pdm_rx_clk->dn_sample_mode == I2S_PDM_DSR_16S ? 2 : 1);
-    clk_info->bclk_div = 8;
+    clk_info->bclk_div = clk_cfg->bclk_div < I2S_PDM_RX_BCLK_DIV_MIN ? I2S_PDM_RX_BCLK_DIV_MIN : clk_cfg->bclk_div;
     clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
     clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
     clk_info->mclk_div = clk_info->sclk / clk_info->mclk;

+ 54 - 4
components/driver/i2s/include/driver/i2s_pdm.h

@@ -44,7 +44,8 @@ extern "C" {
     .sample_rate_hz = rate, \
     .clk_src = I2S_CLK_SRC_DEFAULT, \
     .mclk_multiple = I2S_MCLK_MULTIPLE_256, \
-    .dn_sample_mode = I2S_PDM_DSR_8S \
+    .dn_sample_mode = I2S_PDM_DSR_8S, \
+    .bclk_div = 8, \
 }
 
 /**
@@ -69,6 +70,8 @@ typedef struct {
     i2s_mclk_multiple_t     mclk_multiple;      /*!< The multiple of mclk to the sample rate */
     /* Particular fields */
     i2s_pdm_dsr_t           dn_sample_mode;     /*!< Down-sampling rate mode */
+    uint32_t                bclk_div;           /*!< The division from mclk to bclk. The typical and minimum value is I2S_PDM_RX_BCLK_DIV_MIN.
+                                                 *   It will be set to I2S_PDM_RX_BCLK_DIV_MIN by default if it is smaller than I2S_PDM_RX_BCLK_DIV_MIN */
 } i2s_pdm_rx_clk_config_t;
 
 /**
@@ -165,7 +168,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
 #if SOC_I2S_SUPPORTS_PDM_TX
 #if SOC_I2S_HW_VERSION_2
 /**
- * @brief PDM style in 2 slots(TX)
+ * @brief PDM style in 2 slots(TX) for codec line mode
  * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
  * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
  */
@@ -184,9 +187,33 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
     .sd_dither = 0, \
     .sd_dither2 = 1, \
 }
+
+/**
+ * @brief PDM style in 1 slots(TX) for DAC line mode
+ * @note The noise might be different with different configurations, this macro provides a set of configurations
+ *       that have relatively high SNR (Signal Noise Ratio), you can also adjust them to fit your case.
+ * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
+ * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
+ */
+#define I2S_PDM_TX_SLOT_DAC_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
+    .data_bit_width = bits_per_sample, \
+    .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
+    .slot_mode = mono_or_stereo, \
+    .sd_prescale = 0, \
+    .sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
+    .hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
+    .lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
+    .sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \
+    .line_mode = ((mono_or_stereo) == I2S_SLOT_MODE_MONO ? \
+                 I2S_PDM_TX_ONE_LINE_DAC : I2S_PDM_TX_TWO_LINE_DAC), \
+    .hp_en = true, \
+    .hp_cut_off_freq_hz = 35.5, \
+    .sd_dither = 0, \
+    .sd_dither2 = 1, \
+}
 #else // SOC_I2S_HW_VERSION_2
 /**
- * @brief PDM style in 2 slots(TX)
+ * @brief PDM style in 2 slots(TX) for codec line mode
  * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
  * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
  */
@@ -204,7 +231,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
 #endif // SOC_I2S_HW_VERSION_2
 
 /**
- * @brief i2s default pdm tx clock configuration
+ * @brief i2s default pdm tx clock configuration for codec line mode
  * @note TX PDM can only be set to the following two up-sampling rate configurations:
  *       1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000
  *       2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz
@@ -218,6 +245,27 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
     .mclk_multiple = I2S_MCLK_MULTIPLE_256, \
     .up_sample_fp = 960, \
     .up_sample_fs = 480, \
+    .bclk_div = 8, \
+}
+
+/**
+ * @brief i2s default pdm tx clock configuration for DAC line mode
+ * @note TX PDM can only be set to the following two up-sampling rate configurations:
+ *       1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000
+ *       2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz
+ *       If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000.
+ *       Otherwise, the second configuration should be adopted.
+ * @note The noise might be different with different configurations, this macro provides a set of configurations
+ *       that have relatively high SNR (Signal Noise Ratio), you can also adjust them to fit your case.
+ * @param rate sample rate (not suggest to exceed 48000 Hz, otherwise more glitches and noise may appear)
+ */
+#define I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG(rate) { \
+    .sample_rate_hz = rate, \
+    .clk_src = I2S_CLK_SRC_DEFAULT, \
+    .mclk_multiple = I2S_MCLK_MULTIPLE_256, \
+    .up_sample_fp = 960, \
+    .up_sample_fs = (rate) / 100, \
+    .bclk_div = 13, \
 }
 
 /*
@@ -275,6 +323,8 @@ typedef struct {
     /* Particular fields */
     uint32_t                up_sample_fp;       /*!< Up-sampling param fp */
     uint32_t                up_sample_fs;       /*!< Up-sampling param fs, not allowed to be greater than 480 */
+    uint32_t                bclk_div;           /*!< The division from mclk to bclk. The minimum value is I2S_PDM_TX_BCLK_DIV_MIN.
+                                                 *   It will be set to I2S_PDM_TX_BCLK_DIV_MIN by default if it is smaller than I2S_PDM_TX_BCLK_DIV_MIN */
 } i2s_pdm_tx_clk_config_t;
 
 /**

+ 0 - 9
examples/peripherals/.build-test-rules.yml

@@ -45,9 +45,6 @@ examples/peripherals/i2s/i2s_adc_dac:
 examples/peripherals/i2s/i2s_basic/i2s_pdm:
   disable:
     - if: SOC_I2S_SUPPORTS_PDM != 1
-    - if: IDF_TARGET == "esp32h2"
-      temporary: true
-      reason: rtc timer is not supported
 
 examples/peripherals/i2s/i2s_basic/i2s_std:
   disable:
@@ -290,9 +287,3 @@ examples/peripherals/uart/uart_echo_rs485:
 examples/peripherals/usb:
   disable:
     - if: SOC_USB_PERIPH_NUM != 1
-
-examples/peripherals/wave_gen:
-  enable:
-    - if: IDF_TARGET == "esp32"
-      temporary: true
-      reason: the other targets are not tested yet

+ 5 - 5
examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- |
 
 # I2S Basic PDM Mode Example
 
@@ -99,9 +99,9 @@ This example is going to show how to use the PDM TX and RX mode.
 #### PDM TX
 
 * An earphone or a speaker
-* An audio power amplifier that can input PDM signal. If the power amplifier can only receive the analog signal without PDM clock, a low-pass passive or active filter is required to restore the PDM data wave into analog signal, before it is transmitted to the power amplifier.
+* An audio power amplifier that can input PDM signal. If the power amplifier can only receive the analog signal without PDM clock (i.e. DAC line mode, otherwise codec line mode), a low-pass passive or active filter is required to restore the PDM data wave into analog signal, before it is transmitted to the power amplifier.
 
-**MAX98358**
+**MAX98358 (codec case)**
 
 Please refer to the [Datasheet of MAX98358](https://datasheets.maximintegrated.com/en/ds/MAX98358.pdf) for more details.
 
@@ -121,7 +121,7 @@ Please refer to the [Datasheet of MAX98358](https://datasheets.maximintegrated.c
 └────────────────────────┘               └───────────────┘
 ```
 
-**NS4150**
+**NS4150 (dac case)**
 
 Please refer to the NS4150 datasheet for more details.
 

+ 2 - 2
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/CMakeLists.txt

@@ -1,10 +1,10 @@
 set(srcs "i2s_pdm_example_main.c")
 
-if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX)
+if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX AND CONFIG_EXAMPLE_PDM_TX)
     list(APPEND srcs "i2s_pdm_tx.c")
 endif()
 
-if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX)
+if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX AND CONFIG_EXAMPLE_PDM_RX)
     list(APPEND srcs "i2s_pdm_rx.c")
 endif()
 

+ 24 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/Kconfig.projbuild

@@ -19,4 +19,28 @@ menu "I2S PDM Example Configuration"
                 PDM RX example will show the received data from a PDM microphone.
     endchoice
 
+    choice EXAMPLE_PDM_TX_LINE_MODE
+        prompt "I2S PDM TX Line Mode"
+        depends on EXAMPLE_PDM_TX && SOC_I2S_HW_VERSION_2
+        default EXAMPLE_PDM_TX_CODEC
+        help
+            Decide to output PDM signal into a PDM codec or a low-pass filter
+
+        config EXAMPLE_PDM_TX_CODEC
+            bool "Codec line mode"
+            help
+                Output PDM signal to a PDM codec. The PDM clock signal is mandatory for PDM codec,
+                the codec can differentiate the left and right sound channels by sampling data
+                on positive or negative edges. That means the data of the left and right channels
+                can coexist on a same data line.
+
+        config EXAMPLE_PDM_TX_DAC
+            bool "DAC line mode"
+            help
+                Output PDM signal to a low-pass filter, so that the low-pass filter can restore the PDM
+                signal to analog wave. Therefore, each data line can only contains one sound channel,
+                if both left and right channels are required, two data lines should be specified as well.
+                Normally the PDM signal is not sufficient in DAC line mode.
+    endchoice
+
 endmenu

+ 6 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c

@@ -54,9 +54,15 @@ static i2s_chan_handle_t i2s_example_init_pdm_tx(void)
      * These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM TX mode.
      * They can help to specify the slot and clock configurations for initialization or re-configuring */
     i2s_pdm_tx_config_t pdm_tx_cfg = {
+#if CONFIG_EXAMPLE_PDM_TX_DAC
+        .clk_cfg = I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
+        /* The data bit-width of PDM mode is fixed to 16 */
+        .slot_cfg = I2S_PDM_TX_SLOT_DAC_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+#else
         .clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
         /* The data bit-width of PDM mode is fixed to 16 */
         .slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+#endif
         .gpio_cfg = {
             .clk = EXAMPLE_PDM_TX_CLK_IO,
             .dout = EXAMPLE_PDM_TX_DOUT_IO,

+ 1 - 1
examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py

@@ -9,7 +9,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32s3
 @pytest.mark.esp32c3
 @pytest.mark.esp32c6
-# @pytest.mark.esp32h2  IDF-6808
+@pytest.mark.esp32h2
 @pytest.mark.generic
 @pytest.mark.parametrize(
     'config',