Ver Fonte

i2s: add basic examples for STD/TDM/PDM mode

laokaiyao há 3 anos atrás
pai
commit
90866e99fb
64 ficheiros alterados com 1616 adições e 409 exclusões
  1. 1 1
      components/driver/deprecated/driver/i2s.h
  2. 2 2
      components/driver/deprecated/i2s_legacy.c
  3. 2 2
      components/driver/i2s/i2s_common.c
  4. 10 1
      components/driver/i2s/i2s_pdm.c
  5. 2 0
      components/driver/i2s/i2s_std.c
  6. 10 0
      components/driver/i2s/i2s_tdm.c
  7. 2 2
      components/driver/include/driver/i2s_common.h
  8. 20 8
      components/driver/include/driver/i2s_pdm.h
  9. 1 0
      components/driver/include/driver/i2s_types.h
  10. 126 14
      components/hal/esp32c3/include/hal/i2s_ll.h
  11. 125 14
      components/hal/esp32h2/include/hal/i2s_ll.h
  12. 126 14
      components/hal/esp32s3/include/hal/i2s_ll.h
  13. 25 14
      components/hal/i2s_hal.c
  14. 7 1
      components/hal/include/hal/i2s_hal.h
  15. 24 0
      components/hal/include/hal/i2s_types.h
  16. 2 0
      components/soc/esp32/i2s_periph.c
  17. 1 0
      components/soc/esp32c3/i2s_periph.c
  18. 6 13
      components/soc/esp32c3/include/soc/gpio_sig_map.h
  19. 15 11
      components/soc/esp32h2/i2s_periph.c
  20. 1 0
      components/soc/esp32s2/i2s_periph.c
  21. 2 0
      components/soc/esp32s3/i2s_periph.c
  22. 1 0
      components/soc/include/soc/i2s_periph.h
  23. 5 5
      docs/en/api-reference/peripherals/i2s.rst
  24. 1 3
      docs/en/migration-guides/release-5.x/peripherals.rst
  25. 0 98
      examples/peripherals/i2s/i2s_basic/README.md
  26. 1 1
      examples/peripherals/i2s/i2s_basic/i2s_pdm/CMakeLists.txt
  27. 165 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md
  28. 12 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/main/CMakeLists.txt
  29. 23 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example.h
  30. 31 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example_main.c
  31. 78 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c
  32. 109 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c
  33. 22 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py
  34. 1 0
      examples/peripherals/i2s/i2s_basic/i2s_pdm/sdkconfig.defaults
  35. 6 0
      examples/peripherals/i2s/i2s_basic/i2s_std/CMakeLists.txt
  36. 73 0
      examples/peripherals/i2s/i2s_basic/i2s_std/README.md
  37. 2 0
      examples/peripherals/i2s/i2s_basic/i2s_std/main/CMakeLists.txt
  38. 210 0
      examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c
  39. 35 0
      examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py
  40. 1 0
      examples/peripherals/i2s/i2s_basic/i2s_std/sdkconfig.defaults
  41. 6 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/CMakeLists.txt
  42. 72 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md
  43. 2 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/main/CMakeLists.txt
  44. 207 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c
  45. 33 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py
  46. 1 0
      examples/peripherals/i2s/i2s_basic/i2s_tdm/sdkconfig.defaults
  47. 0 2
      examples/peripherals/i2s/i2s_basic/main/CMakeLists.txt
  48. 0 180
      examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c
  49. 0 18
      examples/peripherals/i2s/i2s_basic/pytest_i2s_basic.py
  50. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/CMakeLists.txt
  51. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/README.md
  52. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/main/CMakeLists.txt
  53. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/main/Kconfig.projbuild
  54. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/main/canon.pcm
  55. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c
  56. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/main/idf_component.yml
  57. 0 0
      examples/peripherals/i2s/i2s_codec/i2s_es8311/pytest_i2s_es8311.py
  58. 0 0
      examples/peripherals/i2s/i2s_recorder/CMakeLists.txt
  59. 0 0
      examples/peripherals/i2s/i2s_recorder/README.md
  60. 0 0
      examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt
  61. 0 0
      examples/peripherals/i2s/i2s_recorder/main/Kconfig.projbuild
  62. 9 4
      examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c
  63. 0 0
      examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py
  64. 0 1
      tools/ci/check_copyright_ignore.txt

+ 1 - 1
components/driver/deprecated/driver/i2s.h

@@ -25,7 +25,7 @@
 
 #if !CONFIG_I2S_SUPPRESS_DEPRECATE_WARN
 #warning "This set of I2S APIs has been deprecated, \
-please include 'driver/i2s_std.h', 'driver/i2s_pdm' or 'driver/i2s_tdm' instead. \
+please include 'driver/i2s_std.h', 'driver/i2s_pdm.h' or 'driver/i2s_tdm.h' instead. \
 if you want to keep using the old APIs and ignore this warning, \
 you can enable 'Suppress leagcy driver deprecated warning' option under 'I2S Configuration' menu in Kconfig"
 #endif

+ 2 - 2
components/driver/deprecated/i2s_legacy.c

@@ -1315,11 +1315,11 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
         SLOT_CFG(pdm_tx).lp_scale = I2S_PDM_SIG_SCALING_MUL_1;
         SLOT_CFG(pdm_tx).sinc_scale = I2S_PDM_SIG_SCALING_MUL_1;
 #if SOC_I2S_HW_VERSION_2
-        SLOT_CFG(pdm_tx).sd_en = true;
+        SLOT_CFG(pdm_tx).line_mode = I2S_PDM_TX_ONE_LINE_CODEC;
         SLOT_CFG(pdm_tx).hp_en = true;
         SLOT_CFG(pdm_tx).hp_cut_off_freq_hz = 49;
         SLOT_CFG(pdm_tx).sd_dither = 0;
-        SLOT_CFG(pdm_tx).sd_dither2 = 0;
+        SLOT_CFG(pdm_tx).sd_dither2 = 1;
 #endif // SOC_I2S_HW_VERSION_2
 
         /* Generate PDM TX clock configuration */

+ 2 - 2
components/driver/i2s/i2s_common.c

@@ -432,7 +432,7 @@ esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bu
     if (handle->dir == I2S_DIR_RX) {
         i2s_ll_rx_set_eof_num(handle->controller->hal.dev, bufsize);
     }
-    ESP_LOGD(TAG, "DMA malloc info: dma_desc_num = %d, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = %d, ", num, bufsize);
+    ESP_LOGD(TAG, "DMA malloc info: dma_desc_num = %d, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = %d", num, bufsize);
     return ESP_OK;
 err:
     i2s_free_dma_desc(handle);
@@ -1112,7 +1112,7 @@ esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name)
     }
     portEXIT_CRITICAL(&g_i2s.spinlock);
     if (occupied_comp != NULL) {
-        ESP_LOGE(TAG, "i2s controller %d has been occupied by %s", id, occupied_comp);
+        ESP_LOGW(TAG, "i2s controller %d has been occupied by %s", id, occupied_comp);
     }
     return ret;
 }

+ 10 - 1
components/driver/i2s/i2s_pdm.c

@@ -67,6 +67,8 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx
     portENTER_CRITICAL(&g_i2s.spinlock);
     /* Set clock configurations in HAL*/
     i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
+    /* Work aroud for PDM TX clock, set the raw division directly to reduce the noise */
+    i2s_ll_tx_set_raw_clk_div(handle->controller->hal.dev, 1, 1, 0, 0);
     portEXIT_CRITICAL(&g_i2s.spinlock);
 
     /* Update the mode info: clock configuration */
@@ -114,8 +116,14 @@ static esp_err_t i2s_pdm_tx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_
                         ESP_ERR_INVALID_ARG, TAG, "clk gpio is invalid");
     ESP_RETURN_ON_FALSE((gpio_cfg->dout == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->dout)),
                         ESP_ERR_INVALID_ARG, TAG, "dout gpio is invalid");
+    i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
     /* Set data output GPIO */
     i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
+#if SOC_I2S_HW_VERSION_2
+    if (pdm_tx_cfg->slot_cfg.line_mode == I2S_PDM_TX_TWO_LINE_DAC) {
+        i2s_gpio_check_and_set(gpio_cfg->dout2, i2s_periph_signal[id].data_out1_sig, false, false);
+    }
+#endif
 
     if (handle->role == I2S_ROLE_SLAVE) {
         /* For "tx + slave" mode, select TX signal index for ws and bck */
@@ -132,7 +140,6 @@ static esp_err_t i2s_pdm_tx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_
     i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
 #endif
     /* Update the mode info: gpio configuration */
-    i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
     memcpy(&(pdm_tx_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_pdm_tx_gpio_config_t));
 
     return ESP_OK;
@@ -189,6 +196,7 @@ esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_t
     /* Initialization finished, mark state as ready */
     handle->state = I2S_CHAN_STATE_READY;
     xSemaphoreGive(handle->mutex);
+    ESP_LOGD(TAG, "The tx channel on I2S0 has been initialized to PDM TX mode successfully");
     return ret;
 
 err:
@@ -460,6 +468,7 @@ esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_r
     /* Initialization finished, mark state as ready */
     handle->state = I2S_CHAN_STATE_READY;
     xSemaphoreGive(handle->mutex);
+    ESP_LOGD(TAG, "The rx channel on I2S0 has been initialized to PDM RX mode successfully");
     return ret;
 
 err:

+ 2 - 0
components/driver/i2s/i2s_std.c

@@ -247,6 +247,8 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
     /* Initialization finished, mark state as ready */
     handle->state = I2S_CHAN_STATE_READY;
     xSemaphoreGive(handle->mutex);
+    ESP_LOGD(TAG, "The %s channel on I2S%d has been initialized to STD mode successfully",
+             handle->dir == I2S_DIR_TX ? "tx" : "rx", handle->controller->id);
     return ret;
 
 err:

+ 10 - 0
components/driver/i2s/i2s_tdm.c

@@ -40,6 +40,14 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
         clk_info->bclk = rate * handle->total_slot * slot_bits;
         clk_info->mclk = rate * clk_cfg->mclk_multiple;
         clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
+        /* While RECEIVING multiple slots, the data will go wrong if the bclk_div is euqal or smaller than 2 */
+    check:
+        if (clk_info->bclk_div <= 2) {
+            clk_info->mclk *= 2;
+            clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
+            ESP_LOGW(TAG, "the current mclk multiple is too small, adjust the mclk multiple to %d", clk_info->mclk / rate);
+            goto check;
+        }
     } else {
         /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
         clk_info->bclk_div = 8;
@@ -241,6 +249,8 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
     /* Initialization finished, mark state as ready */
     handle->state = I2S_CHAN_STATE_READY;
     xSemaphoreGive(handle->mutex);
+    ESP_LOGD(TAG, "The %s channel on I2S%d has been initialized to TDM mode successfully",
+             handle->dir == I2S_DIR_TX ? "tx" : "rx", handle->controller->id);
     return ret;
 
 err:

+ 2 - 2
components/driver/include/driver/i2s_common.h

@@ -22,8 +22,8 @@ extern "C" {
 #define I2S_CHANNEL_DEFAULT_CONFIG(i2s_num, i2s_role) { \
     .id = i2s_num, \
     .role = i2s_role, \
-    .dma_desc_num = 3, \
-    .dma_frame_num = 500, \
+    .dma_desc_num = 6, \
+    .dma_frame_num = 250, \
     .auto_clear = false, \
 }
 

+ 20 - 8
components/driver/include/driver/i2s_pdm.h

@@ -32,6 +32,8 @@ extern "C" {
     .data_bit_width = bits_per_sample, \
     .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
     .slot_mode = mono_or_stereo, \
+    .slot_mask = (mono_or_stereo  == I2S_SLOT_MODE_MONO) ? \
+                 I2S_PDM_SLOT_LEFT : I2S_PDM_SLOT_BOTH, \
 }
 
 /**
@@ -53,6 +55,8 @@ typedef struct {
     i2s_data_bit_width_t    data_bit_width;     /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */
     i2s_slot_bit_width_t    slot_bit_width;     /*!< I2S slot bit width (total bits per slot) , only support 16 bits for PDM mode */
     i2s_slot_mode_t         slot_mode;          /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
+    /* Particular fields */
+    i2s_pdm_slot_mask_t     slot_mask;          /*!< Choose the slots to activate */
 } i2s_pdm_rx_slot_config_t;
 
 /**
@@ -168,16 +172,16 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
     .slot_mode = mono_or_stereo, \
     .sd_prescale = 0, \
     .sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
-    .hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
+    .hp_scale = I2S_PDM_SIG_SCALING_DIV_2, \
     .lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
     .sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \
-    .sd_en = true, \
+    .line_mode = I2S_PDM_TX_ONE_LINE_CODEC, \
     .hp_en = true, \
-    .hp_cut_off_freq_hz = 49, \
+    .hp_cut_off_freq_hz = 35.5, \
     .sd_dither = 0, \
-    .sd_dither2 = 0, \
+    .sd_dither2 = 1, \
 }
-#else
+#else // SOC_I2S_HW_VERSION_2
 /**
  * @brief PDM style in 2 slots(TX)
  * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
@@ -209,7 +213,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
     .clk_src = I2S_CLK_SRC_DEFAULT, \
     .mclk_multiple = I2S_MCLK_MULTIPLE_256, \
     .up_sample_fp = 960, \
-    .up_sample_fs = ((rate) / 100), \
+    .up_sample_fs = 480, \
 }
 
 /*
@@ -234,7 +238,10 @@ typedef struct {
     /* General fields */
     i2s_data_bit_width_t    data_bit_width;     /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */
     i2s_slot_bit_width_t    slot_bit_width;     /*!< I2S slot bit width (total bits per slot), only support 16 bits for PDM mode */
