i2s: add slot sequence table

Closes: https://github.com/espressif/esp-idf/issues/9208

When I2S is configured into different modes, the slot sequence varies.
This commit updates slot sequence tables and corresponding descriptions
in (both code and programming guide).
This commit is contained in:
laokaiyao
2022-07-05 11:22:27 +08:00
parent 92ea22fe81
commit edee3ee3cd
51 changed files with 795 additions and 292 deletions

View File

@@ -116,7 +116,7 @@ else()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${includes}
PRIV_INCLUDE_DIRS "include/driver"
PRIV_REQUIRES efuse esp_timer esp_adc esp_psram
PRIV_REQUIRES efuse esp_timer esp_adc
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support
LDFRAGMENTS linker.lf)
endif()

View File

@@ -1057,15 +1057,15 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
slot_cfg->slot_mode = ((ch & 0xFFFF) == I2S_CHANNEL_MONO) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) {
if (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) {
if (slot_cfg->std.slot_mask == I2S_STD_SLOT_LEFT_RIGHT) {
slot_cfg->std.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
if (slot_cfg->std.slot_mask == I2S_STD_SLOT_BOTH) {
slot_cfg->std.slot_mask = I2S_STD_SLOT_LEFT;
#if SOC_I2S_HW_VERSION_1
// Enable right first to get correct data sequence
slot_cfg->std.ws_pol = !slot_cfg->std.ws_pol;
#endif
}
} else {
slot_cfg->std.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
slot_cfg->std.slot_mask = I2S_STD_SLOT_BOTH;
}
}
#if SOC_I2S_SUPPORTS_TDM
@@ -1279,12 +1279,12 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
SLOT_CFG(std).ws_width = i2s_config->bits_per_sample;
SLOT_CFG(std).ws_pol = false;
if (i2s_config->channel_format == I2S_CHANNEL_FMT_RIGHT_LEFT) {
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_BOTH;
} else if (i2s_config->channel_format == I2S_CHANNEL_FMT_ALL_LEFT ||
i2s_config->channel_format == I2S_CHANNEL_FMT_ONLY_LEFT) {
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_LEFT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_LEFT;
} else {
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_RIGHT;
}
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
SLOT_CFG(std).bit_shift = true;

View File

@@ -46,10 +46,9 @@
#include "esp_attr.h"
#include "esp_rom_gpio.h"
#if CONFIG_SPIRAM && !CONFIG_IDF_TARGET_ESP32
#include "esp_psram.h"
#endif
/* The actual max size of DMA buffer is 4095
* Set 4092 here to align with 4-byte, so that the position of the slot data in the buffer will be relatively fixed */
#define I2S_DMA_BUFFER_MAX_SIZE (4092)
// If ISR handler is allowed to run whilst cache is disabled,
@@ -62,7 +61,6 @@
#define I2S_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif //CONFIG_I2S_ISR_IRAM_SAFE
#define I2S_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#define I2S_PSRAM_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
/**
* @brief Global i2s platform object
@@ -410,22 +408,13 @@ esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bu
handle->dma.desc_num = num;
handle->dma.buf_size = bufsize;
uint32_t dma_alloc_caps = I2S_DMA_ALLOC_CAPS;
uint32_t mem_alloc_caps = I2S_MEM_ALLOC_CAPS;
#if (CONFIG_SPIRAM_USE_MALLOC || CONFIG_SPIRAM_USE_CAPS_ALLOC) && !CONFIG_IDF_TARGET_ESP32
if (handle->dma.psram_en && esp_psram_is_initialized()) {
ESP_LOGD(TAG, "DMA buffer will be allocate in the psram");
dma_alloc_caps = I2S_PSRAM_ALLOC_CAPS;
mem_alloc_caps = I2S_PSRAM_ALLOC_CAPS;
}
#endif
handle->dma.desc = (lldesc_t **)heap_caps_aligned_calloc(4, num, sizeof(lldesc_t *), mem_alloc_caps);
/* Descriptors must be in the internal RAM */
handle->dma.desc = (lldesc_t **)heap_caps_calloc(num, sizeof(lldesc_t *), I2S_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc, ESP_ERR_NO_MEM, err, TAG, "create I2S DMA decriptor array failed");
handle->dma.bufs = (uint8_t **)heap_caps_aligned_calloc(4, num, sizeof(uint8_t *), mem_alloc_caps);
handle->dma.bufs = (uint8_t **)heap_caps_calloc(num, sizeof(uint8_t *), I2S_MEM_ALLOC_CAPS);
for (int i = 0; i < num; i++) {
/* Allocate DMA descriptor */
handle->dma.desc[i] = (lldesc_t *) heap_caps_aligned_calloc(4, 1, sizeof(lldesc_t), dma_alloc_caps);
handle->dma.desc[i] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), I2S_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[i], ESP_ERR_NO_MEM, err, TAG, "allocate DMA description failed");
handle->dma.desc[i]->owner = 1;
handle->dma.desc[i]->eof = 1;
@@ -433,9 +422,10 @@ esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bu
handle->dma.desc[i]->length = bufsize;
handle->dma.desc[i]->size = bufsize;
handle->dma.desc[i]->offset = 0;
handle->dma.bufs[i] = (uint8_t *) heap_caps_aligned_calloc(4, 1, bufsize * sizeof(uint8_t), dma_alloc_caps);
handle->dma.bufs[i] = (uint8_t *) heap_caps_calloc(1, bufsize * sizeof(uint8_t), I2S_DMA_ALLOC_CAPS);
handle->dma.desc[i]->buf = handle->dma.bufs[i];
ESP_GOTO_ON_FALSE(handle->dma.desc[i]->buf, ESP_ERR_NO_MEM, err, TAG, "allocate DMA buffer failed");
ESP_LOGV(TAG, "desc addr: %8p\tbuffer addr:%8p", handle->dma.desc[i], handle->dma.bufs[i]);
}
/* Connect DMA descriptor as a circle */
for (int i = 0; i < num; i++) {
@@ -463,6 +453,10 @@ uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq_hz)
* So the div here should be at least 2 */
mclk_div = mclk_div < 2 ? 2 : mclk_div;
uint32_t expt_freq = mclk_freq_hz * mclk_div;
if (expt_freq > SOC_APLL_MAX_HZ) {
ESP_LOGE(TAG, "The required APLL frequecy exceed its maximum value");
return 0;
}
uint32_t real_freq = 0;
esp_err_t ret = periph_rtc_apll_freq_set(expt_freq, &real_freq);
if (ret == ESP_ERR_INVALID_ARG) {
@@ -795,7 +789,6 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *
err, TAG, "register I2S tx channel failed");
i2s_obj->tx_chan->role = chan_cfg->role;
i2s_obj->tx_chan->dma.auto_clear = chan_cfg->auto_clear;
i2s_obj->tx_chan->dma.psram_en = chan_cfg->dma_buf_in_psram;
i2s_obj->tx_chan->dma.desc_num = chan_cfg->dma_desc_num;
i2s_obj->tx_chan->dma.frame_num = chan_cfg->dma_frame_num;
i2s_obj->tx_chan->start = i2s_tx_channel_start;
@@ -808,7 +801,6 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *
ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_RX, chan_cfg->dma_desc_num),
err, TAG, "register I2S rx channel failed");
i2s_obj->rx_chan->role = chan_cfg->role;
i2s_obj->rx_chan->dma.psram_en = chan_cfg->dma_buf_in_psram;
i2s_obj->rx_chan->dma.desc_num = chan_cfg->dma_desc_num;
i2s_obj->rx_chan->dma.frame_num = chan_cfg->dma_frame_num;
i2s_obj->rx_chan->start = i2s_rx_channel_start;

View File

