Merge branch 'feature/add_default_config_for_i2s_pdm_dac_mode' into 'master'

i2s: public bclk_div and add a default config for PDM TX DAC

Closes IDF-7289

See merge request espressif/esp-idf!23466
This commit is contained in:
Kevin (Lao Kaiyao) 2023-05-08 12:36:05 +08:00
commit e9e5f1b90f
8 changed files with 96 additions and 23 deletions

View File

@ -30,6 +30,7 @@ static const char *TAG = "i2s_pdm";
---------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_PDM_TX
#define I2S_PDM_TX_BCLK_DIV_MIN 8 /*!< The minimum bclk_div for PDM TX mode */
static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
{
uint32_t rate = clk_cfg->sample_rate_hz;
@ -38,7 +39,7 @@ static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_
// Over sampling ratio (integer, mostly should be 1 or 2)
uint32_t over_sample_ratio = pdm_tx_clk->up_sample_fp / pdm_tx_clk->up_sample_fs;
clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * over_sample_ratio;
clk_info->bclk_div = 8;
clk_info->bclk_div = clk_cfg->bclk_div < I2S_PDM_TX_BCLK_DIV_MIN ? I2S_PDM_TX_BCLK_DIV_MIN : clk_cfg->bclk_div;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
@ -318,13 +319,14 @@ err:
---------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_PDM_RX
#define I2S_PDM_RX_BCLK_DIV_MIN 8 /*!< The minimum bclk_div for PDM RX mode */
static esp_err_t i2s_pdm_rx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
{
uint32_t rate = clk_cfg->sample_rate_hz;
i2s_pdm_rx_clk_config_t *pdm_rx_clk = (i2s_pdm_rx_clk_config_t *)clk_cfg;
clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * (pdm_rx_clk->dn_sample_mode == I2S_PDM_DSR_16S ? 2 : 1);
clk_info->bclk_div = 8;
clk_info->bclk_div = clk_cfg->bclk_div < I2S_PDM_RX_BCLK_DIV_MIN ? I2S_PDM_RX_BCLK_DIV_MIN : clk_cfg->bclk_div;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;

View File

@ -44,7 +44,8 @@ extern "C" {
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.dn_sample_mode = I2S_PDM_DSR_8S \
.dn_sample_mode = I2S_PDM_DSR_8S, \
.bclk_div = 8, \
}
/**
@ -69,6 +70,8 @@ typedef struct {
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
/* Particular fields */
i2s_pdm_dsr_t dn_sample_mode; /*!< Down-sampling rate mode */
uint32_t bclk_div; /*!< The division from mclk to bclk. The typical and minimum value is I2S_PDM_RX_BCLK_DIV_MIN.
* It will be set to I2S_PDM_RX_BCLK_DIV_MIN by default if it is smaller than I2S_PDM_RX_BCLK_DIV_MIN */
} i2s_pdm_rx_clk_config_t;
/**
@ -165,7 +168,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
#if SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_HW_VERSION_2
/**
* @brief PDM style in 2 slots(TX)
* @brief PDM style in 2 slots(TX) for codec line mode
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
@ -184,9 +187,33 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
.sd_dither = 0, \
.sd_dither2 = 1, \
}
/**
* @brief PDM style in 1 slots(TX) for DAC line mode
* @note The noise might be different with different configurations, this macro provides a set of configurations
* that have relatively high SNR (Signal Noise Ratio), you can also adjust them to fit your case.
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_PDM_TX_SLOT_DAC_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, \
.sd_prescale = 0, \
.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.line_mode = ((mono_or_stereo) == I2S_SLOT_MODE_MONO ? \
I2S_PDM_TX_ONE_LINE_DAC : I2S_PDM_TX_TWO_LINE_DAC), \
.hp_en = true, \
.hp_cut_off_freq_hz = 35.5, \
.sd_dither = 0, \
.sd_dither2 = 1, \
}
#else // SOC_I2S_HW_VERSION_2
/**
* @brief PDM style in 2 slots(TX)
* @brief PDM style in 2 slots(TX) for codec line mode
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
@ -204,7 +231,7 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
#endif // SOC_I2S_HW_VERSION_2
/**
* @brief i2s default pdm tx clock configuration
* @brief i2s default pdm tx clock configuration for codec line mode
* @note TX PDM can only be set to the following two up-sampling rate configurations:
* 1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000
* 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz
@ -218,6 +245,27 @@ esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_p
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.up_sample_fp = 960, \
.up_sample_fs = 480, \
.bclk_div = 8, \
}
/**
* @brief i2s default pdm tx clock configuration for DAC line mode
* @note TX PDM can only be set to the following two up-sampling rate configurations:
* 1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000
* 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz
* If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000.
* Otherwise, the second configuration should be adopted.
* @note The noise might be different with different configurations, this macro provides a set of configurations
* that have relatively high SNR (Signal Noise Ratio), you can also adjust them to fit your case.
* @param rate sample rate (not suggest to exceed 48000 Hz, otherwise more glitches and noise may appear)
*/
#define I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.up_sample_fp = 960, \
.up_sample_fs = (rate) / 100, \
.bclk_div = 13, \
}
/*
@ -275,6 +323,8 @@ typedef struct {
/* Particular fields */
uint32_t up_sample_fp; /*!< Up-sampling param fp */
uint32_t up_sample_fs; /*!< Up-sampling param fs, not allowed to be greater than 480 */
uint32_t bclk_div; /*!< The division from mclk to bclk. The minimum value is I2S_PDM_TX_BCLK_DIV_MIN.
* It will be set to I2S_PDM_TX_BCLK_DIV_MIN by default if it is smaller than I2S_PDM_TX_BCLK_DIV_MIN */
} i2s_pdm_tx_clk_config_t;
/**

View File

@ -45,9 +45,6 @@ examples/peripherals/i2s/i2s_adc_dac:
examples/peripherals/i2s/i2s_basic/i2s_pdm:
disable:
- if: SOC_I2S_SUPPORTS_PDM != 1
- if: IDF_TARGET == "esp32h2"
temporary: true
reason: rtc timer is not supported
examples/peripherals/i2s/i2s_basic/i2s_std:
disable:
@ -290,9 +287,3 @@ examples/peripherals/uart/uart_echo_rs485:
examples/peripherals/usb:
disable:
- if: SOC_USB_PERIPH_NUM != 1
examples/peripherals/wave_gen:
enable:
- if: IDF_TARGET == "esp32"
temporary: true
reason: the other targets are not tested yet

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
# I2S Basic PDM Mode Example
@ -99,9 +99,9 @@ This example is going to show how to use the PDM TX and RX mode.
#### 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 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.
* An audio power amplifier that can input PDM signal. If the power amplifier can only receive the analog signal without PDM clock (i.e. DAC line mode, otherwise codec line mode), 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**
**MAX98358 (codec case)**
Please refer to the [Datasheet of MAX98358](https://datasheets.maximintegrated.com/en/ds/MAX98358.pdf) for more details.
@ -121,7 +121,7 @@ Please refer to the [Datasheet of MAX98358](https://datasheets.maximintegrated.c
└────────────────────────┘ └───────────────┘
```
**NS4150**
**NS4150 (dac case)**
Please refer to the NS4150 datasheet for more details.

View File

@ -1,10 +1,10 @@
set(srcs "i2s_pdm_example_main.c")
if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX)
if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX AND CONFIG_EXAMPLE_PDM_TX)
list(APPEND srcs "i2s_pdm_tx.c")
endif()
if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX)
if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX AND CONFIG_EXAMPLE_PDM_RX)
list(APPEND srcs "i2s_pdm_rx.c")
endif()

View File

@ -19,4 +19,28 @@ menu "I2S PDM Example Configuration"
PDM RX example will show the received data from a PDM microphone.
endchoice
choice EXAMPLE_PDM_TX_LINE_MODE
prompt "I2S PDM TX Line Mode"
depends on EXAMPLE_PDM_TX && SOC_I2S_HW_VERSION_2
default EXAMPLE_PDM_TX_CODEC
help
Decide to output PDM signal into a PDM codec or a low-pass filter
config EXAMPLE_PDM_TX_CODEC
bool "Codec line mode"
help
Output PDM signal to a PDM codec. The PDM clock signal is mandatory for PDM codec,
the codec can differentiate the left and right sound channels by sampling data
on positive or negative edges. That means the data of the left and right channels
can coexist on a same data line.
config EXAMPLE_PDM_TX_DAC
bool "DAC line mode"
help
Output PDM signal to a low-pass filter, so that the low-pass filter can restore the PDM
signal to analog wave. Therefore, each data line can only contains one sound channel,
if both left and right channels are required, two data lines should be specified as well.
Normally the PDM signal is not sufficient in DAC line mode.
endchoice
endmenu

View File

@ -54,9 +54,15 @@ static i2s_chan_handle_t i2s_example_init_pdm_tx(void)
* These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM TX mode.
* They can help to specify the slot and clock configurations for initialization or re-configuring */
i2s_pdm_tx_config_t pdm_tx_cfg = {
#if CONFIG_EXAMPLE_PDM_TX_DAC
.clk_cfg = I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
/* The data bit-width of PDM mode is fixed to 16 */
.slot_cfg = I2S_PDM_TX_SLOT_DAC_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
#else
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
/* The data bit-width of PDM mode is fixed to 16 */
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
#endif
.gpio_cfg = {
.clk = EXAMPLE_PDM_TX_CLK_IO,
.dout = EXAMPLE_PDM_TX_DOUT_IO,

View File

@ -9,7 +9,7 @@ from pytest_embedded import Dut
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c6
# @pytest.mark.esp32h2 IDF-6808
@pytest.mark.esp32h2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',