-    i2s_slot_mode_t         slot_mode;          /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
+    i2s_slot_mode_t         slot_mode;          /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
+                                                 *   For PDM TX mode, mono means the data buffer only contains one slot data,
+                                                 *   Stereo means the data buffer contains two slots data
+                                                 */
     /* Particular fields */
     uint32_t                sd_prescale;        /*!< Sigma-delta filter prescale */
     i2s_pdm_sig_scale_t     sd_scale;           /*!< Sigma-delta filter scaling value */
@@ -242,7 +249,7 @@ typedef struct {
     i2s_pdm_sig_scale_t     lp_scale;           /*!< Low pass filter scaling value */
     i2s_pdm_sig_scale_t     sinc_scale;         /*!< Sinc filter scaling value */
 #if SOC_I2S_HW_VERSION_2
-    bool                    sd_en;              /*!< Sigma-delta filter enable */
+    i2s_pdm_tx_line_mode_t  line_mode;          /*!< PDM TX line mode, on-line codec, one-line dac, two-line dac mode can be selected */
     bool                    hp_en;              /*!< High pass filter enable */
     float                   hp_cut_off_freq_hz; /*!< High pass filter cut-off frequency, range 23.3Hz ~ 185Hz, see cut-off frequency sheet above */
     uint32_t                sd_dither;          /*!< Sigma-delta filter dither */
@@ -269,6 +276,11 @@ typedef struct {
 typedef struct {
     gpio_num_t clk;                /*!< PDM clk pin, output */
     gpio_num_t dout;               /*!< DATA pin, output */
+#if SOC_I2S_HW_VERSION_2
+    gpio_num_t dout2;              /*!< The second data pin for the DAC dual-line mode,
+                                    *   only take effect when the line mode is `I2S_PDM_TX_TWO_LINE_DAC`
+                                    */
+#endif
     struct {
         uint32_t   clk_inv: 1;     /*!< Set 1 to invert the clk output */
     } invert_flags;                /*!< GPIO pin invert flags */

+ 1 - 0
components/driver/include/driver/i2s_types.h

@@ -47,6 +47,7 @@ typedef enum {
     I2S_MCLK_MULTIPLE_128       = 128,     /*!< mclk = sample_rate * 128 */
     I2S_MCLK_MULTIPLE_256       = 256,     /*!< mclk = sample_rate * 256 */
     I2S_MCLK_MULTIPLE_384       = 384,     /*!< mclk = sample_rate * 384 */
+    I2S_MCLK_MULTIPLE_512       = 512,     /*!< mclk = sample_rate * 512 */
 } i2s_mclk_multiple_t;
 
 /**

+ 126 - 14
components/hal/esp32c3/include/hal/i2s_ll.h

@@ -218,6 +218,40 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
     hw->tx_conf1.tx_bck_div_num = val - 1;
 }
 
+/**
+ * @brief Set I2S tx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->tx_clkm_div_conf.tx_clkm_div_x = x;
+    hw->tx_clkm_div_conf.tx_clkm_div_y = y;
+    hw->tx_clkm_div_conf.tx_clkm_div_z = z;
+    hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1;
+}
+
+/**
+ * @brief Set I2S rx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->rx_clkm_div_conf.rx_clkm_div_x = x;
+    hw->rx_clkm_div_conf.rx_clkm_div_y = y;
+    hw->rx_clkm_div_conf.rx_clkm_div_z = z;
+    hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
+}
+
 /**
  * @brief Configure I2S TX module clock divider
  *
@@ -272,7 +306,7 @@ finish:
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
         } else {
             hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1;
+            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_z = numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0;
         }
@@ -346,7 +380,7 @@ finish:
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
         } else {
             hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1;
+            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_z = numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0;
         }
@@ -607,6 +641,29 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
     }
 }
 
+/**
+ * @brief PDM slot mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param mod Channel mode
+ *            while tx_ws_idle_pol = 0:
+ *            0: stereo
+ *            1: Both slots transmit left
+ *            2: Both slots transmit right
+ *            3: Left transmits `conf_single_data` right transmits data
+ *            4: Right transmits `conf_single_data` left transmits data
+ *            while tx_ws_idle_pol = 1:
+              0: stereo
+ *            1: Both slots transmit right
+ *            2: Both slots transmit left
+ *            3: Right transmits `conf_single_data` left transmits data
+ *            4: Left transmits `conf_single_data` right transmits data
+ */
+static inline void i2s_ll_tx_set_pdm_chan_mod(i2s_dev_t *hw, uint32_t mod)
+{
+    hw->tx_conf.tx_chan_mod = mod;
+}
+
 /**
  * @brief Set TX WS signal pol level
  *
@@ -772,18 +829,6 @@ static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable)
     hw->tx_pcm2pdm_conf.tx_pdm_hp_bypass = !enable;
 }
 
-/**
- * @brief Enable I2S TX PDM sigma-delta codec
- *
- * @param hw Peripheral I2S hardware instance address.
- * @param dither I2S TX PDM sigmadelta dither value
- */
-static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable)
-{
-    hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = enable;
-    hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = enable;
-}
-
 /**
  * @brief Set I2S TX PDM sigma-delta codec dither
  *
@@ -1010,6 +1055,73 @@ static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool ena)
     hw->tx_conf.sig_loopback = ena;
 }
 
+/**
+ * @brief PDM TX DMA data take mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_fst_valid  Whether take the DMA data at the first half period
+ *                      Only take effet when 'is_mono' is true
+ */
+static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool is_fst_valid)
+{
+    hw->tx_conf.tx_mono = is_mono;
+    hw->tx_conf.tx_mono_fst_vld = is_fst_valid;
+}
+
+/**
+ * @brief PDM TX slot mode
+ * @note     Mode     Left Slot       Right Slot      Chan Mode       WS Pol
+ *          -----------------------------------------------------------------
+ *           Stereo   Left            Right           0               x
+ *          -----------------------------------------------------------------
+ *           Mono     Left            Left            1               0
+ *           Mono     Right           Right           2               0
+ *           Mono     Single          Right           3               0
+ *           Mono     Left            Single          4               0
+ *          -----------------------------------------------------------------
+ *           Mono     Right           Right           1               1
+ *           Mono     Left            Left            2               1
+ *           Mono     Left            Single          3               1
+ *           Mono     Single          Right           4               1
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_copy   Whether the un-selected slot copies the data from the selected one
+ *                  If not, the un-selected slot will transmit the data from 'conf_single_data'
+ * @param mask      The slot mask to selet the slot
+ */
+static inline void i2s_ll_tx_pdm_slot_mode(i2s_dev_t *hw, bool is_mono, bool is_copy, i2s_pdm_slot_mask_t mask)
+{
+    if (is_mono) {
+        /* The default tx_ws_idle_pol is false */
+        if (is_copy) {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 1 : 2;
+        } else {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 4 : 3;
+        }
+    } else {
+        hw->tx_conf.tx_chan_mod = 0;
+    }
+}
+
+/**
+ * @brief PDM TX line mode
+ * @note    Mode         DAC Mode        2 lines output
+ *          -------------------------------------------
+ *          PDM codec    0               1
+ *          DAC 1-line   1               0
+ *          DAC 2-line   1               1
+ *
+ * @param hw    Peripheral I2S hardware instance address.
+ * @param line_mode    PDM TX line mode
+ */
+static inline void i2s_ll_tx_pdm_line_mode(i2s_dev_t *hw, i2s_pdm_tx_line_mode_t line_mode)
+{
+    hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = line_mode > I2S_PDM_TX_ONE_LINE_CODEC;
+    hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = line_mode != I2S_PDM_TX_ONE_LINE_DAC;
+}
+
 #ifdef __cplusplus
 }
 #endif

+ 125 - 14
components/hal/esp32h2/include/hal/i2s_ll.h

@@ -220,6 +220,40 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
     hw->tx_conf1.tx_bck_div_num = val - 1;
 }
 
+/**
+ * @brief Set I2S tx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->tx_clkm_div_conf.tx_clkm_div_x = x;
+    hw->tx_clkm_div_conf.tx_clkm_div_y = y;
+    hw->tx_clkm_div_conf.tx_clkm_div_z = z;
+    hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1;
+}
+
+/**
+ * @brief Set I2S rx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->rx_clkm_div_conf.rx_clkm_div_x = x;
+    hw->rx_clkm_div_conf.rx_clkm_div_y = y;
+    hw->rx_clkm_div_conf.rx_clkm_div_z = z;
+    hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
+}
+
 /**
  * @brief Configure I2S TX module clock divider
  *
@@ -274,7 +308,7 @@ finish:
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
         } else {
             hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1;
+            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_z = numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0;
         }
@@ -348,7 +382,7 @@ finish:
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
         } else {
             hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1;
+            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_z = numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0;
         }
@@ -609,6 +643,28 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
     }
 }
 
+/**
+ * @brief PDM slot mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param mod Channel mode
+ *            while tx_ws_idle_pol = 0:
+ *            0: stereo
+ *            1: Both slots transmit left
+ *            2: Both slots transmit right
+ *            3: Left transmits `conf_single_data` right transmits data
+ *            4: Right transmits `conf_single_data` left transmits data
+ *            while tx_ws_idle_pol = 1:
+              0: stereo
+ *            1: Both slots transmit right
+ *            2: Both slots transmit left
+ *            3: Right transmits `conf_single_data` left transmits data
+ *            4: Left transmits `conf_single_data` right transmits data
+ */
+static inline void i2s_ll_tx_set_pdm_chan_mod(i2s_dev_t *hw, uint32_t mod)
+{
+    hw->tx_conf.tx_chan_mod = mod;
+}
 
 /**
  * @brief Set TX WS signal pol level
@@ -775,18 +831,6 @@ static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable)
     hw->tx_pcm2pdm_conf.tx_pdm_hp_bypass = !enable;
 }
 
-/**
- * @brief Enable I2S TX PDM sigma-delta codec
- *
- * @param hw Peripheral I2S hardware instance address.
- * @param dither I2S TX PDM sigmadelta dither value
- */
-static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable)
-{
-    hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = enable;
-    hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = enable;
-}
-
 /**
  * @brief Set I2S TX PDM sigma-delta codec dither
  *
@@ -1024,6 +1068,73 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
     hw->rx_conf.rx_mono_fst_vld = mono_ena;
 }
 
+/**
+ * @brief PDM TX DMA data take mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_fst_valid  Whether take the DMA data at the first half period
+ *                      Only take effet when 'is_mono' is true
+ */
+static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool is_fst_valid)
+{
+    hw->tx_conf.tx_mono = is_mono;
+    hw->tx_conf.tx_mono_fst_vld = is_fst_valid;
+}
+
+/**
+ * @brief PDM TX slot mode
+ * @note     Mode     Left Slot       Right Slot      Chan Mode       WS Pol
+ *          -----------------------------------------------------------------
+ *           Stereo   Left            Right           0               x
+ *          -----------------------------------------------------------------
+ *           Mono     Left            Left            1               0
+ *           Mono     Right           Right           2               0
+ *           Mono     Single          Right           3               0
+ *           Mono     Left            Single          4               0
+ *          -----------------------------------------------------------------
+ *           Mono     Right           Right           1               1
+ *           Mono     Left            Left            2               1
+ *           Mono     Left            Single          3               1
+ *           Mono     Single          Right           4               1
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_copy   Whether the un-selected slot copies the data from the selected one
+ *                  If not, the un-selected slot will transmit the data from 'conf_single_data'
+ * @param mask      The slot mask to selet the slot
+ */
+static inline void i2s_ll_tx_pdm_slot_mode(i2s_dev_t *hw, bool is_mono, bool is_copy, i2s_pdm_slot_mask_t mask)
+{
+    if (is_mono) {
+        /* The default tx_ws_idle_pol is false */
+        if (is_copy) {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 1 : 2;
+        } else {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 4 : 3;
+        }
+    } else {
+        hw->tx_conf.tx_chan_mod = 0;
+    }
+}
+
+/**
+ * @brief PDM TX line mode
+ * @note    Mode         DAC Mode        2 lines output
+ *          -------------------------------------------
+ *          PDM codec    0               1
+ *          DAC 1-line   1               0
+ *          DAC 2-line   1               1
+ *
+ * @param hw    Peripheral I2S hardware instance address.
+ * @param line_mode    PDM TX line mode
+ */
+static inline void i2s_ll_tx_pdm_line_mode(i2s_dev_t *hw, i2s_pdm_tx_line_mode_t line_mode)
+{
+    hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = line_mode > I2S_PDM_TX_ONE_LINE_CODEC;
+    hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = line_mode != I2S_PDM_TX_ONE_LINE_DAC;
+}
+
 #ifdef __cplusplus
 }
 #endif

