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

This commit is contained in:
laokaiyao 2022-06-20 20:30:31 +08:00
parent 92b4e134bb
commit 90866e99fb
64 changed files with 1616 additions and 409 deletions

View File

@ -25,7 +25,7 @@
#if !CONFIG_I2S_SUPPRESS_DEPRECATE_WARN #if !CONFIG_I2S_SUPPRESS_DEPRECATE_WARN
#warning "This set of I2S APIs has been deprecated, \ #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, \ 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" you can enable 'Suppress leagcy driver deprecated warning' option under 'I2S Configuration' menu in Kconfig"
#endif #endif

View File

@ -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).lp_scale = I2S_PDM_SIG_SCALING_MUL_1;
SLOT_CFG(pdm_tx).sinc_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 #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_en = true;
SLOT_CFG(pdm_tx).hp_cut_off_freq_hz = 49; SLOT_CFG(pdm_tx).hp_cut_off_freq_hz = 49;
SLOT_CFG(pdm_tx).sd_dither = 0; 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 #endif // SOC_I2S_HW_VERSION_2
/* Generate PDM TX clock configuration */ /* Generate PDM TX clock configuration */

View File

@ -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) { if (handle->dir == I2S_DIR_RX) {
i2s_ll_rx_set_eof_num(handle->controller->hal.dev, bufsize); 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; return ESP_OK;
err: err:
i2s_free_dma_desc(handle); 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); portEXIT_CRITICAL(&g_i2s.spinlock);
if (occupied_comp != NULL) { 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; return ret;
} }

View File