@@ -68,7 +68,8 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx
/* Set clock configurations in HAL*/
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
#if SOC_I2S_HW_VERSION_2
/* Work aroud for PDM TX clock, set the raw division directly to reduce the noise */
/* Work aroud for PDM TX clock, overwrite the raw division directly to reduce the noise
* This set of coefficients is a special division to reduce the background noise in PDM TX mode */
i2s_ll_tx_set_raw_clk_div(handle->controller->hal.dev, 1, 1, 0, 0);
#endif
portEXIT_CRITICAL(&g_i2s.spinlock);
@@ -167,9 +168,10 @@ esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_t
}
handle->mode_info = calloc(1, sizeof(i2s_pdm_tx_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, &pdm_tx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_pdm_tx_slot should be called before i2s_set_pdm_tx_clock while initializing, because clock is relay on the slot */
/* i2s_set_pdm_tx_slot should be called before i2s_set_pdm_tx_clock and i2s_pdm_tx_set_gpio
* while initializing, because clock and gpio is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_slot(handle, &pdm_tx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, &pdm_tx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when the clock source is APLL */
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {

View File

@@ -47,7 +47,6 @@ typedef struct {
uint32_t desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */
uint32_t frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */
uint32_t buf_size; /*!< dma buffer size */
bool psram_en; /*!< Whether prefer to allocate the DMA buffers in the psram */
bool auto_clear; /*!< Set to auto clear DMA TX descriptor, i2s will always send zero automatically if no data to send */
uint32_t rw_pos; /*!< reading/writing pointer position */
void *curr_ptr; /*!< Pointer to current dma buffer */

View File

@@ -62,6 +62,9 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c
{
esp_err_t ret = ESP_OK;
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
ESP_RETURN_ON_FALSE(std_cfg->slot_cfg.data_bit_width != I2S_DATA_BIT_WIDTH_24BIT ||
(clk_cfg->mclk_multiple % 3 == 0), ESP_ERR_INVALID_ARG, TAG,
"The 'mclk_multiple' should be the multiple of 3 while using 24-bit data width");
i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
@@ -86,10 +89,6 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c
static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
{
/* Check configuration validity */
ESP_RETURN_ON_FALSE((slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO) || (slot_cfg->slot_mask != I2S_STD_SLOT_LEFT_RIGHT),
ESP_ERR_INVALID_ARG, TAG, "Can't select both left and right slot in mono mode");
/* Update the total slot num and active slot num */
handle->total_slot = 2;
handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2;

View File

@@ -41,19 +41,20 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
clk_info->mclk = rate * clk_cfg->mclk_multiple;
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
/* While RECEIVING multiple slots, the data will go wrong if the bclk_div is euqal or smaller than 2 */
check:
if (clk_info->bclk_div <= 2) {
do {
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;
}
if (clk_info->bclk_div <= 2) {
ESP_LOGW(TAG, "the current mclk multiple is too small, adjust the mclk multiple to %d", clk_info->mclk / rate);
}
} while (clk_info->bclk_div <= 2);
} else {
/* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
clk_info->bclk_div = 8;
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_APLL ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
#else

View File

@@ -23,8 +23,7 @@ extern "C" {
.id = i2s_num, \
.role = i2s_role, \
.dma_desc_num = 6, \
.dma_frame_num = 250, \
.dma_buf_in_psram = false, \
.dma_frame_num = 240, \
.auto_clear = false, \
}
@@ -60,9 +59,8 @@ typedef struct {
/* DMA configurations */
uint32_t dma_desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */
uint32_t dma_frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */
bool dma_buf_in_psram; /*!< Prefer to allocate the DMA buffers in the psram (not supported on ESP32)
* To allocate the DMA buffers in the psram, SPIRAM should be enabled in menuconfig
uint32_t dma_frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots,
* it should be the multiple of '3' when the data bit width is 24.
*/
bool auto_clear; /*!< Set to auto clear DMA TX buffer, i2s will always send zero automatically if no data to send */
} i2s_chan_config_t;

View File

@@ -191,6 +191,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = I2S_PDM_SLOT_BOTH, \
.sd_prescale = 0, \
.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
@@ -243,6 +244,9 @@ typedef struct {
* Stereo means the data buffer contains two slots data
*/
/* Particular fields */
#if SOC_I2S_HW_VERSION_1
i2s_pdm_slot_mask_t slot_mask; /*!< Slot mask to choose left or right slot */
#endif
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 hp_scale; /*!< High pass filter scaling value */

View File

@@ -16,11 +16,13 @@
#include "hal/gpio_types.h"
#include "driver/i2s_common.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
#if CONFIG_IDF_TARGET_ESP32
/**
* @brief Philip format in 2 slots
* @param bits_per_sample i2s data bit width
@@ -31,7 +33,63 @@ extern "C" {
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
.msb_right = (bits_per_sample <= I2S_DATA_BIT_WIDTH_16BIT) ? \
true : false, \
}
/**
* @brief PCM(short) format in 2 slots
* @note PCM(long) is sample as philip in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
.msb_right = (bits_per_sample <= I2S_DATA_BIT_WIDTH_16BIT) ? \
true : false, \
}
/**
* @brief MSB format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
.msb_right = (bits_per_sample <= I2S_DATA_BIT_WIDTH_16BIT) ? \
true : false, \
}
#elif CONFIG_IDF_TARGET_ESP32S2
/**
* @brief Philip format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
@@ -49,7 +107,7 @@ extern "C" {
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
@@ -66,7 +124,7 @@ extern "C" {
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
I2S_STD_SLOT_LEFT : I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
@@ -83,8 +141,7 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
@@ -103,8 +160,7 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
@@ -122,8 +178,7 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
@@ -152,7 +207,10 @@ typedef struct {
/* General fields */
i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample) */
i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) */
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
* In TX direction, mono means the written buffer contains only one slot data
* and stereo means the written buffer contains both left and right data
*/
/* Particular fields */
i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */
@@ -175,7 +233,11 @@ typedef struct {
/* General fields */
uint32_t sample_rate_hz; /*!< I2S sample rate */
i2s_clock_src_t clk_src; /*!< Choose clock source */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate
* Default is 256 in the helper macro, it can satisfy most of cases,
* but please set this field a multiple of '3' (like 384) when using 24-bit data width,
* otherwise the sample rate might be inaccurate
*/
} i2s_std_clk_config_t;
/**

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 | ESP32-H2 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |

View File

@@ -3,5 +3,4 @@ set(srcs "test_app_main.c"
"test_i2s_iram.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "../../"
WHOLE_ARCHIVE)

View File

@@ -32,7 +32,7 @@
#include "soc/pcnt_periph.h"
#endif
#include "test_inc/test_i2s.h"
#include "../../test_inc/test_i2s.h"
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
@@ -416,7 +416,7 @@ TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
};
i2s_std_config_t rx_std_cfg = tx_std_cfg;
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
@@ -463,7 +463,7 @@ TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
* rx receive: 0x00[R] 0x02[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
@@ -493,7 +493,7 @@ TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_BOTH;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));

View File

@@ -15,7 +15,7 @@
#include "soc/soc_caps.h"
#include "esp_private/i2s_platform.h"
#include "esp_private/spi_flash_os.h"
#include "test_inc/test_i2s.h"
#include "../../test_inc/test_i2s.h"
#if CONFIG_I2S_ISR_IRAM_SAFE

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 | ESP32-H2 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |

View File

@@ -2,5 +2,4 @@ set(srcs "test_app_main.c"
"test_legacy_i2s.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "../../"
WHOLE_ARCHIVE)

View File

@@ -30,7 +30,7 @@
#include "soc/pcnt_periph.h"
#endif
#include "test_inc/test_i2s.h"
#include "../../test_inc/test_i2s.h"
#define PERCENT_DIFF 0.0001
@@ -254,9 +254,9 @@ TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s_legacy]")
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
/* Config TX as stereo channel directly, because legacy driver can't support config tx&rx separately */
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
i2s_ll_tx_select_std_slot(&I2S0, I2S_STD_SLOT_BOTH, false);
#else
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
i2s_ll_tx_select_std_slot(&I2S0, I2S_STD_SLOT_BOTH);
#endif
i2s_ll_tx_enable_mono_mode(&I2S0, false);
@@ -352,9 +352,9 @@ TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s_legacy]")
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
i2s_ll_tx_select_std_slot(&I2S0, I2S_STD_SLOT_BOTH, false);
#else
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
i2s_ll_tx_select_std_slot(&I2S0, I2S_STD_SLOT_BOTH);
#endif
i2s_ll_tx_enable_mono_mode(&I2S0, false);
@@ -676,7 +676,7 @@ TEST_CASE("I2S_write_and_read_test_master_rx_and_slave_tx", "[i2s_legacy]")
TEST_CASE("I2S_memory_leaking_test", "[i2s_legacy]")
{
i2s_config_t master_i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX,
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = SAMPLE_BITS,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
@@ -701,20 +701,33 @@ TEST_CASE("I2S_memory_leaking_test", "[i2s_legacy]")
.data_out_num = -1,
.data_in_num = DATA_IN_IO
};
uint8_t *w_buf = calloc(1, 2000);
TEST_ASSERT(w_buf);
uint8_t *r_buf = calloc(1, 2000);
TEST_ASSERT(r_buf);
size_t w_bytes = 0;
size_t r_bytes = 0;
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, 2000, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, 2000, &r_bytes, portMAX_DELAY));
i2s_driver_uninstall(I2S_NUM_0);
int initial_size = esp_get_free_heap_size();
for (int i = 0; i < 100; i++) {
for (int i = 0; i < 50; i++) {
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, 2000, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, 2000, &r_bytes, portMAX_DELAY));
i2s_driver_uninstall(I2S_NUM_0);
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT(initial_size == esp_get_free_heap_size());
free(w_buf);
free(r_buf);
}
#if SOC_I2S_SUPPORTS_APLL