+ 126 - 14
components/hal/esp32s3/include/hal/i2s_ll.h

@@ -221,6 +221,40 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
     hw->tx_conf1.tx_bck_div_num = val - 1;
 }
 
+/**
+ * @brief Set I2S tx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->tx_clkm_div_conf.tx_clkm_div_x = x;
+    hw->tx_clkm_div_conf.tx_clkm_div_y = y;
+    hw->tx_clkm_div_conf.tx_clkm_div_z = z;
+    hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1;
+}
+
+/**
+ * @brief Set I2S rx raw clock division
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param x  div x
+ * @param y  div y
+ * @param z  div z
+ * @param yn1 yn1
+ */
+static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
+{
+    hw->rx_clkm_div_conf.rx_clkm_div_x = x;
+    hw->rx_clkm_div_conf.rx_clkm_div_y = y;
+    hw->rx_clkm_div_conf.rx_clkm_div_z = z;
+    hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
+}
+
 /**
  * @brief Configure I2S TX module clock divider
  *
@@ -275,7 +309,7 @@ finish:
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
         } else {
             hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1;
+            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_z = numerator;
             hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0;
         }
@@ -349,7 +383,7 @@ finish:
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
         } else {
             hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1;
+            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_z = numerator;
             hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0;
         }
@@ -610,6 +644,29 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
     }
 }
 
+/**
+ * @brief PDM slot mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param mod Channel mode
+ *            while tx_ws_idle_pol = 0:
+ *            0: stereo
+ *            1: Both slots transmit left
+ *            2: Both slots transmit right
+ *            3: Left transmits `conf_single_data` right transmits data
+ *            4: Right transmits `conf_single_data` left transmits data
+ *            while tx_ws_idle_pol = 1:
+              0: stereo
+ *            1: Both slots transmit right
+ *            2: Both slots transmit left
+ *            3: Right transmits `conf_single_data` left transmits data
+ *            4: Left transmits `conf_single_data` right transmits data
+ */
+static inline void i2s_ll_tx_set_pdm_chan_mod(i2s_dev_t *hw, uint32_t mod)
+{
+    hw->tx_conf.tx_chan_mod = mod;
+}
+
 /**
  * @brief Set TX WS signal pol level
  *
@@ -827,18 +884,6 @@ static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable)
     hw->tx_pcm2pdm_conf.tx_hp_bypass = !enable;
 }
 
-/**
- * @brief Enable I2S TX PDM sigma-delta codec
- *
- * @param hw Peripheral I2S hardware instance address.
- * @param dither I2S TX PDM sigmadelta dither value
- */
-static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable)
-{
-    hw->tx_pcm2pdm_conf.tx_dac_2out_en = enable;
-    hw->tx_pcm2pdm_conf.tx_dac_mode_en = enable;
-}
-
 /**
  * @brief Set I2S TX PDM sigma-delta codec dither
  *
@@ -1035,6 +1080,73 @@ static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool ena)
     hw->tx_conf.sig_loopback = ena;
 }
 
+/**
+ * @brief PDM TX DMA data take mode
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_fst_valid  Whether take the DMA data at the first half period
+ *                      Only take effet when 'is_mono' is true
+ */
+static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool is_fst_valid)
+{
+    hw->tx_conf.tx_mono = is_mono;
+    hw->tx_conf.tx_mono_fst_vld = is_fst_valid;
+}
+
+/**
+ * @brief PDM TX slot mode
+ * @note     Mode     Left Slot       Right Slot      Chan Mode       WS Pol
+ *          -----------------------------------------------------------------
+ *           Stereo   Left            Right           0               x
+ *          -----------------------------------------------------------------
+ *           Mono     Left            Left            1               0
+ *           Mono     Right           Right           2               0
+ *           Mono     Single          Right           3               0
+ *           Mono     Left            Single          4               0
+ *          -----------------------------------------------------------------
+ *           Mono     Right           Right           1               1
+ *           Mono     Left            Left            2               1
+ *           Mono     Left            Single          3               1
+ *           Mono     Single          Right           4               1
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param is_mono   The DMA data only has one slot (mono) or contains two slots (stereo)
+ * @param is_copy   Whether the un-selected slot copies the data from the selected one
+ *                  If not, the un-selected slot will transmit the data from 'conf_single_data'
+ * @param mask      The slot mask to selet the slot
+ */
+static inline void i2s_ll_tx_pdm_slot_mode(i2s_dev_t *hw, bool is_mono, bool is_copy, i2s_pdm_slot_mask_t mask)
+{
+    if (is_mono) {
+        /* The default tx_ws_idle_pol is false */
+        if (is_copy) {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 1 : 2;
+        } else {
+            hw->tx_conf.tx_chan_mod = mask == I2S_PDM_SLOT_LEFT ? 4 : 3;
+        }
+    } else {
+        hw->tx_conf.tx_chan_mod = 0;
+    }
+}
+
+/**
+ * @brief PDM TX line mode
+ * @note    Mode         DAC Mode        2 lines output
+ *          -------------------------------------------
+ *          PDM codec    0               1
+ *          DAC 1-line   1               0
+ *          DAC 2-line   1               1
+ *
+ * @param hw    Peripheral I2S hardware instance address.
+ * @param line_mode    PDM TX line mode
+ */
+static inline void i2s_ll_tx_pdm_line_mode(i2s_dev_t *hw, i2s_pdm_tx_line_mode_t line_mode)
+{
+    hw->tx_pcm2pdm_conf.tx_dac_mode_en = line_mode > I2S_PDM_TX_ONE_LINE_CODEC;
+    hw->tx_pcm2pdm_conf.tx_dac_2out_en = line_mode != I2S_PDM_TX_ONE_LINE_DAC;
+}
+
 #ifdef __cplusplus
 }
 #endif

+ 25 - 14
components/hal/i2s_hal.c

@@ -124,12 +124,10 @@ void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal)
 #if SOC_I2S_SUPPORTS_PDM_TX
 void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
 {
-    uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
-                              slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
+    bool is_mono = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO;
     i2s_ll_tx_reset(hal->dev);
     i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
-    i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
-    i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
+    i2s_ll_tx_enable_msb_shift(hal->dev, false);
 
     i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->pdm_tx.sd_prescale);
     i2s_ll_tx_set_pdm_hp_scale(hal->dev, slot_cfg->pdm_tx.hp_scale);
@@ -138,11 +136,23 @@ void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
     i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->pdm_tx.sd_scale);
 
 #if SOC_I2S_HW_VERSION_1
+    uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
+                              slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
     i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
+    i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
+    i2s_ll_tx_enable_mono_mode(hal->dev, is_mono);
 #elif SOC_I2S_HW_VERSION_2
-    /* Still need to enable the first 2 TDM channel mask to get the correct number of frame */
-    i2s_ll_tx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1);
-    i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_tx.hp_en);
+    /* PDM TX line mode */
+    i2s_ll_tx_pdm_line_mode(hal->dev, slot_cfg->pdm_tx.line_mode);
+    /* Force use 32 bit in PDM TX stereo mode to satisfy the frequency */
+    uint32_t slot_bit_width = is_mono ? 16 : 32;
+    i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_bit_width);
+    i2s_ll_tx_set_half_sample_bit(hal->dev, 16); // Fixed to 16 in PDM mode
+    /* By default, taking the DMA data at the first half period of WS  */
+    i2s_ll_tx_pdm_dma_take_mode(hal->dev, is_mono, true);
+    i2s_ll_tx_set_ws_idle_pol(hal->dev, false);
+    /* Slot mode seems not take effect according to the test, leave it default here */
+    i2s_ll_tx_pdm_slot_mode(hal->dev, is_mono, false, I2S_PDM_SLOT_BOTH);
     uint8_t cnt = 0;
     float min = 1000;
     float expt_cut_off = slot_cfg->pdm_tx.hp_cut_off_freq_hz;
@@ -154,9 +164,9 @@ void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
             cnt = i;
         }
     }
+    i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_tx.hp_en);
     i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, cut_off_coef[cnt][1]);
     i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, cut_off_coef[cnt][2]);
-    i2s_ll_tx_enable_pdm_sd_codec(hal->dev, slot_cfg->pdm_tx.sd_en);
     i2s_ll_tx_set_pdm_sd_dither(hal->dev, slot_cfg->pdm_tx.sd_dither);
     i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->pdm_tx.sd_dither2);
 #endif
@@ -176,12 +186,14 @@ void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
     i2s_ll_rx_reset(hal->dev);
     i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
     i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
-    i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
 #if SOC_I2S_HW_VERSION_1
+    i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
+    i2s_ll_rx_select_slot(hal->dev, slot_cfg->pdm_rx.slot_mask, false);
     i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
 #elif SOC_I2S_HW_VERSION_2
-    /* Still need to enable the first 2 TDM channel mask to get the correct number of frame */
-    i2s_ll_rx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1);
+    i2s_ll_rx_enable_mono_mode(hal->dev, false);
+    /* Set the channel mask to enable corresponding slots */
+    i2s_ll_rx_set_active_chan_mask(hal->dev,  slot_cfg->pdm_rx.slot_mask);
 #endif
 }
 
@@ -223,7 +235,7 @@ void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
     i2s_ll_tx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
                                    I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
     i2s_ll_tx_set_skip_mask(hal->dev, slot_cfg->tdm.skip_mask);
-    i2s_ll_tx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
+    i2s_ll_tx_set_half_sample_bit(hal->dev, __builtin_popcount(slot_cfg->tdm.slot_mask) * slot_bit_width / 2);
     i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
     i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
     i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);
@@ -235,7 +247,6 @@ void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
                               slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
     uint32_t cnt;
     uint32_t msk = slot_cfg->tdm.slot_mask;
-    for (cnt = 0; msk; cnt++, msk >>= 1);
     /* Get the maximum slot number */
     cnt = 32 - __builtin_clz(msk);
     /* There should be at least 2 slots in total even for mono mode */
@@ -257,7 +268,7 @@ void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
     /* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */
     i2s_ll_rx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
                                    I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
-    i2s_ll_rx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
+    i2s_ll_rx_set_half_sample_bit(hal->dev, __builtin_popcount(slot_cfg->tdm.slot_mask) * slot_bit_width / 2);
     i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
     i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
     i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);

+ 7 - 1
components/hal/include/hal/i2s_hal.h

