Skip to content

Commit

Permalink
Merge branch 'feature/add_default_config_for_i2s_pdm_dac_mode' into '…
Browse files Browse the repository at this point in the history
…master'

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

Closes IDF-7289

See merge request espressif/esp-idf!23466
  • Loading branch information
L-KAYA committed May 8, 2023
2 parents 38e84bd + bf8419f commit e9e5f1b
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 23 deletions.
6 changes: 4 additions & 2 deletions components/driver/i2s/i2s_pdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -318,13 +319,14 @@ esp_err_t i2s_channel_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_p
---------------------------------------------------------------*/

#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;
Expand Down
58 changes: 54 additions & 4 deletions components/driver/i2s/include/driver/i2s_pdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, \
}

/**
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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
*/
Expand All @@ -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
*/
Expand All @@ -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
Expand All @@ -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, \
}

/*
Expand Down Expand Up @@ -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;

/**
Expand Down
9 changes: 0 additions & 9 deletions examples/peripherals/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
10 changes: 5 additions & 5 deletions examples/peripherals/i2s/i2s_basic/i2s_pdm/README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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.

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

Expand Down
Original file line number Diff line number Diff line change
@@ -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()

Expand Down
24 changes: 24 additions & 0 deletions examples/peripherals/i2s/i2s_basic/i2s_pdm/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions examples/peripherals/i2s/i2s_basic/i2s_pdm/main/i2s_pdm_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c6
# @pytest.mark.esp32h2 IDF-6808
@pytest.mark.esp32h2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
Expand Down

0 comments on commit e9e5f1b

Please sign in to comment.