mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
i2s: add basic examples for STD/TDM/PDM mode
This commit is contained in:
parent
92b4e134bb
commit
90866e99fb
@ -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
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
|
@ -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)
|
165
examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md
Normal file
165
examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md
Normal 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.
|
@ -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 ".")
|
@ -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);
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "i2s_pdm_example.h"
|
||||||
|
|
||||||
|
#define EXAMPLE_PDM_TX 0
|
||||||
|
/* ESP32-C3 does not support PDM RX currently */
|
||||||
|
#if !CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define EXAMPLE_PDM_RX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXAMPLE_PDM_DIR EXAMPLE_PDM_TX
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
#if EXAMPLE_PDM_DIR == EXAMPLE_PDM_TX
|
||||||
|
printf("I2S PDM TX example start\n---------------------------\n");
|
||||||
|
xTaskCreate(i2s_example_pdm_tx_task, "i2s_example_pdm_tx_task", 4096, NULL, 5, NULL);
|
||||||
|
#else
|
||||||
|
printf("I2S PDM RX example start\n---------------------------\n");
|
||||||
|
xTaskCreate(i2s_example_pdm_rx_task, "i2s_example_pdm_rx_task", 4096, NULL, 5, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
78
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c
Normal file
78
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_rx.c
Normal 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);
|
||||||
|
}
|
109
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c
Normal file
109
examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c
Normal 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);
|
||||||
|
}
|
22
examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py
Normal file
22
examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py
Normal 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)
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(i2s_std_example)
|
73
examples/peripherals/i2s/i2s_basic/i2s_std/README.md
Normal file
73
examples/peripherals/i2s/i2s_basic/i2s_std/README.md
Normal 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.
|
@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "i2s_std_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/i2s_std.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
|
||||||
|
* Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated,
|
||||||
|
* Specifically, due to the hardware limitation, the simplex rx & tx channels can't be registered on the same controllers on ESP32 and ESP32-S2,
|
||||||
|
* and ESP32-S2 has only one I2S controller, so it can't allocate two simplex channels */
|
||||||
|
#define EXAMPLE_I2S_DUPLEX_MODE (1 || CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define EXAMPLE_STD_BCLK_IO1 GPIO_NUM_4 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_STD_WS_IO1 GPIO_NUM_5 // I2S word select io number
|
||||||
|
#define EXAMPLE_STD_DOUT_IO1 GPIO_NUM_18 // I2S data out io number
|
||||||
|
#define EXAMPLE_STD_DIN_IO1 GPIO_NUM_19 // I2S data in io number
|
||||||
|
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
#define EXAMPLE_STD_BCLK_IO2 GPIO_NUM_22 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_STD_WS_IO2 GPIO_NUM_23 // I2S word select io number
|
||||||
|
#define EXAMPLE_STD_DOUT_IO2 GPIO_NUM_25 // I2S data out io number
|
||||||
|
#define EXAMPLE_STD_DIN_IO2 GPIO_NUM_26 // I2S data in io number
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define EXAMPLE_STD_BCLK_IO1 GPIO_NUM_2 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_STD_WS_IO1 GPIO_NUM_3 // I2S word select io number
|
||||||
|
#define EXAMPLE_STD_DOUT_IO1 GPIO_NUM_4 // I2S data out io number
|
||||||
|
#define EXAMPLE_STD_DIN_IO1 GPIO_NUM_5 // I2S data in io number
|
||||||
|
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
#define EXAMPLE_STD_BCLK_IO2 GPIO_NUM_6 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_STD_WS_IO2 GPIO_NUM_7 // I2S word select io number
|
||||||
|
#define EXAMPLE_STD_DOUT_IO2 GPIO_NUM_8 // I2S data out io number
|
||||||
|
#define EXAMPLE_STD_DIN_IO2 GPIO_NUM_9 // I2S data in io number
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXAMPLE_BUFF_SIZE 2048
|
||||||
|
|
||||||
|
static i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
||||||
|
static i2s_chan_handle_t rx_chan; // I2S rx channel handler
|
||||||
|
|
||||||
|
static void i2s_example_read_task(void *args)
|
||||||
|
{
|
||||||
|
uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||||
|
assert(r_buf); // Check if r_buf allocation success
|
||||||
|
size_t r_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
/* Read i2s data */
|
||||||
|
if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
|
||||||
|
printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
|
||||||
|
printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
|
||||||
|
r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
|
||||||
|
} else {
|
||||||
|
printf("Read Task: i2s read failed\n");
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
free(r_buf);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2s_example_write_task(void *args)
|
||||||
|
{
|
||||||
|
uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||||
|
assert(w_buf); // Check if w_buf allocation success
|
||||||
|
|
||||||
|
/* Assign w_buf */
|
||||||
|
for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
|
||||||
|
w_buf[i] = 0x12;
|
||||||
|
w_buf[i + 1] = 0x34;
|
||||||
|
w_buf[i + 2] = 0x56;
|
||||||
|
w_buf[i + 3] = 0x78;
|
||||||
|
w_buf[i + 4] = 0x9A;
|
||||||
|
w_buf[i + 5] = 0xBC;
|
||||||
|
w_buf[i + 6] = 0xDE;
|
||||||
|
w_buf[i + 7] = 0xF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t w_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
/* Write i2s data */
|
||||||
|
if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
|
||||||
|
printf("Write Task: i2s write %d bytes\n", w_bytes);
|
||||||
|
} else {
|
||||||
|
printf("Write Task: i2s write failed\n");
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
free(w_buf);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
static void i2s_example_init_std_duplex(void)
|
||||||
|
{
|
||||||
|
/* Setp 1: Determine the I2S channel configuration and allocate both channels
|
||||||
|
* The default configuration can be generated by the helper macro,
|
||||||
|
* it only requires the I2S controller id and I2S role */
|
||||||
|
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
|
||||||
|
|
||||||
|
/* Step 2: Setting the configurations of standard mode, and initialize rx & tx channels
|
||||||
|
* The slot configuration and clock configuration can be generated by the macros
|
||||||
|
* These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
|
||||||
|
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||||
|
i2s_std_config_t std_cfg = {
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||||
|
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_STD_BCLK_IO1,
|
||||||
|
.ws = EXAMPLE_STD_WS_IO1,
|
||||||
|
.dout = EXAMPLE_STD_DOUT_IO1,
|
||||||
|
.din = EXAMPLE_STD_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/* Initialize the channels */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void i2s_example_init_std_simplex(void)
|
||||||
|
{
|
||||||
|
/* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
|
||||||
|
* The default configuration can be generated by the helper macro,
|
||||||
|
* it only requires the I2S controller id and I2S role
|
||||||
|
* The tx and rx channels here are registered on different I2S controller,
|
||||||
|
* only ESP32-C3, ESP32-S3 and ESP32-H2 allow to register two separate tx & rx channels on a same controller */
|
||||||
|
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||||
|
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
|
||||||
|
|
||||||
|
/* Step 2: Setting the configurations of standard mode and initialize each channels one by one
|
||||||
|
* The slot configuration and clock configuration can be generated by the macros
|
||||||
|
* These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
|
||||||
|
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||||
|
i2s_std_config_t tx_std_cfg = {
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||||
|
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_STD_BCLK_IO1,
|
||||||
|
.ws = EXAMPLE_STD_WS_IO1,
|
||||||
|
.dout = EXAMPLE_STD_DOUT_IO1,
|
||||||
|
.din = EXAMPLE_STD_DIN_IO1,
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
|
||||||
|
|
||||||
|
i2s_std_config_t rx_std_cfg = {
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||||
|
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_STD_BCLK_IO2,
|
||||||
|
.ws = EXAMPLE_STD_WS_IO2,
|
||||||
|
.dout = EXAMPLE_STD_DOUT_IO2,
|
||||||
|
.din = EXAMPLE_STD_DIN_IO2,
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/* Default is only receiving left slot in mono mode,
|
||||||
|
* update to right here to show how to change the default configuration */
|
||||||
|
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
i2s_example_init_std_duplex();
|
||||||
|
#else
|
||||||
|
i2s_example_init_std_simplex();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Step 3: Enable the tx and rx channels before writing or reading data */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
|
||||||
|
|
||||||
|
/* Step 4: Create writing and reading task */
|
||||||
|
xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
|
||||||
|
xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL);
|
||||||
|
}
|
35
examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py
Normal file
35
examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py
Normal 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)
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(i2s_tdm_example)
|
72
examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md
Normal file
72
examples/peripherals/i2s/i2s_basic/i2s_tdm/README.md
Normal 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.
|
||||||
|
|
@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "i2s_tdm_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/i2s_tdm.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
|
||||||
|
* Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated */
|
||||||
|
#define EXAMPLE_I2S_DUPLEX_MODE 1
|
||||||
|
|
||||||
|
#define EXAMPLE_TDM_BCLK_IO1 GPIO_NUM_2 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_TDM_WS_IO1 GPIO_NUM_3 // I2S word select io number
|
||||||
|
#define EXAMPLE_TDM_DOUT_IO1 GPIO_NUM_4 // I2S data out io number
|
||||||
|
#define EXAMPLE_TDM_DIN_IO1 GPIO_NUM_5 // I2S data in io number
|
||||||
|
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
#define EXAMPLE_TDM_BCLK_IO2 GPIO_NUM_6 // I2S bit clock io number
|
||||||
|
#define EXAMPLE_TDM_WS_IO2 GPIO_NUM_7 // I2S word select io number
|
||||||
|
#define EXAMPLE_TDM_DOUT_IO2 GPIO_NUM_8 // I2S data out io number
|
||||||
|
#define EXAMPLE_TDM_DIN_IO2 GPIO_NUM_9 // I2S data in io number
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXAMPLE_BUFF_SIZE 2048
|
||||||
|
|
||||||
|
static i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
||||||
|
static i2s_chan_handle_t rx_chan; // I2S rx channel handler
|
||||||
|
|
||||||
|
static void i2s_example_read_task(void *args)
|
||||||
|
{
|
||||||
|
uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||||
|
assert(r_buf); // Check if r_buf allocation success
|
||||||
|
size_t r_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
/* Read i2s data */
|
||||||
|
if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
|
||||||
|
printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
|
||||||
|
printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
|
||||||
|
r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
|
||||||
|
} else {
|
||||||
|
printf("Read Task: i2s read failed\n");
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
free(r_buf);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2s_example_write_task(void *args)
|
||||||
|
{
|
||||||
|
uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||||
|
assert(w_buf); // Check if w_buf allocation success
|
||||||
|
|
||||||
|
/* Assign w_buf */
|
||||||
|
for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
|
||||||
|
w_buf[i] = 0x12;
|
||||||
|
w_buf[i + 1] = 0x34;
|
||||||
|
w_buf[i + 2] = 0x56;
|
||||||
|
w_buf[i + 3] = 0x78;
|
||||||
|
w_buf[i + 4] = 0x9A;
|
||||||
|
w_buf[i + 5] = 0xBC;
|
||||||
|
w_buf[i + 6] = 0xDE;
|
||||||
|
w_buf[i + 7] = 0xF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t w_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
/* Write i2s data */
|
||||||
|
if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
|
||||||
|
printf("Write Task: i2s write %d bytes\n", w_bytes);
|
||||||
|
} else {
|
||||||
|
printf("Write Task: i2s write failed\n");
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
free(w_buf);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
static void i2s_example_init_tdm_duplex(void)
|
||||||
|
{
|
||||||
|
/* Setp 1: Determine the I2S channel configuration and allocate both channels
|
||||||
|
* The default configuration can be generated by the helper macro,
|
||||||
|
* it only requires the I2S controller id and I2S role */
|
||||||
|
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
|
||||||
|
|
||||||
|
/* Step 2: Setting the configurations of TDM mode, and initialize rx & tx channels
|
||||||
|
* The slot configuration and clock configuration can be generated by the macros
|
||||||
|
* These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode.
|
||||||
|
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||||
|
i2s_tdm_config_t tdm_cfg = {
|
||||||
|
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||||
|
/* Limited by the hardware, the number of bit clock can't exceed 128 in one frame,
|
||||||
|
* which is to say, TDM mode can only support 32 bit-width data upto 4 slots,
|
||||||
|
* 16 bit-width data upto 8 slots and 8 bit-width data upto 16 slots */
|
||||||
|
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
|
||||||
|
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_TDM_BCLK_IO1,
|
||||||
|
.ws = EXAMPLE_TDM_WS_IO1,
|
||||||
|
.dout = EXAMPLE_TDM_DOUT_IO1,
|
||||||
|
.din = EXAMPLE_TDM_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
|
||||||
|
* The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
|
||||||
|
* Modify the mclk_multiple to 512 directly here to avoid the warning */
|
||||||
|
tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||||
|
|
||||||
|
/* Initialize the channels */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void i2s_example_init_tdm_simplex(void)
|
||||||
|
{
|
||||||
|
/* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
|
||||||
|
* The default configuration can be generated by the helper macro,
|
||||||
|
* it only requires the I2S controller id and I2S role */
|
||||||
|
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||||
|
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
|
||||||
|
|
||||||
|
/* Step 2: Setting the configurations of TDM mode and initialize each channels one by one
|
||||||
|
* The slot configuration and clock configuration can be generated by the macros
|
||||||
|
* These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode.
|
||||||
|
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||||
|
i2s_tdm_config_t tx_tdm_cfg = {
|
||||||
|
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||||
|
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
|
||||||
|
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_TDM_BCLK_IO1,
|
||||||
|
.ws = EXAMPLE_TDM_WS_IO1,
|
||||||
|
.dout = EXAMPLE_TDM_DOUT_IO1,
|
||||||
|
.din = EXAMPLE_TDM_DIN_IO1,
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
tx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tx_tdm_cfg));
|
||||||
|
|
||||||
|
i2s_tdm_config_t rx_tdm_cfg = {
|
||||||
|
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(44100),
|
||||||
|
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO,
|
||||||
|
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 |
|
||||||
|
I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||||
|
.bclk = EXAMPLE_TDM_BCLK_IO2,
|
||||||
|
.ws = EXAMPLE_TDM_WS_IO2,
|
||||||
|
.dout = EXAMPLE_TDM_DOUT_IO2,
|
||||||
|
.din = EXAMPLE_TDM_DIN_IO2,
|
||||||
|
.invert_flags = {
|
||||||
|
.mclk_inv = false,
|
||||||
|
.bclk_inv = false,
|
||||||
|
.ws_inv = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
|
||||||
|
* The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
|
||||||
|
* Modify the mclk_multiple to 512 directly here to avoid the warning */
|
||||||
|
rx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &rx_tdm_cfg));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||||
|
i2s_example_init_tdm_duplex();
|
||||||
|
#else
|
||||||
|
i2s_example_init_tdm_simplex();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Step 3: Enable the tx and rx channels before writing or reading data */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
|
||||||
|
|
||||||
|
/* Step 4: Create writing and reading task */
|
||||||
|
xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
|
||||||
|
xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL);
|
||||||
|
}
|
33
examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py
Normal file
33
examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py
Normal 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)
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
@ -1,2 +0,0 @@
|
|||||||
idf_component_register(SRCS "i2s_example_main.c"
|
|
||||||
INCLUDE_DIRS ".")
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
@ -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,
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user