@@ -73,13 +73,19 @@ typedef struct {
             i2s_pdm_sig_scale_t     lp_scale;           /*!< Low pass filter scaling value */
             i2s_pdm_sig_scale_t     sinc_scale;         /*!< Sinc filter scaling value */
 #if SOC_I2S_HW_VERSION_2
-            bool                    sd_en;              /*!< Sigma-delta filter enable */
+            i2s_pdm_tx_line_mode_t  line_mode;          /*!< PDM TX line mode, on-line codec, one-line dac, two-line dac mode can be selected */
             bool                    hp_en;              /*!< High pass filter enable */
             float                   hp_cut_off_freq_hz; /*!< High pass filter cut-off frequency, range 23.3Hz ~ 185Hz, see cut-off frequency sheet above */
             uint32_t                sd_dither;          /*!< Sigma-delta filter dither */
             uint32_t                sd_dither2;         /*!< Sigma-delta filter dither2 */
 #endif // SOC_I2S_HW_VERSION_2
         } pdm_tx;                                       /*!< Specific configurations for PDM TX mode */
+#endif
+#if SOC_I2S_SUPPORTS_PDM_RX
+        /* PDM TX configurations */
+        struct {
+            i2s_pdm_slot_mask_t     slot_mask;          /*!< Choose the slots to activate */
+        } pdm_rx;                                       /*!< Specific configurations for PDM TX mode */
 #endif
     };
 

+ 24 - 0
components/hal/include/hal/i2s_types.h

@@ -101,6 +101,20 @@ typedef enum {
     I2S_PDM_SIG_SCALING_MUL_2 = 2,   /*!< I2S TX PDM signal scaling: x2 */
     I2S_PDM_SIG_SCALING_MUL_4 = 3,   /*!< I2S TX PDM signal scaling: x4 */
 } i2s_pdm_sig_scale_t;
+
+#if SOC_I2S_HW_VERSION_2
+/**
+ * @brief PDM TX line mode
+ * @note  For the standard codec mode, PDM pins are connect to a codec which requires both clock signal and data signal
+ *        For the DAC output mode, PDM data signal can be connected to a power amplifier directly with a low-pass filter,
+ *        normally, DAC output mode doesn't need the clock signal.
+ */
+typedef enum {
+    I2S_PDM_TX_ONE_LINE_CODEC,         /*!< Standard PDM format output, left and right slot data on a single line */
+    I2S_PDM_TX_ONE_LINE_DAC,           /*!< PDM DAC format output, left or right slot data on a single line */
+    I2S_PDM_TX_TWO_LINE_DAC,           /*!< PDM DAC format output, left and right slot data on separated lines */
+} i2s_pdm_tx_line_mode_t;
+#endif // SOC_I2S_HW_VERSION_2
 #endif // SOC_I2S_SUPPORTS_PDM_TX
 
 /**
@@ -112,6 +126,16 @@ typedef enum {
     I2S_STD_SLOT_LEFT_RIGHT = BIT(0) | BIT(1),  /*!< I2S transmits or receives both left and right slot */
 } i2s_std_slot_mask_t;
 
+/**
+ * @brief I2S slot select in PDM mode
+ *
+ */
+typedef enum {
+    I2S_PDM_SLOT_RIGHT      = BIT(0),           /*!< I2S PDM only transmits or receives the PDM device whose 'select' pin is pulled up */
+    I2S_PDM_SLOT_LEFT       = BIT(1),           /*!< I2S PDM only transmits or receives the PDM device whose 'select' pin is pulled down */
+    I2S_PDM_SLOT_BOTH       = BIT(0) | BIT(1),  /*!< I2S PDM transmits or receives both two slots */
+} i2s_pdm_slot_mask_t;
+
 #if SOC_I2S_SUPPORTS_TDM
 /**
  * @brief tdm slot number

+ 2 - 0
components/soc/esp32/i2s_periph.c

@@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2S0I_WS_IN_IDX,
 
         .data_out_sig = I2S0O_DATA_OUT23_IDX,
+        .data_out1_sig = -1,
         .data_in_sig  = I2S0I_DATA_IN15_IDX,
 
         .irq          = ETS_I2S0_INTR_SOURCE,
@@ -44,6 +45,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2S1I_WS_IN_IDX,
 
         .data_out_sig = I2S1O_DATA_OUT23_IDX,
+        .data_out1_sig = -1,
         .data_in_sig  = I2S1I_DATA_IN15_IDX,
 
         .irq          = ETS_I2S1_INTR_SOURCE,

+ 1 - 0
components/soc/esp32c3/i2s_periph.c

@@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2SI_WS_IN_IDX,
 
         .data_out_sig = I2SO_SD_OUT_IDX,
+        .data_out1_sig = I2SO_SD1_OUT_IDX,
         .data_in_sig  = I2SI_SD_IN_IDX,
 
         .irq          = -1,

+ 6 - 13
components/soc/esp32c3/include/soc/gpio_sig_map.h

@@ -1,16 +1,8 @@
-// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 #ifndef _SOC_GPIO_SIG_MAP_H_
 #define _SOC_GPIO_SIG_MAP_H_
 
@@ -100,6 +92,7 @@
 #define GPIO_SD1_OUT_IDX              56
 #define GPIO_SD2_OUT_IDX              57
 #define GPIO_SD3_OUT_IDX              58
+#define I2SO_SD1_OUT_IDX              59
 #define FSPICLK_IN_IDX                63
 #define FSPICLK_OUT_IDX               63
 #define FSPIQ_IN_IDX                  64

+ 15 - 11
components/soc/esp32h2/i2s_periph.c

@@ -12,18 +12,22 @@
 */
 const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
     {
-        // TODO ESP32-H2 IDF-2098
+        .mck_out_sig  = I2S_MCLK_OUT_IDX,
+
+        .m_tx_bck_sig = I2SO_BCK_OUT_IDX,
+        .m_rx_bck_sig = I2SI_BCK_OUT_IDX,
+        .m_tx_ws_sig  = I2SO_WS_OUT_IDX,
+        .m_rx_ws_sig  = I2SI_WS_OUT_IDX,
+
+        .s_tx_bck_sig = I2SO_BCK_IN_IDX,
+        .s_rx_bck_sig = I2SI_BCK_IN_IDX,
+        .s_tx_ws_sig  = I2SO_WS_IN_IDX,
+        .s_rx_ws_sig  = I2SI_WS_IN_IDX,
+
+        .data_out_sig = I2SO_SD_OUT_IDX,
+        .data_out1_sig = I2SO_SD1_OUT_IDX,
+        .data_in_sig  = I2SI_SD_IN_IDX,
 
-        // .o_bck_in_sig = I2S0O_BCK_IN_IDX,
-        // .o_ws_in_sig = I2S0O_WS_IN_IDX,
-        // .o_bck_out_sig = I2S0O_BCK_OUT_IDX,
-        // .o_ws_out_sig = I2S0O_WS_OUT_IDX,
-        // .o_data_out_sig = I2S0O_SD_OUT_IDX,
-        // .i_bck_in_sig = I2S0I_BCK_OUT_IDX,
-        // .i_ws_in_sig = I2S0I_WS_OUT_IDX,
-        // .i_bck_out_sig = I2S0I_BCK_IN_IDX,
-        // .i_ws_out_sig = I2S0I_WS_IN_IDX,
-        // .i_data_in_sig = I2S0I_SD_IN_IDX,
         .irq = ETS_I2S1_INTR_SOURCE,
         .module = PERIPH_I2S1_MODULE,
     }

+ 1 - 0
components/soc/esp32s2/i2s_periph.c

@@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2S0I_WS_IN_IDX,
 
         .data_out_sig = I2S0O_DATA_OUT23_IDX,
+        .data_out1_sig = -1,
         .data_in_sig  = I2S0I_DATA_IN15_IDX,
 
         .irq          = ETS_I2S0_INTR_SOURCE,

+ 2 - 0
components/soc/esp32s3/i2s_periph.c

@@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2S0I_WS_IN_IDX,
 
         .data_out_sig = I2S0O_SD_OUT_IDX,
+        .data_out1_sig = I2S0O_SD1_OUT_IDX,
         .data_in_sig  = I2S0I_SD_IN_IDX,
 
         .irq          = -1,
@@ -44,6 +45,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
         .s_rx_ws_sig  = I2S1I_WS_IN_IDX,
 
         .data_out_sig = I2S1O_SD_OUT_IDX,
+        .data_out1_sig = -1,
         .data_in_sig  = I2S1I_SD_IN_IDX,
 
         .irq          = -1,

+ 1 - 0
components/soc/include/soc/i2s_periph.h