View File

@@ -185,7 +185,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
i2s_ll_tx_bypass_pcm(bus->hal.dev, true);
i2s_ll_tx_set_slave_mod(bus->hal.dev, false);
i2s_ll_tx_set_bits_mod(bus->hal.dev, bus_config->bus_width);
i2s_ll_tx_select_slot(bus->hal.dev, I2S_STD_SLOT_ONLY_LEFT, false); // mono
i2s_ll_tx_select_std_slot(bus->hal.dev, I2S_STD_SLOT_BOTH, true); // copy mono
bus->bus_width = bus_config->bus_width;
i2s_ll_tx_enable_right_first(bus->hal.dev, true);
#if SOC_I2S_SUPPORTS_DMA_EQUAL

View File

@@ -745,28 +745,107 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
hw->conf.rx_msb_shift = msb_shift_enable;
}
/**
* @brief Set I2S PDM TX chan mode
* @param slot_mask select slot to send data
* @param is_mono is mono mode
*/
static inline void i2s_ll_tx_select_pdm_slot(i2s_dev_t *hw, i2s_pdm_slot_mask_t slot_mask, bool is_mono)
{
if (is_mono) {
switch (slot_mask)
{
case I2S_PDM_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 3;
break;
case I2S_PDM_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 4;
break;
case I2S_PDM_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 1; // 1 & 2 has same effect
break;
default:
break;
}
} else {
switch (slot_mask)
{
case I2S_PDM_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_PDM_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 2;
break;
case I2S_PDM_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
}
}
}
/**
* @brief Set I2S PDM RX chan mode
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_rx_select_pdm_slot(i2s_dev_t *hw, i2s_pdm_slot_mask_t slot_mask)
{
switch (slot_mask)
{
case I2S_PDM_SLOT_RIGHT:
hw->conf_chan.rx_chan_mod = 1;
break;
case I2S_PDM_SLOT_LEFT:
hw->conf_chan.rx_chan_mod = 2;
break;
case I2S_PDM_SLOT_BOTH:
hw->conf_chan.rx_chan_mod = 0;
break;
default:
break;
}
}
/**
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
* @param is_mono is mono mode
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_mono)
{
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
if (is_mono) {
switch (slot_mask)
{
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 3;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 4;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 1; // 1 & 2 has same effect
break;
default:
break;
}
} else {
switch (slot_mask)
{
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 2;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
}
}
}
@@ -777,17 +856,17 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
* @param slot_mask select slot to receive data
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->conf_chan.rx_chan_mod = 0;
break;
default:

View File

@@ -591,7 +591,7 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -599,13 +599,13 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->tx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -619,7 +619,7 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -627,13 +627,13 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->rx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -1084,6 +1084,8 @@ static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool
* Mono Left Left 2 1
* Mono Left Single 3 1
* Mono Single Right 4 1
* @note The 'Single' above means always sending the value of `conf_single_data` reg
* The default value of `conf_single_data` reg is '0', it is not public for now
*
* @param hw Peripheral I2S hardware instance address.
* @param is_mono The DMA data only has one slot (mono) or contains two slots (stereo)

View File

@@ -593,7 +593,7 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -601,13 +601,13 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->tx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -621,7 +621,7 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -629,13 +629,13 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->rx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -1097,6 +1097,8 @@ static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool
* Mono Left Left 2 1
* Mono Left Single 3 1
* Mono Single Right 4 1
* @note The 'Single' above means always sending the value of `conf_single_data` reg
* The default value of `conf_single_data` reg is '0', it is not public for now
*
* @param hw Peripheral I2S hardware instance address.
* @param is_mono The DMA data only has one slot (mono) or contains two slots (stereo)

View File

@@ -841,22 +841,40 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
* @param is_mono is mono mode
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_mono)
{
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
if (is_mono) {
switch (slot_mask)
{
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 3;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 4;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 1; // 1 & 2 has same effect
break;
default:
break;
}
} else {
switch (slot_mask)
{
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_STD_SLOT_LEFT:
hw->conf_chan.tx_chan_mod = 2;
break;
case I2S_STD_SLOT_BOTH:
hw->conf_chan.tx_chan_mod = 0;
break;
default:
break;
}
}
}
@@ -866,17 +884,17 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->conf_chan.rx_chan_mod = 0;
break;
default:

View File

@@ -594,7 +594,7 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_tx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -602,13 +602,13 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->tx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -622,7 +622,7 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
* @param hw Peripheral I2S hardware instance address.
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
static inline void i2s_ll_rx_select_std_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
@@ -630,13 +630,13 @@ static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
case I2S_STD_SLOT_LEFT:
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
case I2S_STD_SLOT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
case I2S_STD_SLOT_BOTH:
hw->rx_tdm_ctrl.val |= 0x03;
break;
default:
@@ -1109,6 +1109,8 @@ static inline void i2s_ll_tx_pdm_dma_take_mode(i2s_dev_t *hw, bool is_mono, bool
* Mono Left Left 2 1
* Mono Left Single 3 1
* Mono Single Right 4 1
* @note The 'Single' above means always sending the value of `conf_single_data` reg
* The default value of `conf_single_data` reg is '0', it is not public for now
*
* @param hw Peripheral I2S hardware instance address.
* @param is_mono The DMA data only has one slot (mono) or contains two slots (stereo)

View File

@@ -62,18 +62,22 @@ void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
i2s_ll_tx_reset(hal->dev);
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_tx_select_std_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
// According to the test, the behavior of tx_msb_right is opposite with TRM, TRM is wrong?
i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask);
bool is_copy_mono = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO && slot_cfg->std.slot_mask == I2S_STD_SLOT_BOTH;
i2s_ll_tx_enable_mono_mode(hal->dev, is_copy_mono);
i2s_ll_tx_select_std_slot(hal->dev, is_copy_mono ? I2S_STD_SLOT_LEFT : slot_cfg->std.slot_mask);
i2s_ll_tx_set_skip_mask(hal->dev, (slot_cfg->std.slot_mask != I2S_STD_SLOT_BOTH) &&
(slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO));
i2s_ll_tx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
@@ -93,13 +97,13 @@ void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
i2s_ll_rx_select_std_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask);
i2s_ll_rx_select_std_slot(hal->dev, slot_cfg->std.slot_mask);
i2s_ll_rx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
@@ -141,6 +145,9 @@ void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
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);
i2s_ll_tx_select_pdm_slot(hal->dev, slot_cfg->pdm_tx.slot_mask, is_mono);
i2s_ll_tx_enable_msb_right(hal->dev, false);
i2s_ll_tx_enable_right_first(hal->dev, false);
#elif SOC_I2S_HW_VERSION_2
/* PDM TX line mode */
i2s_ll_tx_pdm_line_mode(hal->dev, slot_cfg->pdm_tx.line_mode);
@@ -188,12 +195,16 @@ void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
#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_select_pdm_slot(hal->dev, slot_cfg->pdm_rx.slot_mask);
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
i2s_ll_rx_enable_msb_right(hal->dev, false);
i2s_ll_rx_enable_right_first(hal->dev, false);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_tx_set_half_sample_bit(hal->dev, 16);
i2s_ll_rx_enable_mono_mode(hal->dev, false);
/* Set the channel mask to enable corresponding slots */
i2s_ll_rx_set_active_chan_mask(hal->dev, slot_cfg->pdm_rx.slot_mask);
/* Set the channel mask to enable corresponding slots, always enable two slots for stereo mode */
i2s_ll_rx_set_active_chan_mask(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO ?
I2S_PDM_SLOT_BOTH : slot_cfg->pdm_rx.slot_mask);
#endif
}