@ -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); portENTER_CRITICAL(&g_i2s.spinlock);
/* Set clock configurations in HAL*/ /* Set clock configurations in HAL*/
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); 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); portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: clock configuration */ /* 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_ERR_INVALID_ARG, TAG, "clk gpio is invalid");
ESP_RETURN_ON_FALSE((gpio_cfg->dout == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->dout)), ESP_RETURN_ON_FALSE((gpio_cfg->dout == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->dout)),
ESP_ERR_INVALID_ARG, TAG, "dout gpio is invalid"); 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 */ /* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false); 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) { if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */ /* 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); i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
#endif #endif
/* Update the mode info: gpio configuration */ /* 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)); memcpy(&(pdm_tx_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_pdm_tx_gpio_config_t));
return ESP_OK; 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 */ /* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY; handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex); xSemaphoreGive(handle->mutex);
ESP_LOGD(TAG, "The tx channel on I2S0 has been initialized to PDM TX mode successfully");
return ret; return ret;
err: 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 */ /* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY; handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex); xSemaphoreGive(handle->mutex);
ESP_LOGD(TAG, "The rx channel on I2S0 has been initialized to PDM RX mode successfully");
return ret; return ret;
err: err:

View File

@ -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 */ /* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY; handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex); 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; return ret;
err: err:

View File

@ -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->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = rate * clk_cfg->mclk_multiple; clk_info->mclk = rate * clk_cfg->mclk_multiple;
clk_info->bclk_div = clk_info->mclk / clk_info->bclk; 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 { } else {
/* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */ /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
clk_info->bclk_div = 8; 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 */ /* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY; handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex); 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; return ret;
err: err:

View File

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

View File

@ -32,6 +32,8 @@ extern "C" {
.data_bit_width = bits_per_sample, \ .data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \ .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \ .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_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_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 */
/* Particular fields */
i2s_pdm_slot_mask_t slot_mask; /*!< Choose the slots to activate */
} i2s_pdm_rx_slot_config_t; } 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, \ .slot_mode = mono_or_stereo, \
.sd_prescale = 0, \ .sd_prescale = 0, \
.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \ .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, \ .lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.sinc_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_en = true, \
.hp_cut_off_freq_hz = 49, \ .hp_cut_off_freq_hz = 35.5, \
.sd_dither = 0, \ .sd_dither = 0, \
.sd_dither2 = 0, \ .sd_dither2 = 1, \
} }
#else #else // SOC_I2S_HW_VERSION_2
/** /**
* @brief PDM style in 2 slots(TX) * @brief PDM style in 2 slots(TX)
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode * @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, \ .clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \ .mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.up_sample_fp = 960, \ .up_sample_fp = 960, \
.up_sample_fs = ((rate) / 100), \ .up_sample_fs = 480, \
} }
/* /*
@ -234,7 +238,10 @@ typedef struct {
/* General fields */ /* 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_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_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 */ /* Particular fields */
uint32_t sd_prescale; /*!< Sigma-delta filter prescale */ uint32_t sd_prescale; /*!< Sigma-delta filter prescale */
i2s_pdm_sig_scale_t sd_scale; /*!< Sigma-delta filter scaling value */ 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 lp_scale; /*!< Low pass filter scaling value */
i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */ i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */
#if SOC_I2S_HW_VERSION_2 #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 */ 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 */ 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_dither; /*!< Sigma-delta filter dither */
@ -269,6 +276,11 @@ typedef struct {
typedef struct { typedef struct {
gpio_num_t clk; /*!< PDM clk pin, output */ gpio_num_t clk; /*!< PDM clk pin, output */
gpio_num_t dout; /*!< DATA 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 { struct {
uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */ uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */
} invert_flags; /*!< GPIO pin invert flags */ } invert_flags; /*!< GPIO pin invert flags */

View File

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

View File

@ -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; 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 * @brief Configure I2S TX module clock divider
* *
@ -272,7 +306,7 @@ finish:
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
} else { } else {
hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; 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; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
} else { } else {
hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; 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 * @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; 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 * @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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -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; 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 * @brief Configure I2S TX module clock divider
* *
@ -274,7 +308,7 @@ finish:
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
} else { } else {
hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; 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; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
} else { } else {
hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; 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 * @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; 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 * @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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -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; 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 * @brief Configure I2S TX module clock divider
* *
@ -275,7 +309,7 @@ finish:
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
} else { } else {
hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; 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; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
} else { } else {
hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; 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_z = numerator;
hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; 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 * @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; 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 * @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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -124,12 +124,10 @@ void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal)
#if SOC_I2S_SUPPORTS_PDM_TX #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) 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 ? bool is_mono = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO;
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
i2s_ll_tx_reset(hal->dev); i2s_ll_tx_reset(hal->dev);
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave 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_msb_shift(hal->dev, false);
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->pdm_tx.sd_prescale); 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); 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); i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->pdm_tx.sd_scale);
#if SOC_I2S_HW_VERSION_1 #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_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 #elif SOC_I2S_HW_VERSION_2
/* Still need to enable the first 2 TDM channel mask to get the correct number of frame */ /* PDM TX line mode */
i2s_ll_tx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1); i2s_ll_tx_pdm_line_mode(hal->dev, slot_cfg->pdm_tx.line_mode);
i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_tx.hp_en); /* 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; uint8_t cnt = 0;
float min = 1000; float min = 1000;
float expt_cut_off = slot_cfg->pdm_tx.hp_cut_off_freq_hz; 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; 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_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_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_dither(hal->dev, slot_cfg->pdm_tx.sd_dither);
i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->pdm_tx.sd_dither2); i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->pdm_tx.sd_dither2);
#endif #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_reset(hal->dev);
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave 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_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 #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); i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2 #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_enable_mono_mode(hal->dev, false);
i2s_ll_rx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1); /* Set the channel mask to enable corresponding slots */
i2s_ll_rx_set_active_chan_mask(hal->dev, slot_cfg->pdm_rx.slot_mask);
#endif #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_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_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_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_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_left_align(hal->dev, slot_cfg->tdm.left_align);
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian); 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; slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
uint32_t cnt; uint32_t cnt;
uint32_t msk = slot_cfg->tdm.slot_mask; uint32_t msk = slot_cfg->tdm.slot_mask;
for (cnt = 0; msk; cnt++, msk >>= 1);
/* Get the maximum slot number */ /* Get the maximum slot number */
cnt = 32 - __builtin_clz(msk); cnt = 32 - __builtin_clz(msk);
/* There should be at least 2 slots in total even for mono mode */ /* 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 */ /* 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_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_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_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_left_align(hal->dev, slot_cfg->tdm.left_align);
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian); i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);

View File

@ -73,13 +73,19 @@ typedef struct {
i2s_pdm_sig_scale_t lp_scale; /*!< Low pass filter scaling value */ i2s_pdm_sig_scale_t lp_scale; /*!< Low pass filter scaling value */
i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */ i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */
#if SOC_I2S_HW_VERSION_2 #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 */ 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 */ 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_dither; /*!< Sigma-delta filter dither */
uint32_t sd_dither2; /*!< Sigma-delta filter dither2 */ uint32_t sd_dither2; /*!< Sigma-delta filter dither2 */
#endif // SOC_I2S_HW_VERSION_2 #endif // SOC_I2S_HW_VERSION_2
} pdm_tx; /*!< Specific configurations for PDM TX mode */ } 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 #endif
}; };

View File