@@ -32,6 +32,7 @@ typedef struct {
     const uint8_t s_rx_ws_sig;
 
     const uint8_t data_out_sig;
+    const uint8_t data_out1_sig; // Only valid in version 2
     const uint8_t data_in_sig;
 
     const uint8_t irq;

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

@@ -408,8 +408,8 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
 
         /* Init the channel into PDM TX mode */
         i2s_pdm_tx_config_t pdm_tx_cfg = {
-            .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(36000),
-            .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+            .clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(36000),
+            .slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
             .gpio_cfg = {
                 .clk = GPIO_NUM_5,
                 .dout = GPIO_NUM_18,
@@ -448,12 +448,12 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
 
         /* Allocate an I2S rx channel */
         i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
-        i2s_new_channel(&chan_cfg, &rx_handle, NULL);
+        i2s_new_channel(&chan_cfg, NULL, &rx_handle);
 
         /* Init the channel into PDM RX mode */
         i2s_pdm_rx_config_t pdm_rx_cfg = {
-            .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(36000),
-            .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+            .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(36000),
+            .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
             .gpio_cfg = {
                 .clk = GPIO_NUM_5,
                 .din = GPIO_NUM_19,

+ 1 - 3
docs/en/migration-guides/release-5.x/peripherals.rst

@@ -277,9 +277,7 @@ LCD
     I2S driver
     ----------
 
-    {I2S_DRIVER_HEADERS:default=":component_file:`driver/include/driver/i2s_std.h`, :component_file:`driver/include/driver/i2s_pdm.h` or :component_file:`driver/include/driver/i2s_tdm.h`", esp32=":component_file:`driver/include/driver/i2s_std.h` or :component_file:`driver/include/driver/i2s_pdm.h`", esp32s2=":component_file:`driver/include/driver/i2s_std.h`"}
-
-    Shortcomings are exposed when supporting all the new features of ESP32-C3 & ESP32-S3 by the old I2S driver, so it is re-designed to make it more compatible and flexible to all the communication modes. New APIs are available by including corresponding mode header files {I2S_DRIVER_HEADERS}. Meanwhile, the old APIs in :component_file:`driver/deprecated/driver/i2s.h` are still supported for backward compatibility. But there will be warnings if you keep using the old APIs in your project, these warnings can be suppressed by the Kconfig option :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN`. Here is the general overview of the current I2S files:
+    Shortcomings are exposed when supporting all the new features of ESP32-C3 & ESP32-S3 by the old I2S driver, so it is re-designed to make it more compatible and flexible to all the communication modes. New APIs are available by including corresponding mode header files :component_file:`driver/include/driver/i2s_std.h`, :component_file:`driver/include/driver/i2s_pdm.h` or :component_file:`driver/include/driver/i2s_tdm.h`. Meanwhile, the old APIs in :component_file:`driver/deprecated/driver/i2s.h` are still supported for backward compatibility. But there will be warnings if you keep using the old APIs in your project, these warnings can be suppressed by the Kconfig option :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN`. Here is the general overview of the current I2S files:
 
     .. figure:: ../../../_static/diagrams/i2s/i2s_file_structure.png
         :align: center

+ 0 - 98
examples/peripherals/i2s/i2s_basic/README.md

@@ -1,98 +0,0 @@
-| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- | -------- |
-
-# I2S Example
-
-(See the README.md file in the upper level 'examples' directory for more information about examples.)
-
-In this example, we generate a 100Hz triangle and sine wave and send it out from left and right channels at a sample rate of 36kHz through the I2S bus.
-
-## How to Use Example
-
-### Hardware Required
-
-* A development board with ESP32/ESP32-S2/ESP32-C3/ESP32-S3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
-* A USB cable for power supply and programming
-
-### Configure the Project
-
-```
-idf.py menuconfig
-```
-
-### Build and Flash
-
-Build the project and flash it to the board, then run monitor tool to view serial output:
-
-```
-idf.py -p PORT flash monitor
-```
-
-(To exit the serial monitor, type ``Ctrl-]``.)
-
-See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
-
-## Example Output
-
-Running this example, you will see I2S start to read and write data, and only the first 4 elements in the receive buffer will be displayed. The output log can be seen below:
-
-```
-[i2s write] 1440 bytes are written successfully
-
-[i2s read] 8192 bytes are read successfully
-----------------------------------------------
-[0]    0 [1]    0 [2]    0 [3]    0
-
-[i2s write] 1440 bytes are written successfully
-[i2s write] 1440 bytes are written successfully
-[i2s write] 1440 bytes are written successfully
-
-[i2s read] 8192 bytes are read successfully
-----------------------------------------------
-[0] a7d468d9 [1] a88a6a1d [2] a9406b58 [3] a9f66c8b
-
-[i2s write] 1440 bytes are written successfully
-[i2s write] 1440 bytes are written successfully
-
-[i2s read] 8192 bytes are read successfully
-----------------------------------------------
-[0] 8b622120 [1] 8c182347 [2] 8cce256c [3] 8d84278d
-```
-
-There is a abnormal case that printing `Data dropped`, it is caused by a long polling time of `i2s_channel_read`, please refer to the `Application Notes` section in I2S API reference.
-
-```
-[i2s read] 8192 bytes are read successfully
-----------------------------------------------
-[0] a7d468d9 [1] a88a6a1d [2] a9406b58 [3] a9f66c8b
-
-
-[i2s monitor] Data dropped
-
-
-[i2s monitor] Data dropped
-
-
-[i2s monitor] Data dropped
-
-[i2s write] 1440 bytes are written successfully
-
-[i2s monitor] Data dropped
-```
-
-If you have a logic analyzer, you can use a logic analyzer to grab online data. The following table describes the pins we use by default (Note that you can also use other pins for the same purpose).
-
-| pin name| function | gpio_num |
-|:---:|:---:|:---:|
-| WS  |word select| GPIO_NUM_15 |
-| SCK |continuous serial clock| GPIO_NUM_13 |
-| SD  |serial data| GPIO_NUM_21 |
-
-## Troubleshooting
-
-* Program upload failure
-
-    * Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
-    * The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
-
-For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

+ 1 - 1
examples/peripherals/i2s/i2s_basic/CMakeLists.txt → examples/peripherals/i2s/i2s_basic/i2s_pdm/CMakeLists.txt

@@ -3,4 +3,4 @@
 cmake_minimum_required(VERSION 3.16)
 
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-project(esp32_i2s_driver_example)
+project(i2s_pdm_example)

+ 165 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md

@@ -0,0 +1,165 @@
+| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- |
+
+# I2S Basic Standard Mode Example
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+This example is going to show how to use the PDM TX and RX mode.
+
+## How to Use Example
+
+### Hardware Required
+
+#### General
+
+* A development board with any one of ESP32, ESP32-C3 or ESP32-S3 SoC
+* A USB cable for power supply and programming
+
+#### PDM RX
+
+* A PDM microphone whose `sel` pin is supposed to be pulled down, and connecting its `clk` pin to `GPIO_NUM_4`, `data` pin to `GPIO_NUM_5`.
+
+```
+┌─────────────┐               ┌──────────────────┐
+│     ESP     │               │   PDM microphone │
+│             │   PDM clock   │                  │
+│      GPIO 0 ├──────────────►│ CLK              │
+│             │   PDM data    │                  │
+│      GPIO 2 │◄──────────────┤ DATA             │
+│             │               │                  │
+│             │         ┌─────┤ SEL              │
+│             │         │     │                  │
+│         GND ├─────────┴─────┤ GND              │
+│             │               │                  │
+│         VCC ├───────────────┤ VCC              │
+└─────────────┘               └──────────────────┘
+```
+
+#### 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 band-pass filter is required to restore the PDM data wave into analog signal, before it is transmitted to the power amplifier.
+
+**MAX98358**
+
+```
+┌─────────────┐               ┌───────────────┐
+│     ESP     │               │   MAX 98358   │
+│             │   PDM clock   │               │
+│      GPIO 4 ├──────────────►│ CLK           │   ┌─────────┐
+│             │   PDM data    │               │   │ Speaker │
+│      GPIO 5 ├──────────────►│ DATA     OUTP ├───┤         │
+│             │               │               │   │         │
+│             │         ┌─────┤ SD_MODE  OUTN ├───┤         │
+│             │         │     │               │   │         │
+│         VCC ├─────────┴─────┤ VCC           │   └─────────┘
+│             │               │               │
+│         GND ├───────────────┤ GND           │
+└─────────────┘               └───────────────┘
+```
+
+**NS4150**
+
+```
+┌─────────────┐                              ┌───────────────┐
+│     ESP     │                              │    NS 4150    │
+│             │                              │               │
+│      GPIO 4 │                              │ INN           │   ┌─────────┐
+│             │PDM data┌────────────────┐    │               │   │ Speaker │
+│      GPIO 5 ├────────┤Band-pass Filter├───►│ INP       VoP ├───┤         │
+│             │        └────────────────┘    │               │   │         │
+│             │                          ┌───┤ CTRL      VoN ├───┤         │
+│             │                          │   │               │   │         │
+│         VCC ├──────────────────────────┴───┤ VCC           │   └─────────┘
+│             │                              │               │
+│         GND ├──────────────────────────────┤ GND           │
+└─────────────┘                              └───────────────┘
+```
+
+### Configure the Project
+
+PDM can only works in simplex mode, setting the macro `EXAMPLE_PDM_DIR` to `EXAMPLE_PDM_TX` or `EXAMPLE_PDM_RX` can choose the PDM direction of this example. But currently ESP32-C3 does not support PDM RX mode.
+
+### Build and Flash
+
+Build the project and flash it to the board, then run monitor tool to view serial output:
+
+```
+idf.py -p PORT build flash monitor
+```
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+### PDM TX
+
+While `EXAMPLE_PDM_DIR` is set to `EXAMPLE_PDM_TX`, then you can see the following log:
+
+```
+I2S PDM TX example start
+---------------------------
+D (284) i2s_common: tx channel is registered on I2S0 successfully
+D (294) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
+D (304) i2s_pdm: Clock division info: [sclk] 160000000 Hz [mdiv] 3 [mclk] 49152000 Hz [bdiv] 8 [bclk] 6144000 Hz
+D (314) i2s_pdm: The tx channel on I2S0 has been initialized to PDM TX mode successfully
+D (324) i2s_common: i2s tx channel enabled
+Playing bass `twinkle twinkle little star`
+Playing alto `twinkle twinkle little star`
+Playing treble `twinkle twinkle little star`
+...
+```
+
+You can hear the audio 'twinkle twinkle little star' in three tones if you connected a speaker.on it.
+
+### PDM RX
+
+While `EXAMPLE_PDM_DIR` is set to `EXAMPLE_PDM_RX`, but without connecting a PDM microphone, then you can see the following log:
+
+```
+I2S PDM RX example start
+---------------------------
+D (10) i2s_common: rx channel is registered on I2S0 successfully
+D (10) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
+D (20) i2s_common: i2s rx channel enabled
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] -6595 [1] 0 [2] -29199 [3] -32768
+[4] -30203 [5] -32156 [6] -30704 [7] -31348
+
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] -30935 [1] -30935 [2] -30935 [3] -30935
+[4] -30935 [5] -30935 [6] -30935 [7] -30935
+
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] -30935 [1] -30935 [2] -30935 [3] -30935
+[4] -30935 [5] -30935 [6] -30935 [7] -30935
+```
+
+And only if you connect a PDM microphone, you can see the data is change:
+
+```
+I2S PDM RX example start
+---------------------------
+D (10) i2s_common: rx channel is registered on I2S0 successfully
+D (10) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
+D (20) i2s_common: i2s rx channel enabled
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] -3181 [1] 0 [2] -7194 [3] -24288
+[4] -777 [5] 3650 [6] 109 [7] 571
+
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 2391 [1] 2378 [2] 2397 [3] 2385
+[4] 2399 [5] 2375 [6] 2401 [7] 2389
+```
+
+## Troubleshooting
+
+For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

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

@@ -0,0 +1,12 @@
+set(srcs "i2s_pdm_example_main.c")
+
+if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX)
+    list(APPEND srcs "i2s_pdm_tx.c")
+endif()
+
+if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX)
+    list(APPEND srcs "i2s_pdm_rx.c")
+endif()
+
+idf_component_register(SRCS "${srcs}"
+                    INCLUDE_DIRS ".")

+ 23 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example.h

@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#pragma once
+
+#define EXAMPLE_BUFF_SIZE   2048
+
+/**
+ * @brief I2S PDM TX example task
+ *
+ * @param args  The user data given from task creating, not used in this example
+ */
+void i2s_example_pdm_tx_task(void *args);
+
+/**
+ * @brief I2S PDM RX example task
+ *
+ * @param args  The user data given from task creating, not used in this example
+ */
+void i2s_example_pdm_rx_task(void *args);

+ 31 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example_main.c

@@ -0,0 +1,31 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sdkconfig.h"
+#include "i2s_pdm_example.h"
+
+#define EXAMPLE_PDM_TX      0
+/* ESP32-C3 does not support PDM RX currently */
+#if !CONFIG_IDF_TARGET_ESP32C3
+#define EXAMPLE_PDM_RX      1
+#endif
+
+#define EXAMPLE_PDM_DIR     EXAMPLE_PDM_TX
+
+void app_main(void)
+{
+#if EXAMPLE_PDM_DIR == EXAMPLE_PDM_TX
+    printf("I2S PDM TX example start\n---------------------------\n");
+    xTaskCreate(i2s_example_pdm_tx_task, "i2s_example_pdm_tx_task", 4096, NULL, 5, NULL);
+#else
+    printf("I2S PDM RX example start\n---------------------------\n");
+    xTaskCreate(i2s_example_pdm_rx_task, "i2s_example_pdm_rx_task", 4096, NULL, 5, NULL);
+#endif
+}

+ 78 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c

@@ -0,0 +1,78 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s_pdm.h"
+#include "driver/i2s_std.h"
+#include "driver/gpio.h"
+#include "esp_check.h"
+#include "sdkconfig.h"
+#include "i2s_pdm_example.h"
+
+#include "hal/i2s_ll.h"
+
+#define EXAMPLE_PDM_RX_CLK_IO           GPIO_NUM_0      // I2S PDM RX clock io number
+#define EXAMPLE_PDM_RX_DIN_IO           GPIO_NUM_2      // I2S PDM RX data in io number
+
+#define EXAMPLE_PDM_RX_FREQ_HZ          16000           // I2S PDM RX frequency
+
+static i2s_chan_handle_t                rx_chan;        // I2S rx channel handler
+
+static void i2s_example_init_pdm_rx(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate RX channel only
+     * The default configuration can be generated by the helper macro,
+     * but note that PDM channel can only be registered on I2S_NUM_0 */
+    i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
+
+    /* Step 2: Setting the configurations of PDM RX mode and initialize the RX channel
+     * The slot configuration and clock configuration can be generated by the macros
+     * These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM RX mode.
+     * They can help to specify the slot and clock configurations for initialization or re-configuring */
+    i2s_pdm_rx_config_t pdm_rx_cfg = {
+        .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_RX_FREQ_HZ),
+        /* The data bit-width of PDM mode is fixed to 16 */
+        .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+        .gpio_cfg = {
+            .clk = EXAMPLE_PDM_RX_CLK_IO,
+            .din = EXAMPLE_PDM_RX_DIN_IO,
+            .invert_flags = {
+                .clk_inv = false,
+            },
+        },
+    };
+    ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));
+
+    /* Step 3: Enable the rx channels before reading data */
+    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
+}
+
+
+void i2s_example_pdm_rx_task(void *args)
+{
+    int16_t *r_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(r_buf);
+    i2s_example_init_pdm_rx();
+
+    size_t r_bytes = 0;
+    while (1) {
+        /* Read i2s data */
+        if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
+            printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
+            printf("[0] %d [1] %d [2] %d [3] %d\n[4] %d [5] %d [6] %d [7] %d\n\n",
+                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
+        } else {
+            printf("Read Task: i2s read failed\n");
+        }
+        vTaskDelay(pdMS_TO_TICKS(200));
+    }
+    free(r_buf);
+    vTaskDelete(NULL);
+}

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