View File

@@ -67,6 +67,9 @@ typedef struct {
#if SOC_I2S_SUPPORTS_PDM_TX
/* PDM TX configurations */
struct {
#if SOC_I2S_HW_VERSION_1
i2s_pdm_slot_mask_t slot_mask; /*!< Slot mask to choose left or right slot */
#endif
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 hp_scale; /*!< High pass filter scaling value */

View File

@@ -64,7 +64,12 @@ typedef enum {
I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */
} i2s_slot_bit_width_t;
#if SOC_I2S_SUPPORTED
typedef soc_periph_i2s_clk_src_t i2s_clock_src_t; /*!< I2S clock source */
#else
typedef int i2s_clock_src_t; /*!< Define a default type to avoid compiling warnings */
#endif
#if SOC_I2S_SUPPORTS_PCM
/**
@@ -119,11 +124,13 @@ typedef enum {
/**
* @brief I2S slot select in standard mode
* @note It has different meanings in tx/rx/mono/stereo mode, and it may have differen behaviors on different targets
* For the details, please refer to the I2S API reference
*/
typedef enum {
I2S_STD_SLOT_ONLY_LEFT = BIT(0), /*!< I2S only transmits or receives left slot */
I2S_STD_SLOT_ONLY_RIGHT = BIT(1), /*!< I2S only transmits or receives right slot */
I2S_STD_SLOT_LEFT_RIGHT = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
I2S_STD_SLOT_LEFT = BIT(0), /*!< I2S transmits or receives left slot */
I2S_STD_SLOT_RIGHT = BIT(1), /*!< I2S transmits or receives right slot */
I2S_STD_SLOT_BOTH = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
} i2s_std_slot_mask_t;
/**

View File

@@ -3,8 +3,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_I2S_STRUCT_H_
#define _SOC_I2S_STRUCT_H_
#pragma once
#include <stdint.h>
@@ -460,5 +459,3 @@ extern i2s_dev_t I2S1;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_I2S_STRUCT_H_ */

View File

@@ -3,8 +3,10 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_I2S_STRUCT_H_
#define _SOC_I2S_STRUCT_H_
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -324,5 +326,3 @@ extern i2s_dev_t I2S0;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_I2S_STRUCT_H_ */

View File

@@ -3,8 +3,10 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_I2S_STRUCT_H_
#define _SOC_I2S_STRUCT_H_
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -330,5 +332,3 @@ extern i2s_dev_t I2S0;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_I2S_STRUCT_H_ */

View File

@@ -3,8 +3,10 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_I2S_STRUCT_H_
#define _SOC_I2S_STRUCT_H_
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -407,5 +409,3 @@ _Static_assert(sizeof(i2s_dev_t)==0x100, "invalid i2s_dev_t size");
#ifdef __cplusplus
}
#endif
#endif /* _SOC_I2S_STRUCT_H_ */

View File

@@ -3,11 +3,10 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_I2S_STRUCT_H_
#define _SOC_I2S_STRUCT_H_
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -336,7 +335,3 @@ extern i2s_dev_t I2S1;
#ifdef __cplusplus
}
#endif
#endif /*_SOC_I2S_STRUCT_H_ */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -135,7 +135,7 @@ Standard mode always has left and right two sound channels which are called 'slo
PDM Mode (TX)
^^^^^^^^^^^^^
PDM mode for tx channel can convert PCM data into PDM format which always has left and right slots. PDM TX can only support 16 bits width sample data. PDM TX only needs CLK pin for clock signal and DOUT pin for data signal (i.e. WS and SD signal in the following figure, the BCK signal is an internal bit sampling clock, not needed between PDM devices). This mode allows user to configure the up-sampling parameters :cpp:member:`i2s_pdm_tx_clk_config_t::up_sample_fp` :cpp:member:`i2s_pdm_tx_clk_config_t::up_sample_fs`. The up-sampling rate can be calculated by ``up_sample_rate = fp / fs``, there are up-sampling modes in PDM TX:
PDM(Pulse-density Modulation) mode for tx channel can convert PCM data into PDM format which always has left and right slots. PDM TX can only support 16 bits width sample data. PDM TX only needs CLK pin for clock signal and DOUT pin for data signal (i.e. WS and SD signal in the following figure, the BCK signal is an internal bit sampling clock, not needed between PDM devices). This mode allows user to configure the up-sampling parameters :cpp:member:`i2s_pdm_tx_clk_config_t::up_sample_fp` :cpp:member:`i2s_pdm_tx_clk_config_t::up_sample_fs`. The up-sampling rate can be calculated by ``up_sample_rate = fp / fs``, there are up-sampling modes in PDM TX:
- **Fixed Clock Frequency**: In this mode the up-sampling rate will change according to the sample rate. Setting ``fp = 960`` and ``fs = sample_rate / 100``, then the clock frequency(Fpdm) on CLK pin will be fixed to 128 * 48 KHz = 6.144 MHz, note that this frequency is not equal to the sample rate(Fpcm).
- **Fixed Up-sampling Rate**: In this mode the up-sampling rate is fixed to 2. Setting ``fp = 960`` and ``fs = 480``, then the clock frequency(Fpdm) on CLK pin will be ``128 * sample_rate``
@@ -148,7 +148,7 @@ Standard mode always has left and right two sound channels which are called 'slo
PDM Mode (RX)
^^^^^^^^^^^^^
PDM mode for rx channel can receive PDM format data and convert the data into PCM format. PDM RX can only support 16 bits width sample data. PDM RX only need WS pin for clock signal and DIN pin for data signal. This mode allows user to configure the down-sampling parameter :cpp:member:`i2s_pdm_rx_clk_config_t::dn_sample_mode`, there are two down-sampling modes in PDM RX:
PDM(Pulse-density Modulation) mode for rx channel can receive PDM format data and convert the data into PCM format. PDM RX can only support 16 bits width sample data. PDM RX only need WS pin for clock signal and DIN pin for data signal. This mode allows user to configure the down-sampling parameter :cpp:member:`i2s_pdm_rx_clk_config_t::dn_sample_mode`, there are two down-sampling modes in PDM RX:
- :cpp:enumerator:`i2s_pdm_dsr_t::I2S_PDM_DSR_8S`: In this mode, the clock frequency(Fpdm) on WS pin will be sample_rate(Fpcm) * 64.
- :cpp:enumerator:`i2s_pdm_dsr_t::I2S_PDM_DSR_16S`: In this mode, the clock frequency(Fpdm) on WS pin will be sample_rate(Fpcm) * 128.
@@ -159,9 +159,9 @@ Standard mode always has left and right two sound channels which are called 'slo
TDM Mode
^^^^^^^^
TDM mode supports upto 16 slots, these slots can be enabled by :cpp:member:`i2s_tdm_slot_config_t::slot_mask`. But due to the hardware limitation, only upto 4 slots are supported while the slot is set to 32 bit-width, and 8 slots for 16 bit-width, 16 slots for 8 bit-width. The slot communication format of TDM is almost same as standard mode, but there are some small differences between them.
TDM(Time Division Multiplexing) mode supports upto 16 slots, these slots can be enabled by :cpp:member:`i2s_tdm_slot_config_t::slot_mask`. But due to the hardware limitation, only upto 4 slots are supported while the slot is set to 32 bit-width, and 8 slots for 16 bit-width, 16 slots for 8 bit-width. The slot communication format of TDM is almost same as standard mode, but there are some small differences between them.
- **Philip Format** Data signal have one bit shift comparing to the WS(word select) signal. And no matter how many slots are contained in one frame, the duty of WS signal will always keep 50%.
- **Philip Format**: Data signal have one bit shift comparing to the WS(word select) signal. And no matter how many slots are contained in one frame, the duty of WS signal will always keep 50%.
.. wavedrom:: /../_static/diagrams/i2s/tdm_philip.json
@@ -228,7 +228,7 @@ The ``<mode>`` in the diagram can be replaced by corresponding I2S communication
Data Transport
^^^^^^^^^^^^^^
The data transport of I2S peripheral, including sending and receiving, is realized by DMA. Before transporting data, please call :cpp:func:`i2s_channel_enable` to enable the specific channel. When the sent or received data reach the size of one DMA buffer, ``I2S_OUT_EOF`` or ``I2S_IN_SUC_EOF`` interrupt will be triggered. Note that the DMA buffer size is not equal to :cpp:member:`i2s_std_slot_config_t::dma_frame_num`, one frame here means all the sampled data in one WS circle. Therefore, ``dma_buffer_size = dma_frame_num * slot_num * slot_bit_width / 8``. For the transmit case, users can input the data by calling :cpp:func:`i2s_channel_write`. This function will help users to copy the data from the source buffer to the DMA tx buffer and wait for the transmition finished. Then it'll repeat until the sent bytes reach the given size. For the receive case, the function :cpp:func:`i2s_channel_read` will wait for receiving the message queue which contains the DMA buffer address, it will help users to copy the data from DMA rx buffer to the destination buffer.
The data transport of I2S peripheral, including sending and receiving, is realized by DMA. Before transporting data, please call :cpp:func:`i2s_channel_enable` to enable the specific channel. When the sent or received data reach the size of one DMA buffer, ``I2S_OUT_EOF`` or ``I2S_IN_SUC_EOF`` interrupt will be triggered. Note that the DMA buffer size is not equal to :cpp:member:`i2s_chan_config_t::dma_frame_num`, one frame here means all the sampled data in one WS circle. Therefore, ``dma_buffer_size = dma_frame_num * slot_num * slot_bit_width / 8``. For the transmit case, users can input the data by calling :cpp:func:`i2s_channel_write`. This function will help users to copy the data from the source buffer to the DMA tx buffer and wait for the transmition finished. Then it'll repeat until the sent bytes reach the given size. For the receive case, the function :cpp:func:`i2s_channel_read` will wait for receiving the message queue which contains the DMA buffer address, it will help users to copy the data from DMA rx buffer to the destination buffer.
Both :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` are blocking functions, they will keep waiting until the whole source buffer are sent or the whole destination buffer loaded, unless they exceed the max blocking time, then the error code `ESP_ERR_TIMEOUT` will return in this case. To send or receive data asynchronously, callbacks can be registered by :cpp:func:`i2s_channel_register_event_callback`, users are able to access the DMA buffer directly in the callback function instead of transmitting or receiving by the two blocking functions. However, please be aware that it is an interrupt callback, don't do complex logic, floating operation or call non-reentrant functions in the callback.
@@ -268,7 +268,7 @@ Application Example
The examples of the I2S driver can be found in the directory :example:`peripherals/i2s`.
Here are some simple usages of each mode:
Standard TX/RX usage
Standard TX/RX Usage
^^^^^^^^^^^^^^^^^^^^
Different slot communication formats can be generated by following helper macros for standard mode. As described above, there are three formats in standard mode, their helper macros are:
@@ -284,6 +284,87 @@ The clock config helper macro is:
Please refer to :ref:`i2s-api-reference-i2s_std` for STD API information.
And for more details, please refer to :component_file:`driver/include/driver/i2s_std.h`.
STD TX Mode
~~~~~~~~~~~
Take 16-bit data width for example, when the data in a ``uint16_t`` writting buffer are:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 | ... |
+========+========+========+========+========+========+========+========+========+
| 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | ... |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
Here is the table of the real data on the line with different :cpp:member:`i2s_std_slot_config_t::slot_mode` and :cpp:member:`i2s_std_slot_config_t::slot_mask`
.. only:: esp32
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | ws low | ws high | ws low | ws high | ws low | ws high | ws low | ws high |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0002 | 0x0000 | 0x0001 | 0x0000 | 0x0004 | 0x0000 | 0x0003 | 0x0000 |
| 16 bit | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0000 | 0x0002 | 0x0000 | 0x0001 | 0x0000 | 0x0004 | 0x0000 | 0x0003 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0002 | 0x0002 | 0x0001 | 0x0001 | 0x0004 | 0x0004 | 0x0003 | 0x0003 |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | left | 0x0001 | 0x0001 | 0x0003 | 0x0003 | 0x0005 | 0x0005 | 0x0007 | 0x0007 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
It's similar when the data is 32-bit width, but take care when using 8-bit and 24-bit data width. For 8-bit width, the written buffer should still using ``uint16_t`` (i.e. align with 2 bytes), and only the high 8 bits will be valid, the low 8 bits are dropped, and for 24-bit width, the buffer is supposed to use ``uint32_t`` (i.e. align with 4 bytes), and only the high 24 bits valid, the low 8 bits are dropped.
Another point is that, for the ``8-bit`` and ``16-bit`` mono mode, the real data on the line are swapped. To get the correct sequence, the writting buffer need to swap the data every two bytes.
.. only:: esp32s2
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | ws low | ws high | ws low | ws high | ws low | ws high | ws low | ws high |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 | 0x0000 |
| 16 bit | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0000 | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0001 | 0x0001 | 0x0002 | 0x0002 | 0x0003 | 0x0003 | 0x0004 | 0x0004 |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | left | 0x0001 | 0x0001 | 0x0003 | 0x0003 | 0x0005 | 0x0005 | 0x0007 | 0x0007 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
Similar for 8-bit and 32-bit data width, the type of the buffer is better to be ``uint8_t`` and ``uint32_t`` type. But specially, when the data width is 24-bit, the data buffer should aligned with 3-byte(i.e. every 3 bytes stands for a 24-bit data in one slot), additionally, :cpp:member:`i2s_chan_config_t::dma_frame_num`, :cpp:member:`i2s_std_clk_config_t::mclk_multiple` and the writting buffer size should be the multiple of ``3``, otherwise the data on the line or the sample rate will be incorrect.
.. only:: esp32c3 or esp32s3 or esp32h2
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | ws low | ws high | ws low | ws high | ws low | ws high | ws low | ws high |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 | 0x0000 |
| 16 bit | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0000 | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0001 | 0x0001 | 0x0002 | 0x0002 | 0x0003 | 0x0003 | 0x0004 | 0x0004 |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | left | 0x0001 | 0x0001 | 0x0003 | 0x0003 | 0x0005 | 0x0005 | 0x0007 | 0x0007 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | right | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | | both | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
Similar for 8-bit and 32-bit data width, the type of the buffer is better to be ``uint8_t`` and ``uint32_t`` type. But specially, when the data width is 24-bit, the data buffer should aligned with 3-byte(i.e. every 3 bytes stands for a 24-bit data in one slot), additionally, :cpp:member:`i2s_chan_config_t::dma_frame_num`, :cpp:member:`i2s_std_clk_config_t::mclk_multiple` and the writting buffer size should be the multiple of ``3``, otherwise the data on the line or the sample rate will be incorrect.
.. code-block:: c
#include "driver/i2s_std.h"
@@ -336,6 +417,69 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
/* If the handle is not needed any more, delete it to release the channel resources */
i2s_del_channel(tx_handle);
STD RX Mode
~~~~~~~~~~~
Take 16-bit data width for example, when the data on the line are:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| ws low | ws high| ws low | ws high| ws low | ws high| ws low | ws high| ... |
+========+========+========+========+========+========+========+========+========+
| 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | ... |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
Here is the table of the data that received in the buffer with different :cpp:member:`i2s_std_slot_config_t::slot_mode` and :cpp:member:`i2s_std_slot_config_t::slot_mask`
.. only:: esp32
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0001 | 0x0000 | 0x0005 | 0x0003 | 0x0009 | 0x0007 | 0x000d | 0x000b |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| 16 bit | | right | 0x0002 | 0x0000 | 0x0006 | 0x0004 | 0x000a | 0x0008 | 0x000e | 0x000c |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | any | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
The receive case is a little bit complicated on ESP32.
Firstly, when the data width are ``8-bit`` or ``24-bit``, the received data will still align with two bytes or four bytes, which means the valid data are put in the high 8 bits in every two bytes and high 24 bits in every four bytes. For example, the received data will be ``0x5A00`` when the data on the line is ``0x5A`` in 8-bit width, and receive ``0x0000 5A00`` if the data ``0x00 005A`` on the line.
Secondly, for ``8-bit`` and ``16-bit`` mono case, the data in buffer are swapped every two data, they may need to be swapped back manually to get the correct order.
.. only:: esp32s2
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0001 | 0x0003 | 0x0005 | 0x0007 | 0x0009 | 0x000b | 0x000d | 0x000f |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| 16 bit | | right | 0x0002 | 0x0004 | 0x0006 | 0x0008 | 0x000a | 0x000c | 0x000e | 0x0010 |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | any | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
``8-bit``, ``24-bit`` and ``32-bit`` are similar as ``16-bit``, the data bit-width in the receiving buffer are equal to the data bit-width on the line. Additionally, when using ``24-bit`` data width, :cpp:member:`i2s_chan_config_t::dma_frame_num`, :cpp:member:`i2s_std_clk_config_t::mclk_multiple` and the receiving buffer size should be the multiple of ``3``, otherwise the data on the line or the sample rate will be incorrect.
.. only:: esp32c3 or esp32s3 or esp32h2
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| data bit width | slot mode | slot mask | data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 |
+================+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| | mono | left | 0x0001 | 0x0003 | 0x0005 | 0x0007 | 0x0009 | 0x000b | 0x000d | 0x000f |
| | +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| 16 bit | | right | 0x0002 | 0x0004 | 0x0006 | 0x0008 | 0x000a | 0x000c | 0x000e | 0x0010 |
| +-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | stereo | any | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
``8-bit``, ``24-bit`` and ``32-bit`` are similar as ``16-bit``, the data bit-width in the receiving buffer are equal to the data bit-width on the line. Additionally, when using ``24-bit`` data width, :cpp:member:`i2s_chan_config_t::dma_frame_num`, :cpp:member:`i2s_std_clk_config_t::mclk_multiple` and the receiving buffer size should be the multiple of ``3``, otherwise the data on the line or the sample rate will be incorrect.
.. code-block:: c
#include "driver/i2s_std.h"
@@ -397,6 +541,61 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
Please refer to :ref:`i2s-api-reference-i2s_pdm` for PDM TX API information.
And for more details, please refer to :component_file:`driver/include/driver/i2s_pdm.h`.
The PDM data width is fixed to 16-bit, when the data in a ``int16_t`` writting buffer are:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 | ... |
+========+========+========+========+========+========+========+========+========+
| 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | ... |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
.. only:: esp32
Here is the table of the real data on the line with different :cpp:member:`i2s_pdm_tx_slot_config_t::slot_mode` and :cpp:member:`i2s_pdm_tx_slot_config_t::slot_mask` (The PDM format on the line is transferred to PCM format for better comprehension).
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| slot mode | slot mask | left | right | left | right | left | right | left | right |
+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| mono | left | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 | 0x0000 |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | right | 0x0000 | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | both | 0x0001 | 0x0001 | 0x0002 | 0x0002 | 0x0003 | 0x0003 | 0x0004 | 0x0004 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| stereo | left | 0x0001 | 0x0001 | 0x0003 | 0x0003 | 0x0005 | 0x0005 | 0x0007 | 0x0007 |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | right | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | both | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. only:: esp32c3 or esp32s3 or esp32h2
Here is the table of the real data on the line with different :cpp:member:`i2s_pdm_tx_slot_config_t::slot_mode` and :cpp:member:`i2s_pdm_tx_slot_config_t::line_mode` (The PDM format on the line is transferred to PCM format for easier comprehension).
+----------------+-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
| line mode | slot mode | line | left | right | left | right | left | right | left | right |
+================+===========+======+========+========+========+========+========+========+========+========+
| | mono | dout | 0x0001 | 0x0000 | 0x0002 | 0x0000 | 0x0003 | 0x0000 | 0x0004 | 0x0000 |
| one-line codec +-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
| | stereo | dout | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+----------------+-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
| one-line dac | mono | dout | 0x0001 | 0x0001 | 0x0002 | 0x0002 | 0x0003 | 0x0003 | 0x0004 | 0x0004 |
+----------------+-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
| | mono | dout | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| | +------+--------+--------+--------+--------+--------+--------+--------+--------+
| | | dout2| 0x0000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 |
| two-line dac +-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
| | stereo | dout | 0x0002 | 0x0002 | 0x0004 | 0x0004 | 0x0006 | 0x0006 | 0x0008 | 0x0008 |
| | +------+--------+--------+--------+--------+--------+--------+--------+--------+
| | | dout2| 0x0001 | 0x0001 | 0x0003 | 0x0003 | 0x0005 | 0x0005 | 0x0007 | 0x0007 |
+----------------+-----------+------+--------+--------+--------+--------+--------+--------+--------+--------+
.. note::
There are three line modes for PDM TX mode, they are ``I2S_PDM_TX_ONE_LINE_CODEC``, ``I2S_PDM_TX_ONE_LINE_DAC`` and ``I2S_PDM_TX_TWO_LINE_DAC``. One-line codec is for the PDM codecs those require clock signal, the PDM codec can differentiate the left and right slots by the clock level, and the other two are used to driver power amplifiers directly with a low-pass filter, they do not need the clock signal, so there are two lines to differentiate the left and right slots. Additionally, for the mono mode of one-line codec, the slot can be force to change to the right by setting the clock invert flag in gpio configuration.
.. code-block:: c
#include "driver/i2s_pdm.h"
@@ -439,6 +638,44 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
Please refer to :ref:`i2s-api-reference-i2s_pdm` for PDM RX API information.
And for more details, please refer to :component_file:`driver/include/driver/i2s_pdm.h`.
The PDM data width is fixed to 16-bit, when the data on the line (The PDM format on the line is transferred to PCM format for easier comprehension) are:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| left | right | left | right | left | right | left | right | ... |
+========+========+========+========+========+========+========+========+========+
| 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | ... |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
Here is the table of the data that received in a 'int16_t' buffer with different :cpp:member:`i2s_pdm_rx_slot_config_t::slot_mode` and :cpp:member:`i2s_pdm_rx_slot_config_t::slot_mask`
.. only:: esp32
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| slot mode | slot mask | data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 |
+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| mono | left | 0x0001 | 0x0003 | 0x0005 | 0x0007 | 0x0009 | 0x000b | 0x000d | 0x000f |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | right | 0x0002 | 0x0004 | 0x0006 | 0x0008 | 0x000a | 0x000c | 0x000e | 0x0010 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| stereo | both | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. only:: esp32s3
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| slot mode | slot mask | data 0 | data 1 | data 2 | data 3 | data 4 | data 5 | data 6 | data 7 |
+===========+===========+==========+==========+==========+==========+==========+==========+==========+==========+
| mono | left | 0x0001 | 0x0003 | 0x0005 | 0x0007 | 0x0009 | 0x000b | 0x000d | 0x000f |
| +-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | right | 0x0002 | 0x0004 | 0x0006 | 0x0008 | 0x000a | 0x000c | 0x000e | 0x0010 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
| stereo | both | 0x0002 | 0x0001 | 0x0004 | 0x0003 | 0x0006 | 0x0005 | 0x0008 | 0x0007 |
+-----------+-----------+----------+----------+----------+----------+----------+----------+----------+----------+
.. note::
The right slot is received first in stereo mode. To switch the left and right slot in the buffer, please set the :cpp:member:`i2s_pdm_rx_gpio_config_t::invert_flags::clk_inv` to force invert the clock signal.
.. code-block:: c
#include "driver/i2s_pdm.h"
@@ -486,6 +723,9 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
Please refer to :ref:`i2s-api-reference-i2s_tdm` for TDM API information.
And for more details, please refer to :component_file:`driver/include/driver/i2s_tdm.h`.
TDM TX Mode
~~~~~~~~~~~
.. code-block:: c
#include "driver/i2s_tdm.h"
@@ -517,6 +757,9 @@ And for more details, please refer to :component_file:`driver/include/driver/i2s
...
TDM RX Mode
~~~~~~~~~~~
.. code-block:: c
#include "driver/i2s_tdm.h"