@ -101,6 +101,20 @@ typedef enum {
I2S_PDM_SIG_SCALING_MUL_2 = 2, /*!< I2S TX PDM signal scaling: x2 */ 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_SCALING_MUL_4 = 3, /*!< I2S TX PDM signal scaling: x4 */
} i2s_pdm_sig_scale_t; } 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 #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_LEFT_RIGHT = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
} i2s_std_slot_mask_t; } 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 #if SOC_I2S_SUPPORTS_TDM
/** /**
* @brief tdm slot number * @brief tdm slot number

View File

@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
.s_rx_ws_sig = I2S0I_WS_IN_IDX, .s_rx_ws_sig = I2S0I_WS_IN_IDX,
.data_out_sig = I2S0O_DATA_OUT23_IDX, .data_out_sig = I2S0O_DATA_OUT23_IDX,
.data_out1_sig = -1,
.data_in_sig = I2S0I_DATA_IN15_IDX, .data_in_sig = I2S0I_DATA_IN15_IDX,
.irq = ETS_I2S0_INTR_SOURCE, .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, .s_rx_ws_sig = I2S1I_WS_IN_IDX,
.data_out_sig = I2S1O_DATA_OUT23_IDX, .data_out_sig = I2S1O_DATA_OUT23_IDX,
.data_out1_sig = -1,
.data_in_sig = I2S1I_DATA_IN15_IDX, .data_in_sig = I2S1I_DATA_IN15_IDX,
.irq = ETS_I2S1_INTR_SOURCE, .irq = ETS_I2S1_INTR_SOURCE,

View File

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

View File

@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
#ifndef _SOC_GPIO_SIG_MAP_H_ #ifndef _SOC_GPIO_SIG_MAP_H_
#define _SOC_GPIO_SIG_MAP_H_ #define _SOC_GPIO_SIG_MAP_H_
@ -100,6 +92,7 @@
#define GPIO_SD1_OUT_IDX 56 #define GPIO_SD1_OUT_IDX 56
#define GPIO_SD2_OUT_IDX 57 #define GPIO_SD2_OUT_IDX 57
#define GPIO_SD3_OUT_IDX 58 #define GPIO_SD3_OUT_IDX 58
#define I2SO_SD1_OUT_IDX 59
#define FSPICLK_IN_IDX 63 #define FSPICLK_IN_IDX 63
#define FSPICLK_OUT_IDX 63 #define FSPICLK_OUT_IDX 63
#define FSPIQ_IN_IDX 64 #define FSPIQ_IN_IDX 64

View File

@ -12,18 +12,22 @@
*/ */
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { 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, .irq = ETS_I2S1_INTR_SOURCE,
.module = PERIPH_I2S1_MODULE, .module = PERIPH_I2S1_MODULE,
} }

View File

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

View File

@ -25,6 +25,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
.s_rx_ws_sig = I2S0I_WS_IN_IDX, .s_rx_ws_sig = I2S0I_WS_IN_IDX,
.data_out_sig = I2S0O_SD_OUT_IDX, .data_out_sig = I2S0O_SD_OUT_IDX,
.data_out1_sig = I2S0O_SD1_OUT_IDX,
.data_in_sig = I2S0I_SD_IN_IDX, .data_in_sig = I2S0I_SD_IN_IDX,
.irq = -1, .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, .s_rx_ws_sig = I2S1I_WS_IN_IDX,
.data_out_sig = I2S1O_SD_OUT_IDX, .data_out_sig = I2S1O_SD_OUT_IDX,
.data_out1_sig = -1,
.data_in_sig = I2S1I_SD_IN_IDX, .data_in_sig = I2S1I_SD_IN_IDX,
.irq = -1, .irq = -1,

View File

@ -32,6 +32,7 @@ typedef struct {
const uint8_t s_rx_ws_sig; const uint8_t s_rx_ws_sig;
const uint8_t data_out_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 data_in_sig;
const uint8_t irq; const uint8_t irq;

View File

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

View File

@ -277,9 +277,7 @@ LCD
I2S driver 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 :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:
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:
.. figure:: ../../../_static/diagrams/i2s/i2s_file_structure.png .. figure:: ../../../_static/diagrams/i2s/i2s_file_structure.png
:align: center :align: center

View File

@ -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.

View File

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

View File

@ -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.

View File

@ -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 ".")

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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)

View File

@ -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.

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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)

View File

@ -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.

View File

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

View File

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

View File

@ -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)

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -145,10 +145,14 @@ void record_wav(uint32_t rec_time)
// Start recording // Start recording
while (flash_wr_size < flash_rec_time) { while (flash_wr_size < flash_rec_time) {
// Read the RAW samples from the microphone // Read the RAW samples from the microphone
i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000); if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000) == ESP_OK) {
// Write the samples to the WAV file 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]);
fwrite(i2s_readraw_buff, 1, bytes_read, f); // Write the samples to the WAV file
flash_wr_size += bytes_read; fwrite(i2s_readraw_buff, 1, bytes_read, f);
flash_wr_size += bytes_read;
} else {
printf("Read Failed!\n");
}
} }
ESP_LOGI(TAG, "Recording done!"); ESP_LOGI(TAG, "Recording done!");
@ -169,6 +173,7 @@ void init_microphone(void)
i2s_pdm_rx_config_t pdm_rx_cfg = { i2s_pdm_rx_config_t pdm_rx_cfg = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(CONFIG_EXAMPLE_SAMPLE_RATE), .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), .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = { .gpio_cfg = {
.clk = CONFIG_EXAMPLE_I2S_CLK_GPIO, .clk = CONFIG_EXAMPLE_I2S_CLK_GPIO,

View File

@ -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/fe_reg.h
components/soc/esp32c3/include/soc/gpio_pins.h components/soc/esp32c3/include/soc/gpio_pins.h
components/soc/esp32c3/include/soc/gpio_reg.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/gpio_struct.h
components/soc/esp32c3/include/soc/hwcrypto_reg.h components/soc/esp32c3/include/soc/hwcrypto_reg.h
components/soc/esp32c3/include/soc/i2c_reg.h components/soc/esp32c3/include/soc/i2c_reg.h