@@ -0,0 +1,109 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdint.h>
+#include <math.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s_pdm.h"
+#include "driver/gpio.h"
+#include "esp_check.h"
+#include "sdkconfig.h"
+#include "i2s_pdm_example.h"
+
+#define EXAMPLE_PDM_TX_CLK_IO           GPIO_NUM_4      // I2S PDM TX clock io number
+#define EXAMPLE_PDM_TX_DOUT_IO          GPIO_NUM_5      // I2S PDM TX data out io number
+
+#define EXAMPLE_PDM_TX_FREQ_HZ          44100           // I2S PDM TX frequency
+#define EXAMPLE_WAVE_AMPTITUDE          (1000.0)        // 1~32767
+#define CONST_PI                        (3.1416f)
+#define EXAMPLE_SINE_WAVE_LEN(tone)     (uint32_t)((EXAMPLE_PDM_TX_FREQ_HZ / (float)tone) + 0.5) // The sample point number per sine wave to generate the tone
+#define EXAMPLE_TONE_LAST_TIME_MS       500
+#define EXAMPLE_BYTE_NUM_EVERY_TONE     (EXAMPLE_TONE_LAST_TIME_MS * EXAMPLE_PDM_TX_FREQ_HZ / 1000)
+
+static i2s_chan_handle_t                tx_chan;        // I2S tx channel handler
+
+static const uint32_t tone[3][7] = {{262, 294, 330, 349, 392, 440, 494},
+                                    {523, 587, 659, 698, 784, 880, 988},
+                                    {1046, 1175, 1318, 1397, 1568, 1760, 1976}}; // The frequency of tones: do, re, mi, fa, so, la, si, in Hz.
+static const uint8_t song[28] = {1, 1, 5, 5, 6, 6, 5,
+                                 4, 4, 3, 3, 2, 2, 1,
+                                 5, 5, 4, 4, 3, 3, 2,
+                                 5, 5, 4, 4, 3, 3, 2};  // Numbered musical notation of 'twinkle twinkle little star'
+static const uint8_t rhythm[7] = {1, 1, 1, 1, 1, 1, 2}; // Rhythm of 'twinkle twinkle little star', it's repeated in four sections
+
+static const char *tone_name[3] = {"bass", "alto", "treble"};
+
+void i2s_example_init_pdm_tx(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate TX channel only
+     * The default configuration can be generated by the helper macro,
+     * it only requires the I2S controller id and I2S role,
+     * but note that PDM channel can only be registered on I2S_NUM_0 */
+    i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    tx_chan_cfg.auto_clear = true;
+    ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
+
+    /* Step 2: Setting the configurations of PDM TX mode and initialize the TX channel
+     * The slot configuration and clock configuration can be generated by the macros
+     * 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 = {
+        .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),
+        .gpio_cfg = {
+            .clk = EXAMPLE_PDM_TX_CLK_IO,
+            .dout = EXAMPLE_PDM_TX_DOUT_IO,
+            .invert_flags = {
+                .clk_inv = false,
+            },
+        },
+    };
+    ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg));
+
+    /* Step 3: Enable the tx channel before writing data */
+    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
+}
+
+void i2s_example_pdm_tx_task(void *args)
+{
+    int16_t *w_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(w_buf);
+    i2s_example_init_pdm_tx();
+
+    size_t w_bytes = 0;
+
+    uint8_t cnt = 0;            // The current index of the song
+    uint8_t tone_select = 0;    // To selecting the tone level
+
+    printf("Playing %s `twinkle twinkle little star`\n", tone_name[tone_select]);
+    while (1) {
+        int tone_point = EXAMPLE_SINE_WAVE_LEN(tone[tone_select][song[cnt]-1]);
+        /* Generate the tone buffer */
+        for (int i = 0; i < tone_point; i++) {
+            w_buf[i] =  (int16_t)((sin(2 * (float)i * CONST_PI / tone_point)) * EXAMPLE_WAVE_AMPTITUDE);
+        }
+        for (int tot_bytes = 0; tot_bytes < EXAMPLE_BYTE_NUM_EVERY_TONE * rhythm[cnt % 7]; tot_bytes += w_bytes) {
+            /* Play the tone */
+            if (i2s_channel_write(tx_chan, w_buf, tone_point * sizeof(int16_t), &w_bytes, 1000) != ESP_OK) {
+                printf("Write Task: i2s write failed\n");
+            }
+        }
+        cnt++;
+        /* If finished played, switch the tone level */
+        if (cnt == sizeof(song)) {
+            cnt = 0;
+            tone_select++;
+            tone_select %= 3;
+            printf("Playing %s `twinkle twinkle little star`\n", tone_name[tone_select]);
+        }
+        /* Gap between the tones */
+        vTaskDelay(15);
+    }
+    free(w_buf);
+    vTaskDelete(NULL);
+}

+ 22 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py

@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: CC0-1.0
+
+import pytest
+from pytest_embedded import Dut
+
+
+@pytest.mark.esp32
+@pytest.mark.esp32s3
+@pytest.mark.esp32c3
+@pytest.mark.generic
+def test_i2s_pdm_example(dut: Dut) -> None:
+    dut.expect(r'I2S PDM TX example start', timeout=5)
+    dut.expect(r'---------------------------', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: tx channel is registered on I2S0 successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
+               r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_pdm: Clock division info: \[sclk\] ([0-9]+) Hz '
+               r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_pdm: The tx channel on I2S0 has been initialized to PDM TX mode successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5)
+    dut.expect(r'Playing bass `twinkle twinkle little star`', timeout=5)

+ 1 - 0
examples/peripherals/i2s/i2s_basic/i2s_pdm/sdkconfig.defaults

@@ -0,0 +1 @@
+CONFIG_I2S_ENABLE_DEBUG_LOG=y

+ 6 - 0
examples/peripherals/i2s/i2s_basic/i2s_std/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(i2s_std_example)

+ 73 - 0
examples/peripherals/i2s/i2s_basic/i2s_std/README.md

@@ -0,0 +1,73 @@
+| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
+
+# I2S Basic Standard Mode Example
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+This example is going to show how to use the standard mode in simplex mode or full-duplex mode.
+
+## How to Use Example
+
+### Hardware Required
+
+* A development board with any one of ESP32, ESP32-S2, ESP32-C3 or ESP32-S3 SoC
+* A USB cable for power supply and programming
+
+### Configure the Project
+
+There are simplex mode and duplex mode can be chosen in this example, setting `EXAMPLE_I2S_DUPLEX_MODE` to `0` will adopt the simplex mode, otherwise it will adopt the full-duplex mode.
+
+Note that ESP32-S2 simplex mode is not available because this example requires both TX & RX channels, however, ESP32-S2 has only one I2S controller and the simplex TX & RX channels can't coexist in a same controller. By the way, the simplex TX & RX channels can't coexist on ESP32 as well, but ESP32 has two I2S controllers so the simplex TX & RX channels can be registered on the different I2S controllers.
+
+### Build and Flash
+
+Build the project and flash it to the board, then run monitor tool to view serial output:
+
+```
+idf.py -p PORT build flash monitor
+```
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+While `EXAMPLE_I2S_DUPLEX_MODE` is set to `1`, then you can see the following log:
+
+```
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 12 [1] 34 [2] 56 [3] 78
+[4] 9a [5] bc [6] de [7] f0
+
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 12 [1] 34 [2] 56 [3] 78
+[4] 9a [5] bc [6] de [7] f0
+...
+```
+
+While `EXAMPLE_I2S_DUPLEX_MODE` is set to `0`, you can see the receiving data is always `0` because no signal inputted:
+
+```
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 0 [1] 0 [2] 0 [3] 0
+[4] 0 [5] 0 [6] 0 [7] 0
+
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 0 [1] 0 [2] 0 [3] 0
+[4] 0 [5] 0 [6] 0 [7] 0
+...
+```
+
+## Troubleshooting
+
+For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

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

@@ -0,0 +1,2 @@
+idf_component_register(SRCS "i2s_std_example_main.c"
+                    INCLUDE_DIRS ".")

+ 210 - 0
examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c