View File

@@ -34,24 +34,30 @@ examples/peripherals/i2c/i2c_tools:
temporary: true
reason: lack of runners
examples/peripherals/i2s:
disable:
- if: SOC_I2S_SUPPORTED != 1
examples/peripherals/i2s/i2s_adc_dac:
disable:
- if: SOC_I2S_SUPPORTS_ADC_DAC != 1
examples/peripherals/i2s/i2s_audio_recorder_sdcard:
enable:
- if: IDF_TARGET == "esp32" or IDF_TARGET == "esp32s3"
temporary: true
reason: the other targets are not tested yet
examples/peripherals/i2s/i2s_basic/i2s_pdm:
disable:
- if: SOC_I2S_SUPPORTS_PDM != 1
examples/peripherals/i2s/i2s_basic:
examples/peripherals/i2s/i2s_basic/i2s_std:
disable:
- if: SOC_I2S_SUPPORTED != 1
examples/peripherals/i2s/i2s_basic/i2s_tdm:
disable:
- if: SOC_I2S_SUPPORTS_TDM != 1
examples/peripherals/i2s/i2s_codec/i2s_es8311:
disable:
- if: SOC_I2S_SUPPORTED != 1
examples/peripherals/i2s/i2s_recorder:
disable:
- if: SOC_I2S_SUPPORTS_PDM_RX != 1
examples/peripherals/lcd/i80_controller:
disable:
- if: SOC_LCD_I80_SUPPORTED != 1

