diff --git a/components/driver/deprecated/driver/i2s.h b/components/driver/deprecated/driver/i2s.h index 0d9c686acb..07893c3e66 100644 --- a/components/driver/deprecated/driver/i2s.h +++ b/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 diff --git a/components/driver/deprecated/i2s_legacy.c b/components/driver/deprecated/i2s_legacy.c index 798ef011ba..59a7e119c9 100644 --- a/components/driver/deprecated/i2s_legacy.c +++ b/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 */ diff --git a/components/driver/i2s/i2s_common.c b/components/driver/i2s/i2s_common.c index fb5c29bb18..880ad35bab 100644 --- a/components/driver/i2s/i2s_common.c +++ b/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; } diff --git a/components/driver/i2s/i2s_pdm.c b/components/driver/i2s/i2s_pdm.c index 500f822ed0..b8e4d3300b 100644 --- a/components/driver/i2s/i2s_pdm.c +++ b/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: diff --git a/components/driver/i2s/i2s_std.c b/components/driver/i2s/i2s_std.c index f60f8a852c..1acafed8c0 100644 --- a/components/driver/i2s/i2s_std.c +++ b/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: diff --git a/components/driver/i2s/i2s_tdm.c b/components/driver/i2s/i2s_tdm.c index ce7a12522d..01fb79c117 100644 --- a/components/driver/i2s/i2s_tdm.c +++ b/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: diff --git a/components/driver/include/driver/i2s_common.h b/components/driver/include/driver/i2s_common.h index f68f54814c..f1f06ff825 100644 --- a/components/driver/include/driver/i2s_common.h +++ b/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, \ } diff --git a/components/driver/include/driver/i2s_pdm.h b/components/driver/include/driver/i2s_pdm.h index c07ddeb545..2ca0861dc3 100644 --- a/components/driver/include/driver/i2s_pdm.h +++ b/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 */ diff --git a/components/driver/include/driver/i2s_types.h b/components/driver/include/driver/i2s_types.h index 9709d91c71..84cd39241b 100644 --- a/components/driver/include/driver/i2s_types.h +++ b/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; /** diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index db8321746c..de35fddfbd 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/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 diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index d87c2b8f73..50a9fc5b10 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/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 diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index e5ebe9eaa8..193bea171f 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/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 diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index 6cadd8f591..89e26db79c 100644 --- a/components/hal/i2s_hal.c +++ b/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); diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index 71e5827fc7..4e80261dc1 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/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 }; diff --git a/components/hal/include/hal/i2s_types.h b/components/hal/include/hal/i2s_types.h index 272ae826ec..30767a7282 100644 --- a/components/hal/include/hal/i2s_types.h +++ b/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 diff --git a/components/soc/esp32/i2s_periph.c b/components/soc/esp32/i2s_periph.c index d2c0c227a1..a96802b6ee 100644 --- a/components/soc/esp32/i2s_periph.c +++ b/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, diff --git a/components/soc/esp32c3/i2s_periph.c b/components/soc/esp32c3/i2s_periph.c index daa8057277..44dd02d334 100644 --- a/components/soc/esp32c3/i2s_periph.c +++ b/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, diff --git a/components/soc/esp32c3/include/soc/gpio_sig_map.h b/components/soc/esp32c3/include/soc/gpio_sig_map.h index 61aa95a3c4..badcefdc09 100644 --- a/components/soc/esp32c3/include/soc/gpio_sig_map.h +++ b/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 diff --git a/components/soc/esp32h2/i2s_periph.c b/components/soc/esp32h2/i2s_periph.c index bf258634ed..08a85b70d0 100644 --- a/components/soc/esp32h2/i2s_periph.c +++ b/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, } diff --git a/components/soc/esp32s2/i2s_periph.c b/components/soc/esp32s2/i2s_periph.c index 2bee28695a..fd01440c65 100644 --- a/components/soc/esp32s2/i2s_periph.c +++ b/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, diff --git a/components/soc/esp32s3/i2s_periph.c b/components/soc/esp32s3/i2s_periph.c index 5fe15212c5..1f3715a9e7 100644 --- a/components/soc/esp32s3/i2s_periph.c +++ b/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, diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index 4f868c99b9..35522f385e 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/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; diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index 975a7dcf5d..033ac45a3a 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/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, diff --git a/docs/en/migration-guides/release-5.x/peripherals.rst b/docs/en/migration-guides/release-5.x/peripherals.rst index dd3c47293a..fd980f58f3 100644 --- a/docs/en/migration-guides/release-5.x/peripherals.rst +++ b/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 diff --git a/examples/peripherals/i2s/i2s_basic/README.md b/examples/peripherals/i2s/i2s_basic/README.md deleted file mode 100644 index 598482caba..0000000000 --- a/examples/peripherals/i2s/i2s_basic/README.md +++ /dev/null @@ -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. diff --git a/examples/peripherals/i2s/i2s_basic/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_pdm/CMakeLists.txt similarity index 86% rename from examples/peripherals/i2s/i2s_basic/CMakeLists.txt rename to examples/peripherals/i2s/i2s_basic/i2s_pdm/CMakeLists.txt index 4958e595d6..d36bacbe7b 100644 --- a/examples/peripherals/i2s/i2s_basic/CMakeLists.txt +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md b/examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md new file mode 100644 index 0000000000..4d32f821ef --- /dev/null +++ b/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. diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/CMakeLists.txt new file mode 100644 index 0000000000..e1bdf7c96a --- /dev/null +++ b/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 ".") diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example.h b/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example.h new file mode 100644 index 0000000000..e8251153b8 --- /dev/null +++ b/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); diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example_main.c b/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_example_main.c new file mode 100644 index 0000000000..1fd7c0e529 --- /dev/null +++ b/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 +#include +#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 +} diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c b/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c new file mode 100644 index 0000000000..adde3cedd1 --- /dev/null +++ b/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 +#include +#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); +} diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c b/examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c new file mode 100644 index 0000000000..390e89dc01 --- /dev/null +++ b/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 +#include +#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); +} diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py b/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py new file mode 100644 index 0000000000..a23c37fa88 --- /dev/null +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/sdkconfig.defaults b/examples/peripherals/i2s/i2s_basic/i2s_pdm/sdkconfig.defaults new file mode 100644 index 0000000000..5b3efe27a4 --- /dev/null +++ b/examples/peripherals/i2s/i2s_basic/i2s_pdm/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_I2S_ENABLE_DEBUG_LOG=y diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_std/CMakeLists.txt new file mode 100644 index 0000000000..3a5fb14727 --- /dev/null +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/README.md b/examples/peripherals/i2s/i2s_basic/i2s_std/README.md new file mode 100644 index 0000000000..fc8dc48b7b --- /dev/null +++ b/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. diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_std/main/CMakeLists.txt new file mode 100644 index 0000000000..6ae8228b9d --- /dev/null +++ b/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 ".") diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c b/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c new file mode 100644 index 0000000000..f759b2740e --- /dev/null +++ b/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 +#include +#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); +} diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py b/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py new file mode 100644 index 0000000000..18018f11d8 --- /dev/null +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/sdkconfig.defaults b/examples/peripherals/i2s/i2s_basic/i2s_std/sdkconfig.defaults new file mode 100644 index 0000000000..5b3efe27a4 --- /dev/null +++ b/examples/peripherals/i2s/i2s_basic/i2s_std/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_I2S_ENABLE_DEBUG_LOG=y diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_tdm/CMakeLists.txt new file mode 100644 index 0000000000..6158d496e1 --- /dev/null +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md b/examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md new file mode 100644 index 0000000000..a51e0e4d26 --- /dev/null +++ b/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. + diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/CMakeLists.txt new file mode 100644 index 0000000000..90a398149f --- /dev/null +++ b/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 ".") diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c b/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c new file mode 100644 index 0000000000..c0c3536609 --- /dev/null +++ b/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 +#include +#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); +} diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py b/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py new file mode 100644 index 0000000000..afef7bdf76 --- /dev/null +++ b/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) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/sdkconfig.defaults b/examples/peripherals/i2s/i2s_basic/i2s_tdm/sdkconfig.defaults new file mode 100644 index 0000000000..5b3efe27a4 --- /dev/null +++ b/examples/peripherals/i2s/i2s_basic/i2s_tdm/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_I2S_ENABLE_DEBUG_LOG=y diff --git a/examples/peripherals/i2s/i2s_basic/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_basic/main/CMakeLists.txt deleted file mode 100644 index 4c583513cd..0000000000 --- a/examples/peripherals/i2s/i2s_basic/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "i2s_example_main.c" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c b/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c deleted file mode 100644 index 414bcbe03e..0000000000 --- a/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c +++ /dev/null @@ -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 -#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 - -#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); - } -} diff --git a/examples/peripherals/i2s/i2s_basic/pytest_i2s_basic.py b/examples/peripherals/i2s/i2s_basic/pytest_i2s_basic.py deleted file mode 100644 index 691aa7f186..0000000000 --- a/examples/peripherals/i2s/i2s_basic/pytest_i2s_basic.py +++ /dev/null @@ -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) diff --git a/examples/peripherals/i2s/i2s_es8311/CMakeLists.txt b/examples/peripherals/i2s/i2s_codec/i2s_es8311/CMakeLists.txt similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/CMakeLists.txt rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/CMakeLists.txt diff --git a/examples/peripherals/i2s/i2s_es8311/README.md b/examples/peripherals/i2s/i2s_codec/i2s_es8311/README.md similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/README.md rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/README.md diff --git a/examples/peripherals/i2s/i2s_es8311/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/main/CMakeLists.txt rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/main/CMakeLists.txt diff --git a/examples/peripherals/i2s/i2s_es8311/main/Kconfig.projbuild b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/Kconfig.projbuild similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/main/Kconfig.projbuild rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/main/Kconfig.projbuild diff --git a/examples/peripherals/i2s/i2s_es8311/main/canon.pcm b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/canon.pcm similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/main/canon.pcm rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/main/canon.pcm diff --git a/examples/peripherals/i2s/i2s_es8311/main/i2s_es8311_example.c b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/main/i2s_es8311_example.c rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c diff --git a/examples/peripherals/i2s/i2s_es8311/main/idf_component.yml b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/idf_component.yml similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/main/idf_component.yml rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/main/idf_component.yml diff --git a/examples/peripherals/i2s/i2s_es8311/pytest_i2s_es8311.py b/examples/peripherals/i2s/i2s_codec/i2s_es8311/pytest_i2s_es8311.py similarity index 100% rename from examples/peripherals/i2s/i2s_es8311/pytest_i2s_es8311.py rename to examples/peripherals/i2s/i2s_codec/i2s_es8311/pytest_i2s_es8311.py diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/CMakeLists.txt b/examples/peripherals/i2s/i2s_recorder/CMakeLists.txt similarity index 100% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/CMakeLists.txt rename to examples/peripherals/i2s/i2s_recorder/CMakeLists.txt diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/README.md b/examples/peripherals/i2s/i2s_recorder/README.md similarity index 100% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/README.md rename to examples/peripherals/i2s/i2s_recorder/README.md diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/CMakeLists.txt rename to examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/Kconfig.projbuild b/examples/peripherals/i2s/i2s_recorder/main/Kconfig.projbuild similarity index 100% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/Kconfig.projbuild rename to examples/peripherals/i2s/i2s_recorder/main/Kconfig.projbuild diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c b/examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c similarity index 92% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c rename to examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c index f0c7c46d4f..dbb17482f5 100644 --- a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c +++ b/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, diff --git a/examples/peripherals/i2s/i2s_audio_recorder_sdcard/pytest_i2s_record.py b/examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py similarity index 100% rename from examples/peripherals/i2s/i2s_audio_recorder_sdcard/pytest_i2s_record.py rename to examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index d5bb308fdd..22861fa4e2 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/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