@@ -0,0 +1,210 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s_std.h"
+#include "driver/gpio.h"
+#include "esp_check.h"
+#include "sdkconfig.h"
+
+/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
+ * Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated,
+ * Specifically, due to the hardware limitation, the simplex rx & tx channels can't be registered on the same controllers on ESP32 and ESP32-S2,
+ * and ESP32-S2 has only one I2S controller, so it can't allocate two simplex channels */
+#define EXAMPLE_I2S_DUPLEX_MODE     (1 || CONFIG_IDF_TARGET_ESP32S2)
+
+#if CONFIG_IDF_TARGET_ESP32
+    #define EXAMPLE_STD_BCLK_IO1        GPIO_NUM_4      // I2S bit clock io number
+    #define EXAMPLE_STD_WS_IO1          GPIO_NUM_5      // I2S word select io number
+    #define EXAMPLE_STD_DOUT_IO1        GPIO_NUM_18     // I2S data out io number
+    #define EXAMPLE_STD_DIN_IO1         GPIO_NUM_19     // I2S data in io number
+    #if !EXAMPLE_I2S_DUPLEX_MODE
+        #define EXAMPLE_STD_BCLK_IO2    GPIO_NUM_22     // I2S bit clock io number
+        #define EXAMPLE_STD_WS_IO2      GPIO_NUM_23     // I2S word select io number
+        #define EXAMPLE_STD_DOUT_IO2    GPIO_NUM_25     // I2S data out io number
+        #define EXAMPLE_STD_DIN_IO2     GPIO_NUM_26     // I2S data in io number
+    #endif
+#else
+    #define EXAMPLE_STD_BCLK_IO1        GPIO_NUM_2      // I2S bit clock io number
+    #define EXAMPLE_STD_WS_IO1          GPIO_NUM_3      // I2S word select io number
+    #define EXAMPLE_STD_DOUT_IO1        GPIO_NUM_4      // I2S data out io number
+    #define EXAMPLE_STD_DIN_IO1         GPIO_NUM_5      // I2S data in io number
+    #if !EXAMPLE_I2S_DUPLEX_MODE
+        #define EXAMPLE_STD_BCLK_IO2    GPIO_NUM_6      // I2S bit clock io number
+        #define EXAMPLE_STD_WS_IO2      GPIO_NUM_7      // I2S word select io number
+        #define EXAMPLE_STD_DOUT_IO2    GPIO_NUM_8      // I2S data out io number
+        #define EXAMPLE_STD_DIN_IO2     GPIO_NUM_9      // I2S data in io number
+    #endif
+#endif
+
+#define EXAMPLE_BUFF_SIZE               2048
+
+static i2s_chan_handle_t                tx_chan;        // I2S tx channel handler
+static i2s_chan_handle_t                rx_chan;        // I2S rx channel handler
+
+static void i2s_example_read_task(void *args)
+{
+    uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(r_buf); // Check if r_buf allocation success
+    size_t r_bytes = 0;
+    while (1) {
+        /* Read i2s data */
+        if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
+            printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
+            printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
+                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
+        } else {
+            printf("Read Task: i2s read failed\n");
+        }
+        vTaskDelay(pdMS_TO_TICKS(200));
+    }
+    free(r_buf);
+    vTaskDelete(NULL);
+}
+
+static void i2s_example_write_task(void *args)
+{
+    uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(w_buf); // Check if w_buf allocation success
+
+    /* Assign w_buf */
+    for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
+        w_buf[i]     = 0x12;
+        w_buf[i + 1] = 0x34;
+        w_buf[i + 2] = 0x56;
+        w_buf[i + 3] = 0x78;
+        w_buf[i + 4] = 0x9A;
+        w_buf[i + 5] = 0xBC;
+        w_buf[i + 6] = 0xDE;
+        w_buf[i + 7] = 0xF0;
+    }
+
+    size_t w_bytes = 0;
+    while (1) {
+        /* Write i2s data */
+        if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
+            printf("Write Task: i2s write %d bytes\n", w_bytes);
+        } else {
+            printf("Write Task: i2s write failed\n");
+        }
+        vTaskDelay(pdMS_TO_TICKS(200));
+    }
+    free(w_buf);
+    vTaskDelete(NULL);
+}
+
+#if EXAMPLE_I2S_DUPLEX_MODE
+static void i2s_example_init_std_duplex(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate both channels
+     * The default configuration can be generated by the helper macro,
+     * it only requires the I2S controller id and I2S role */
+    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
+
+    /* Step 2: Setting the configurations of standard mode, and initialize rx & tx channels
+     * The slot configuration and clock configuration can be generated by the macros
+     * These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
+     * They can help to specify the slot and clock configurations for initialization or re-configuring */
+    i2s_std_config_t std_cfg = {
+        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(16000),
+        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_STD_BCLK_IO1,
+            .ws   = EXAMPLE_STD_WS_IO1,
+            .dout = EXAMPLE_STD_DOUT_IO1,
+            .din  = EXAMPLE_STD_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    /* Initialize the channels */
+    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
+    ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
+}
+
+#else
+
+static void i2s_example_init_std_simplex(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
+     * The default configuration can be generated by the helper macro,
+     * it only requires the I2S controller id and I2S role
+     * The tx and rx channels here are registered on different I2S controller,
+     * only ESP32-C3, ESP32-S3 and ESP32-H2 allow to register two separate tx & rx channels on a same controller */
+    i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
+    i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
+
+    /* Step 2: Setting the configurations of standard mode and initialize each channels one by one
+     * The slot configuration and clock configuration can be generated by the macros
+     * These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
+     * They can help to specify the slot and clock configurations for initialization or re-configuring */
+    i2s_std_config_t tx_std_cfg = {
+        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(16000),
+        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_STD_BCLK_IO1,
+            .ws   = EXAMPLE_STD_WS_IO1,
+            .dout = EXAMPLE_STD_DOUT_IO1,
+            .din  = EXAMPLE_STD_DIN_IO1,
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
+
+    i2s_std_config_t rx_std_cfg = {
+        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(44100),
+        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_STD_BCLK_IO2,
+            .ws   = EXAMPLE_STD_WS_IO2,
+            .dout = EXAMPLE_STD_DOUT_IO2,
+            .din  = EXAMPLE_STD_DIN_IO2,
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    /* Default is only receiving left slot in mono mode,
+     * update to right here to show how to change the default configuration */
+    rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
+    ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
+}
+#endif
+
+void app_main(void)
+{
+#if EXAMPLE_I2S_DUPLEX_MODE
+    i2s_example_init_std_duplex();
+#else
+    i2s_example_init_std_simplex();
+#endif
+
+    /* Step 3: Enable the tx and rx channels before writing or reading data */
+    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
+    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
+
+    /* Step 4: Create writing and reading task */
+    xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
+    xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL);
+}

+ 35 - 0
examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py

@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: CC0-1.0
+
+import pytest
+from pytest_embedded import Dut
+
+
+@pytest.mark.esp32
+@pytest.mark.esp32s2
+@pytest.mark.esp32s3
+@pytest.mark.esp32c3
+@pytest.mark.generic
+def test_i2s_basic_example(dut: Dut) -> None:
+
+    dut.expect(r'D \(([0-9]+)\) i2s_common: tx channel is registered on I2S0 successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: rx channel is registered on I2S0 successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
+               r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz '
+               r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_std: The tx channel on I2S0 has been initialized to STD mode successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
+               r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz '
+               r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_std: The rx channel on I2S0 has been initialized to STD mode successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled', timeout=5)
+    dut.expect(r'Write Task: i2s write ([0-9]+) bytes', timeout=5)
+    dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5)
+    dut.expect(r'-----------------------------------', timeout=5)
+    dut.expect(r'\[0\] 0 \[1\] 0 \[2\] 0 \[3\] 0', timeout=5)
+    dut.expect(r'\[4\] 0 \[5\] 0 \[6\] 0 \[7\] 0', timeout=5)
+    dut.expect(r'\[0\] 12 \[1\] 34 \[2\] 56 \[3\] 78', timeout=10)
+    dut.expect(r'\[4\] 9a \[5\] bc \[6\] de \[7\] f0', timeout=10)

+ 1 - 0
examples/peripherals/i2s/i2s_basic/i2s_std/sdkconfig.defaults

@@ -0,0 +1 @@
+CONFIG_I2S_ENABLE_DEBUG_LOG=y

+ 6 - 0
examples/peripherals/i2s/i2s_basic/i2s_tdm/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(i2s_tdm_example)

+ 72 - 0
examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md

@@ -0,0 +1,72 @@
+| Supported Targets | ESP32-C3 | ESP32-S3 |
+| ----------------- | -------- | -------- |
+
+# I2S Basic TDM Mode Example
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+This example is going to show how to use the TDM mode in simplex mode or full-duplex mode.
+
+## How to Use Example
+
+### Hardware Required
+
+* A development board with any one of ESP32-C3 or ESP32-S3 SoC
+* A USB cable for power supply and programming
+
+### Configure the Project
+
+There are simplex mode and duplex mode can be chosen in this example, setting `EXAMPLE_I2S_DUPLEX_MODE` to `0` will adopt the simplex mode, otherwise it will adopt the full-duplex mode.
+
+### Build and Flash
+
+Build the project and flash it to the board, then run monitor tool to view serial output:
+
+```
+idf.py -p PORT build flash monitor
+```
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+While `EXAMPLE_I2S_DUPLEX_MODE` is set to `1`, then you can see the following log:
+
+```
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 12 [1] 34 [2] 56 [3] 78
+[4] 9a [5] bc [6] de [7] f0
+
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 12 [1] 34 [2] 56 [3] 78
+[4] 9a [5] bc [6] de [7] f0
+...
+```
+
+While `EXAMPLE_I2S_DUPLEX_MODE` is set to `0`, you can see the receiving data is always `0` because no signal inputted:
+
+```
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 0 [1] 0 [2] 0 [3] 0
+[4] 0 [5] 0 [6] 0 [7] 0
+
+Write Task: i2s write 2048 bytes
+Read Task: i2s read 2048 bytes
+-----------------------------------
+[0] 0 [1] 0 [2] 0 [3] 0
+[4] 0 [5] 0 [6] 0 [7] 0
+...
+```
+
+## Troubleshooting
+
+For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
+

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

@@ -0,0 +1,2 @@
+idf_component_register(SRCS "i2s_tdm_example_main.c"
+                    INCLUDE_DIRS ".")

+ 207 - 0
examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c

@@ -0,0 +1,207 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s_tdm.h"
+#include "driver/gpio.h"
+#include "esp_check.h"
+#include "sdkconfig.h"
+
+/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
+ * Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated */
+#define EXAMPLE_I2S_DUPLEX_MODE     1
+
+#define EXAMPLE_TDM_BCLK_IO1        GPIO_NUM_2      // I2S bit clock io number
+#define EXAMPLE_TDM_WS_IO1          GPIO_NUM_3      // I2S word select io number
+#define EXAMPLE_TDM_DOUT_IO1        GPIO_NUM_4      // I2S data out io number
+#define EXAMPLE_TDM_DIN_IO1         GPIO_NUM_5      // I2S data in io number
+#if !EXAMPLE_I2S_DUPLEX_MODE
+    #define EXAMPLE_TDM_BCLK_IO2    GPIO_NUM_6      // I2S bit clock io number
+    #define EXAMPLE_TDM_WS_IO2      GPIO_NUM_7      // I2S word select io number
+    #define EXAMPLE_TDM_DOUT_IO2    GPIO_NUM_8      // I2S data out io number
+    #define EXAMPLE_TDM_DIN_IO2     GPIO_NUM_9      // I2S data in io number
+#endif
+
+#define EXAMPLE_BUFF_SIZE               2048
+
+static i2s_chan_handle_t                tx_chan;        // I2S tx channel handler
+static i2s_chan_handle_t                rx_chan;        // I2S rx channel handler
+
+static void i2s_example_read_task(void *args)
+{
+    uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(r_buf); // Check if r_buf allocation success
+    size_t r_bytes = 0;
+    while (1) {
+        /* Read i2s data */
+        if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
+            printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
+            printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
+                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
+        } else {
+            printf("Read Task: i2s read failed\n");
+        }
+        vTaskDelay(pdMS_TO_TICKS(200));
+    }
+    free(r_buf);
+    vTaskDelete(NULL);
+}
+
+static void i2s_example_write_task(void *args)
+{
+    uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
+    assert(w_buf); // Check if w_buf allocation success
+
+    /* Assign w_buf */
+    for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
+        w_buf[i]     = 0x12;
+        w_buf[i + 1] = 0x34;
+        w_buf[i + 2] = 0x56;
+        w_buf[i + 3] = 0x78;
+        w_buf[i + 4] = 0x9A;
+        w_buf[i + 5] = 0xBC;
+        w_buf[i + 6] = 0xDE;
+        w_buf[i + 7] = 0xF0;
+    }
+
+    size_t w_bytes = 0;
+    while (1) {
+        /* Write i2s data */
+        if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
+            printf("Write Task: i2s write %d bytes\n", w_bytes);
+        } else {
+            printf("Write Task: i2s write failed\n");
+        }
+        vTaskDelay(pdMS_TO_TICKS(200));
+    }
+    free(w_buf);
+    vTaskDelete(NULL);
+}
+
+#if EXAMPLE_I2S_DUPLEX_MODE
+static void i2s_example_init_tdm_duplex(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate both channels
+     * The default configuration can be generated by the helper macro,
+     * it only requires the I2S controller id and I2S role */
+    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
+
+    /* Step 2: Setting the configurations of TDM mode, and initialize rx & tx channels
+     * The slot configuration and clock configuration can be generated by the macros
+     * These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode.
+     * They can help to specify the slot and clock configurations for initialization or re-configuring */
+    i2s_tdm_config_t tdm_cfg = {
+        .clk_cfg  = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
+        /* Limited by the hardware, the number of bit clock can't exceed 128 in one frame,
+         * which is to say, TDM mode can only support 32 bit-width data upto 4 slots,
+         * 16 bit-width data upto 8 slots and 8 bit-width data upto 16 slots */
+        .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
+                                                    I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_TDM_BCLK_IO1,
+            .ws   = EXAMPLE_TDM_WS_IO1,
+            .dout = EXAMPLE_TDM_DOUT_IO1,
+            .din  = EXAMPLE_TDM_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    /* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
+     * The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
+     * Modify the mclk_multiple to 512 directly here to avoid the warning */
+    tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
+
+    /* Initialize the channels */
+    ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg));
+    ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg));
+}
+
+#else
+
+static void i2s_example_init_tdm_simplex(void)
+{
+    /* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
+     * The default configuration can be generated by the helper macro,
+     * it only requires the I2S controller id and I2S role */
+    i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
+    i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
+    ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
+
+    /* Step 2: Setting the configurations of TDM mode and initialize each channels one by one
+     * The slot configuration and clock configuration can be generated by the macros
+     * These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode.
+     * They can help to specify the slot and clock configurations for initialization or re-configuring */
+    i2s_tdm_config_t tx_tdm_cfg = {
+        .clk_cfg  = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
+        .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
+                                                    I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_TDM_BCLK_IO1,
+            .ws   = EXAMPLE_TDM_WS_IO1,
+            .dout = EXAMPLE_TDM_DOUT_IO1,
+            .din  = EXAMPLE_TDM_DIN_IO1,
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    tx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
+    ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tx_tdm_cfg));
+
+    i2s_tdm_config_t rx_tdm_cfg = {
+        .clk_cfg  = I2S_TDM_CLK_DEFAULT_CONFIG(44100),
+        .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO,
+                                                    I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 |
+                                                    I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7),
+        .gpio_cfg = {
+            .mclk = I2S_GPIO_UNUSED,    // some codecs may require mclk signal, this example doesn't need it
+            .bclk = EXAMPLE_TDM_BCLK_IO2,
+            .ws   = EXAMPLE_TDM_WS_IO2,
+            .dout = EXAMPLE_TDM_DOUT_IO2,
+            .din  = EXAMPLE_TDM_DIN_IO2,
+            .invert_flags = {
+                .mclk_inv = false,
+                .bclk_inv = false,
+                .ws_inv   = false,
+            },
+        },
+    };
+    /* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
+     * The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
+     * Modify the mclk_multiple to 512 directly here to avoid the warning */
+    rx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
+    ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &rx_tdm_cfg));
+}
+#endif
+
+void app_main(void)
+{
+#if EXAMPLE_I2S_DUPLEX_MODE
+    i2s_example_init_tdm_duplex();
+#else
+    i2s_example_init_tdm_simplex();
+#endif
+
+    /* Step 3: Enable the tx and rx channels before writing or reading data */
+    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
+    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
+
+    /* Step 4: Create writing and reading task */
+    xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
+    xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL);
+}

+ 33 - 0
examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py