View File

@@ -1,7 +1,7 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 | ESP32-H2 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |
# I2S Basic Standard Mode Example
# I2S Basic PDM Mode Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@@ -18,68 +18,72 @@ This example is going to show how to use the PDM TX and RX mode.
#### 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`.
* A PDM microphone whose `sel` pin is supposed to be pulled down, and connecting its `clk` pin to `EXAMPLE_PDM_RX_CLK_IO`, `data` pin to `EXAMPLE_PDM_RX_DIN_IO`.
```
┌─────────────┐ ┌──────────────────┐
ESP │ │ PDM microphone │
│ │ PDM clock │ │
GPIO 0 ├──────────────►│ CLK │
│ │ PDM data │ │
GPIO 2 │◄──────────────┤ DATA │
│ │ │ │
│ │ ┌─────┤ SEL │
│ │ │ │ │
│ GND ├─────────┴─────┤ GND │
│ │ │ │
│ VCC ├───────────────┤ VCC │
└─────────────┘ └──────────────────┘
┌───────────────────────┐ ┌──────────────────┐
ESP │ │ PDM microphone │
│ PDM clock │ │
EXAMPLE_PDM_RX_CLK_IO ├──────────────►│ CLK │
│ PDM data │ │
EXAMPLE_PDM_RX_DIN_IO │◄──────────────┤ 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.
* An audio power amplifier that can input PDM signal. If the power amplifier can only receive the analog signal without PDM clock, a low-pass passive or active filter is required to restore the PDM data wave into analog signal, before it is transmitted to the power amplifier.
**MAX98358**
Please refer to the [Datasheet of MAX98358](https://datasheets.maximintegrated.com/en/ds/MAX98358.pdf) for more details.
```
┌─────────────┐ ┌───────────────┐
ESP │ │ MAX 98358 │
│ │ PDM clock │ │
GPIO 4 ├──────────────►│ CLK │ ┌─────────┐
│ │ PDM data │ │ │ Speaker │
GPIO 5 ├──────────────►│ DATA OUTP ├───┤ │
│ │ │ │ │ │
│ │ ┌─────┤ SD_MODE OUTN ├───┤ │
│ │ │ │ │ │ │
│ VCC ├─────────┴─────┤ VCC │ └─────────┘
│ │ │ │
│ GND ├───────────────┤ GND │
└─────────────┘ └───────────────┘
┌────────────────────────┐ ┌───────────────┐
ESP │ │ MAX 98358 │
│ PDM clock │ │
EXAMPLE_PDM_TX_CLK_IO ├──────────────►│ CLK │ ┌─────────┐
│ PDM data │ │ │ Speaker │
EXAMPLE_PDM_TX_DOUT_IO ├──────────────►│ DATA OUTP ├───┤ │
│ │ │ │ │
│ ┌─────┤ SD_MODE OUTN ├───┤ │
│ │ │ │ │ │
VCC ├─────────┴─────┤ VCC │ └─────────┘
│ │ │
GND ├───────────────┤ GND │
└────────────────────────┘ └───────────────┘
```
**NS4150**
Please refer to the [Datasheet of NS4150](http://www.nsiway.com.cn/product/44.html) for more details.
```
┌─────────────┐ ┌───────────────┐
ESP │ │ NS 4150 │
│ │ │ │
GPIO 4 │ │ INN │ ┌─────────┐
│ │PDM data┌────────────────┐ │ │ │ Speaker │
GPIO 5 ├────────┤Band-pass Filter├───►│ INP VoP ├───┤ │
│ │ └────────────────┘ │ │ │ │
│ │ ┌───┤ CTRL VoN ├───┤ │
│ │ │ │ │ │ │
│ VCC ├──────────────────────────┴───┤ VCC │ └─────────┘
│ │ │ │
│ GND ├──────────────────────────────┤ GND │
└─────────────┘ └───────────────┘
┌────────────────────────┐ ┌───────────────┐
ESP │ │ NS 4150 │
│ │ │
EXAMPLE_PDM_TX_CLK_IO │(No need to connect) │ INN │ ┌─────────┐
│PDM data┌────────────────┐ │ │ │ Speaker │
EXAMPLE_PDM_TX_DOUT_IO ├────────┤ Low-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.
PDM can only works in simplex mode, you can select the PDM direction in the menu config, or just setting the macro `EXAMPLE_PDM_DIR` directly. Setting it 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
@@ -113,7 +117,7 @@ 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.
You can hear the audio 'twinkle twinkle little star' in three tones if you connect a speaker to it.
### PDM RX
@@ -141,7 +145,7 @@ Read Task: i2s read 2048 bytes
[4] -30935 [5] -30935 [6] -30935 [7] -30935
```
And only if you connect a PDM microphone, you can see the data is change:
And only if you connect a PDM microphone, you can see the data changes:
```
I2S PDM RX example start

View File

@@ -0,0 +1,22 @@
menu "I2S PDM Example Configuration"
choice EXAMPLE_PDM_DIR
prompt "I2S PDM direction"
default EXAMPLE_PDM_TX
help
Select example PDM direction
config EXAMPLE_PDM_TX
bool "PDM TX"
help
PDM TX example will play 'twinkle twinkle little star' in three tones.
config EXAMPLE_PDM_RX
bool "PDM RX"
# ESP32-C3 not support PDM RX for now, its hardware does not fully supported PDM RX mode
depends on !IDF_TARGET_ESP32C3
help
PDM RX example will show the received data from a PDM microphone.
endchoice
endmenu

View File

@@ -6,6 +6,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define EXAMPLE_BUFF_SIZE 2048
/**
@@ -21,3 +25,7 @@ void i2s_example_pdm_tx_task(void *args);
* @param args The user data given from task creating, not used in this example
*/
void i2s_example_pdm_rx_task(void *args);
#ifdef __cplusplus
}
#endif

View File

@@ -11,20 +11,12 @@
#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
#if CONFIG_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
#elif CONFIG_EXAMPLE_PDM_RX
printf("I2S PDM RX example start\n---------------------------\n");
xTaskCreate(i2s_example_pdm_rx_task, "i2s_example_pdm_rx_task", 4096, NULL, 5, NULL);
#endif

View File