@@ -0,0 +1,33 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: CC0-1.0
+
+import pytest
+from pytest_embedded import Dut
+
+
+@pytest.mark.esp32s3
+@pytest.mark.esp32c3
+@pytest.mark.generic
+def test_i2s_tdm_example(dut: Dut) -> None:
+
+    dut.expect(r'D \(([0-9]+)\) i2s_common: tx channel is registered on I2S0 successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: rx channel is registered on I2S0 successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
+               r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_tdm: Clock division info: \[sclk\] ([0-9]+) Hz '
+               r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_tdm: The tx channel on I2S0 has been initialized to TDM mode successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
+               r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_tdm: Clock division info: \[sclk\] ([0-9]+) Hz '
+               r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_tdm: The rx channel on I2S0 has been initialized to TDM mode successfully', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5)
+    dut.expect(r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled', timeout=5)
+    dut.expect(r'Write Task: i2s write ([0-9]+) bytes', timeout=5)
+    dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5)
+    dut.expect(r'-----------------------------------', timeout=5)
+    dut.expect(r'\[0\] 0 \[1\] 0 \[2\] 0 \[3\] 0', timeout=5)
+    dut.expect(r'\[4\] 0 \[5\] 0 \[6\] 0 \[7\] 0', timeout=5)
+    dut.expect(r'\[0\] 12 \[1\] 34 \[2\] 56 \[3\] 78', timeout=10)
+    dut.expect(r'\[4\] 9a \[5\] bc \[6\] de \[7\] f0', timeout=10)

+ 1 - 0
examples/peripherals/i2s/i2s_basic/i2s_tdm/sdkconfig.defaults

@@ -0,0 +1 @@
+CONFIG_I2S_ENABLE_DEBUG_LOG=y

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

@@ -1,2 +0,0 @@
-idf_component_register(SRCS "i2s_example_main.c"
-                    INCLUDE_DIRS ".")

+ 0 - 180
examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c

@@ -1,180 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Unlicense OR CC0-1.0
- */
-/*  I2S Example
- *  This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
- *  Every 5 seconds, it will change bits_per_sample [16, 24, 32] for i2s data
- */
-#include <stdio.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/queue.h"
-#include "freertos/task.h"
-#include "driver/i2s_std.h"
-#include "driver/gpio.h"
-#include "esp_system.h"
-#include "esp_log.h"
-#include "esp_attr.h"
-#include <math.h>
-
-#define EXAMPLE_SAMPLE_RATE         (36000)
-#define EXAMPLE_DATA_BIT_WIDTH      (I2S_DATA_BIT_WIDTH_16BIT)
-
-#define I2S_NUM         (0)
-#define WAVE_FREQ_HZ    (100)
-#define PI              (3.14159265)
-#define I2S_BCK_IO      (GPIO_NUM_4)
-#define I2S_WS_IO       (GPIO_NUM_5)
-#define I2S_DO_IO       (GPIO_NUM_18)
-#define I2S_DI_IO       (GPIO_NUM_18) /// Loopback internally if data_out and data_in signal are bound to a same GPIO
-
-#define SAMPLE_PER_CYCLE (EXAMPLE_SAMPLE_RATE/WAVE_FREQ_HZ)
-
-static i2s_chan_handle_t tx_handle = NULL;
-static i2s_chan_handle_t rx_handle = NULL;
-
-static volatile int is_overflow = 0;
-
-static uint32_t* example_generate_triangle_sine_waves(int bits, uint32_t *buf_len)
-{
-    uint32_t len = ((bits + 8) / 16)*SAMPLE_PER_CYCLE * 4;
-    uint32_t *samples_data = malloc(len);
-
-    double triangle_float = -(pow(2, bits) / 2 - 1);
-    double triangle_step = (double) pow(2, bits) / SAMPLE_PER_CYCLE;
-
-    for (int i = 0; i < SAMPLE_PER_CYCLE; i++) {
-        double sin_float = sin(i * 2 * PI / SAMPLE_PER_CYCLE);
-        if (sin_float >= 0) {
-            triangle_float += triangle_step;
-        } else {
-            triangle_float -= triangle_step;
-        }
-        sin_float *= (pow(2, bits) / 2 - 1);
-        if (bits == 16) {
-            samples_data[i] = ((short)triangle_float << 16) | (short)sin_float;
-        } else if (bits == 24) { //1-bytes unused
-            samples_data[i * 2] = ((int) triangle_float) << 8;
-            samples_data[i * 2 + 1] = ((int) sin_float) << 8;
-        } else {
-            samples_data[i * 2] = ((int) triangle_float);
-            samples_data[i * 2 + 1] = ((int) sin_float);
-        }
-
-    }
-    *buf_len = len;
-    return samples_data;
-}
-
-static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
-{
-    is_overflow++;
-    return false;
-}
-
-static void example_i2s_read_task(void * args)
-{
-    uint32_t *rx_buf = calloc(1, 8192);
-    size_t bytes_read = 0;
-    uint32_t cnt = 0;
-
-    while (1) {
-        if (i2s_channel_read(rx_handle, rx_buf, 8192, &bytes_read, 1000) == ESP_OK) {
-            if (cnt == 0) {
-                printf("\n[i2s read] %d bytes are read successfully\n", bytes_read);
-                printf("----------------------------------------------\n");
-                printf("[0] %4x [1] %4x [2] %4x [3] %4x\n\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
-            }
-            cnt++;
-            cnt %= 10;
-            /* If the polling time is too long, there will be data dropped event */
-            // vTaskDelay(10);
-        } else {
-            printf("[i2s read] %d bytes are read, timeout triggered\n\n", bytes_read);
-        }
-    }
-    vTaskDelete(NULL);
-}
-
-static void example_i2s_write_task(void * args)
-{
-    uint32_t buf_len = 0;
-    uint32_t *tx_buf =  example_generate_triangle_sine_waves(EXAMPLE_DATA_BIT_WIDTH, &buf_len);
-    size_t bytes_written = 0;
-    uint32_t cnt = 0;
-
-    while (1) {
-        if (i2s_channel_write(tx_handle, tx_buf, buf_len, &bytes_written, 1000) == ESP_OK) {
-            if (cnt == 0) {
-                printf("[i2s write] %d bytes are written successfully\n", bytes_written);
-            }
-            cnt++;
-            cnt %= 20;
-        } else {
-            printf("[i2s write] %d bytes are written, timeout triggered\n", bytes_written);
-        }
-    }
-    vTaskDelete(NULL);
-}
-
-static void example_i2s_init_std_duplex(void)
-{
-    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
-    /* Giving both tx and rx handle will make the i2s works in full-duplex mode and can share the bclk and ws signal */
-    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
-    i2s_std_config_t std_cfg = {
-        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
-        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(EXAMPLE_DATA_BIT_WIDTH, I2S_SLOT_MODE_STEREO),
-        .gpio_cfg = {
-            .mclk = I2S_GPIO_UNUSED,
-            .bclk = I2S_BCK_IO,
-            .ws   = I2S_WS_IO,
-            .dout = I2S_DO_IO,
-            .din  = I2S_DI_IO,
-            .invert_flags = {
-                .mclk_inv = false,
-                .bclk_inv = false,
-                .ws_inv = false,
-            },
-        },
-    };
-#if SOC_I2S_SUPPORTS_APLL
-    // APLL clock is more accurate when sample rate is high
-    std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
-#endif
-    /* Initialize the tx channel handle to standard mode */
-    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
-    /* Initialize the rx channel handle to standard mode */
-    ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
-
-    printf("I2S tx and rx channels have been initialized to standard duplex mode\n");
-
-    i2s_event_callbacks_t cbs = {
-        .on_recv = NULL,
-        .on_recv_q_ovf = i2s_rx_queue_overflow_callback,
-        .on_sent = NULL,
-        .on_send_q_ovf = NULL,
-    };
-    ESP_ERROR_CHECK(i2s_channel_register_event_callback(rx_handle, &cbs, NULL));
-
-    ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
-    ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
-    printf("I2S tx and rx channels enabled\n");
-}
-
-void app_main(void)
-{
-    //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
-    example_i2s_init_std_duplex();
-    xTaskCreate(example_i2s_write_task, "i2s write task", 4096, NULL, 5, NULL);
-    xTaskCreate(example_i2s_read_task, "i2s read task", 8192, NULL, 5, NULL);
-
-    while (1) {
-        if (is_overflow > 0) {
-            printf("[i2s monitor] RX message Queue overflowed\n");
-            is_overflow--;
-        }
-        vTaskDelay(1);
-    }
-}

+ 0 - 18
examples/peripherals/i2s/i2s_basic/pytest_i2s_basic.py

@@ -1,18 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
-# SPDX-License-Identifier: CC0-1.0
-
-import pytest
-from pytest_embedded import Dut
-
-
-@pytest.mark.esp32
-@pytest.mark.esp32s2
-@pytest.mark.esp32s3
-@pytest.mark.esp32c3
-@pytest.mark.generic
-def test_i2s_basic_example(dut: Dut) -> None:
-    dut.expect_exact('I2S tx and rx channels have been initialized to standard duplex mode', timeout=30)
-    dut.expect_exact('I2S tx and rx channels enabled', timeout=30)
-    dut.expect_exact('[i2s write] 1440 bytes are written successfully', timeout=30)
-    dut.expect_exact('', timeout=30)
-    dut.expect_exact('[i2s read] 8192 bytes are read successfully', timeout=30)

+ 0 - 0
examples/peripherals/i2s/i2s_es8311/CMakeLists.txt → examples/peripherals/i2s/i2s_codec/i2s_es8311/CMakeLists.txt


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/README.md → examples/peripherals/i2s/i2s_codec/i2s_es8311/README.md


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/main/CMakeLists.txt → examples/peripherals/i2s/i2s_codec/i2s_es8311/main/CMakeLists.txt


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/main/Kconfig.projbuild → examples/peripherals/i2s/i2s_codec/i2s_es8311/main/Kconfig.projbuild


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/main/canon.pcm → examples/peripherals/i2s/i2s_codec/i2s_es8311/main/canon.pcm


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/main/i2s_es8311_example.c → examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/main/idf_component.yml → examples/peripherals/i2s/i2s_codec/i2s_es8311/main/idf_component.yml


+ 0 - 0
examples/peripherals/i2s/i2s_es8311/pytest_i2s_es8311.py → examples/peripherals/i2s/i2s_codec/i2s_es8311/pytest_i2s_es8311.py


+ 0 - 0
examples/peripherals/i2s/i2s_audio_recorder_sdcard/CMakeLists.txt → examples/peripherals/i2s/i2s_recorder/CMakeLists.txt


+ 0 - 0
examples/peripherals/i2s/i2s_audio_recorder_sdcard/README.md → examples/peripherals/i2s/i2s_recorder/README.md


+ 0 - 0
examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/CMakeLists.txt → examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt


+ 0 - 0
examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/Kconfig.projbuild → examples/peripherals/i2s/i2s_recorder/main/Kconfig.projbuild


+ 9 - 4
examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c → examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c

@@ -145,10 +145,14 @@ void record_wav(uint32_t rec_time)
     // Start recording
     while (flash_wr_size < flash_rec_time) {
         // Read the RAW samples from the microphone
-        i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000);
-        // Write the samples to the WAV file
-        fwrite(i2s_readraw_buff, 1, bytes_read, f);
-        flash_wr_size += bytes_read;
+        if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000) == ESP_OK) {
+            printf("[0] %d [1] %d [2] %d [3]%d ...\n", i2s_readraw_buff[0], i2s_readraw_buff[1], i2s_readraw_buff[2], i2s_readraw_buff[3]);
+            // Write the samples to the WAV file
+            fwrite(i2s_readraw_buff, 1, bytes_read, f);
+            flash_wr_size += bytes_read;
+        } else {
+            printf("Read Failed!\n");
+        }
     }
 
     ESP_LOGI(TAG, "Recording done!");
@@ -169,6 +173,7 @@ void init_microphone(void)
 
     i2s_pdm_rx_config_t pdm_rx_cfg = {
         .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(CONFIG_EXAMPLE_SAMPLE_RATE),
+        /* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */
         .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
         .gpio_cfg = {
             .clk = CONFIG_EXAMPLE_I2S_CLK_GPIO,

+ 0 - 0
examples/peripherals/i2s/i2s_audio_recorder_sdcard/pytest_i2s_record.py → examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py


+ 0 - 1
tools/ci/check_copyright_ignore.txt

@@ -1078,7 +1078,6 @@ components/soc/esp32c3/include/soc/extmem_reg.h
 components/soc/esp32c3/include/soc/fe_reg.h
 components/soc/esp32c3/include/soc/gpio_pins.h
 components/soc/esp32c3/include/soc/gpio_reg.h
-components/soc/esp32c3/include/soc/gpio_sig_map.h
 components/soc/esp32c3/include/soc/gpio_struct.h
 components/soc/esp32c3/include/soc/hwcrypto_reg.h
 components/soc/esp32c3/include/soc/i2c_reg.h