@@ -9,23 +9,20 @@
#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 "esp_err.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)
static i2s_chan_handle_t i2s_example_init_pdm_rx(void)
{
i2s_chan_handle_t rx_chan; // I2S rx channel handler
/* 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 */
@@ -52,6 +49,7 @@ static void i2s_example_init_pdm_rx(void)
/* Step 3: Enable the rx channels before reading data */
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
return rx_chan;
}
@@ -59,7 +57,7 @@ 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();
i2s_chan_handle_t rx_chan = i2s_example_init_pdm_rx();
size_t r_bytes = 0;
while (1) {

View File

@@ -24,21 +24,23 @@
#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.
/* The frequency of tones: do, re, mi, fa, so, la, si, in Hz. */
static const uint32_t tone[3][7] = {{262, 294, 330, 349, 392, 440, 494}, // bass
{523, 587, 659, 698, 784, 880, 988}, // alto
{1046, 1175, 1318, 1397, 1568, 1760, 1976}}; // treble
/* Numbered musical notation of 'twinkle twinkle little star' */
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
5, 5, 4, 4, 3, 3, 2};
/* Rhythm of 'twinkle twinkle little star', it's repeated in four sections */
static const uint8_t rhythm[7] = {1, 1, 1, 1, 1, 1, 2};
static const char *tone_name[3] = {"bass", "alto", "treble"};
void i2s_example_init_pdm_tx(void)
static i2s_chan_handle_t i2s_example_init_pdm_tx(void)
{
i2s_chan_handle_t tx_chan; // I2S tx channel handler
/* 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,
@@ -67,13 +69,15 @@ void i2s_example_init_pdm_tx(void)
/* Step 3: Enable the tx channel before writing data */
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
return 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();
i2s_chan_handle_t tx_chan = i2s_example_init_pdm_tx();
size_t w_bytes = 0;

View File

@@ -9,7 +9,12 @@ from pytest_embedded import Dut
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.generic
def test_i2s_pdm_example(dut: Dut) -> None:
@pytest.mark.parametrize(
'config',
['pdm_tx'],
indirect=True
)
def test_i2s_pdm_tx_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)
@@ -20,3 +25,24 @@ def test_i2s_pdm_example(dut: Dut) -> None:
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)
@pytest.mark.esp32
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
['pdm_rx'],
indirect=True
)
def test_i2s_pdm_rx_example(dut: Dut) -> None:
dut.expect(r'I2S PDM RX example start', timeout=5)
dut.expect(r'---------------------------', 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_common: i2s rx channel enabled', timeout=5)
dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5)
dut.expect(r'-----------------------------------', timeout=5)
dut.expect(r'\[0\] ([-]?[0-9]+) \[1\] ([-]?[0-9]+) \[2\] ([-]?[0-9]+) \[3\] ([-]?[0-9]+)', timeout=5)
dut.expect(r'\[4\] ([-]?[0-9]+) \[5\] ([-]?[0-9]+) \[6\] ([-]?[0-9]+) \[7\] ([-]?[0-9]+)', timeout=5)

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | ESP32-H2 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
# I2S Basic Standard Mode Example
@@ -16,7 +16,7 @@ This example is going to show how to use the standard mode in simplex mode or fu
### 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.
There are simplex mode and duplex mode can be chosen in this example, you can choose the mode in the menuconfig or just setting `EXAMPLE_I2S_DUPLEX_MODE` derectly, when `EXAMPLE_I2S_DUPLEX_MODE` is `0` the example 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.

View File

@@ -0,0 +1,21 @@
menu "I2S STD Example Configuration"
choice DUPLEX_MODE
prompt "I2S STD duplex/simplex select"
default USE_DUPLEX
help
Select duplex mode or simplex mode for the example
config USE_DUPLEX
bool "Duplex TX and RX channels"
help
Allocate TX and RX channels on a same I2S controller in duplex mode, sharing the BCLK and WS signal
config USE_SIMPLEX
bool "Simplex TX and RX channels"
depends on !IDF_TARGET_ESP32S2
help
Allocate TX and RX channels in duplex mode, they are totally separate.
endchoice
endmenu

View File

@@ -17,7 +17,7 @@
* 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)
#define EXAMPLE_I2S_DUPLEX_MODE CONFIG_USE_DUPLEX
#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_STD_BCLK_IO1 GPIO_NUM_4 // I2S bit clock io number
@@ -187,7 +187,7 @@ static void i2s_example_init_std_simplex(void)
};
/* 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;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
}
#endif

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32-C3 | ESP32-S3 | ESP32-H2 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C3 | ESP32-S3 |
| ----------------- | -------- | -------- |
# I2S Basic TDM Mode Example

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 | ESP32-H2 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
# I2S ES8311 Example
@@ -34,21 +34,21 @@ For more details, see [ES8311 datasheet](http://www.everest-semi.com/pdf/ES8311%
┌─────────────────┐ ┌──────────────────────────┐
│ ESP │ │ ES8311 │
│ │ │ │
MCLK-GPIO 0 ├──────────►│PIN2-MCLK │
I2S_MCK_IO├──────────►│PIN2-MCLK │
│ │ │ │ ┌─────────┐
BCLK-GPIO 4 ├──────────►│PIN6-BCLK PIN12-OUTP├───────────┤ │
I2S_BCK_IO├──────────►│PIN6-BCLK PIN12-OUTP├───────────┤ │
│ │ │ │ │ EARPHONE│
WS-GPIO 5 ├──────────►│PIN8-LRCK PIN13-OUTN├───────────┤ │
I2S_WS_IO├──────────►│PIN8-LRCK PIN13-OUTN├───────────┤ │
│ │ │ │ └─────────┘
SDOUT-GPIO 18├──────────►│PIN9-SDIN │
(GPIO 2)│ │ │
SDIN-GPIO 19│◄──────────┤PIN7-SDOUT │
(GPIO 3)│ │ │ ┌─────────┐
I2S_DO_IO├──────────►│PIN9-SDIN │
│ │ │
I2S_DI_IO│◄──────────┤PIN7-SDOUT │
│ │ │ ┌─────────┐
│ │ │ PIN18-MIC1P├───────────┤ │
SCL-GPIO 16├──────────►│PIN1 -CCLK │ │ MIC │
(GPIO 6)│ │ PIN17-MIC1N├───────────┤ │
SDA-GPIO 17│◄─────────►│PIN19-CDATA │ └─────────┘
(GPIO 7)│ │ │
I2C_SCL_IO├──────────►│PIN1 -CCLK │ │ MIC │
│ │ PIN17-MIC1N├───────────┤ │
I2C_SDA_IO│◄─────────►│PIN19-CDATA │ └─────────┘
│ │ │
│ VCC 3.3├───────────┤VCC │
│ │ │ │
│ GND├───────────┤GND │

View File

@@ -36,9 +36,9 @@
#define I2S_DI_IO (GPIO_NUM_19)
#endif
/* Example configurations */
#define EXAMPLE_RECV_BUF_SIZE (2048)
#define EXAMPLE_RECV_BUF_SIZE (2400)
#define EXAMPLE_SAMPLE_RATE (16000)
#define EXAMPLE_MCLK_MULTIPLE (256)
#define EXAMPLE_MCLK_MULTIPLE (384) // If not using 24-bit data width, 256 should be enough
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
#define EXAMPLE_VOICE_VOLUME CONFIG_EXAMPLE_VOICE_VOLUME
#if CONFIG_EXAMPLE_MODE_ECHO
@@ -114,6 +114,7 @@ static esp_err_t i2s_driver_init(void)
},
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));

View File

@@ -3,7 +3,7 @@ dependencies:
espressif/es8311: "==1.0.0"
## Required IDF version
idf:
version: ">=4.1.0"
version: "^5.0"
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"

View File

@@ -168,7 +168,6 @@ components/soc/esp32s2/include/soc/efuse_struct.h
components/soc/esp32s2/include/soc/gpio_sd_struct.h
components/soc/esp32s2/include/soc/gpio_struct.h
components/soc/esp32s2/include/soc/i2c_struct.h
components/soc/esp32s2/include/soc/i2s_struct.h
components/soc/esp32s2/include/soc/ledc_struct.h
components/soc/esp32s2/include/soc/rtc_i2c_struct.h
components/soc/esp32s2/include/soc/rtc_io_struct.h
@@ -201,7 +200,6 @@ components/soc/esp32c3/include/soc/apb_ctrl_struct.h
components/soc/esp32c3/include/soc/apb_saradc_struct.h
components/soc/esp32c3/include/soc/efuse_struct.h
components/soc/esp32c3/include/soc/gpio_sd_struct.h
components/soc/esp32c3/include/soc/i2s_struct.h
components/soc/esp32c3/include/soc/ledc_struct.h
components/soc/esp32c3/include/soc/rtc_cntl_struct.h
components/soc/esp32c3/include/soc/rtc_i2c_struct.h
@@ -213,12 +211,6 @@ components/soc/esp32c3/include/soc/uhci_struct.h
### To be fixed: files which don't compile for esp32c2 target:
components/driver/deprecated/driver/i2s_types_legacy.h
components/driver/deprecated/driver/i2s.h
components/driver/include/driver/i2s_common.h
components/driver/include/driver/i2s_pdm.h
components/driver/include/driver/i2s_std.h
components/driver/include/driver/i2s_tdm.h
components/efuse/esp32c2/include/esp_efuse_table.h
components/soc/esp32c2/include/soc/rtc_cntl_struct.h
components/soc/esp32c2/include/soc/spi_mem_struct.h