I2S Slave mode with external MCLK

Volvox
Posts: 19
Joined: Sat Mar 18, 2017 7:54 pm

I2S Slave mode with external MCLK

Postby Volvox » Fri Jun 26, 2020 6:04 pm

Hello,
I am trying to get the I2S_MODE_SLAVE working with having an external DAC providing the MCLK.
This is working, but only with heavy fragmented noise on the Signal, it seems to result because the external provided MCLK cannot be synchronized with the ESPs internal Clock.

Does anybody has a solution or a workaround for this? I tried it with apll on and off, but this results only in a different noise ..
The audio noise does not come from an improper layout, the signals edges are really clear on the logic analyser but "noisy"/wrong content on the data line

This is my driver setup, the external clock is a 12.288MHz:

Code: Select all

define I2S_MYSTREAM_CFG() {  	                                                \
    .type = AUDIO_STREAM_WRITER,                                                \
    .task_prio = I2S_STREAM_TASK_PRIO,                                          \
    .task_core = 1,                                          \
    .task_stack = I2S_STREAM_TASK_STACK,                                        \
    .out_rb_size = 24*1024,		                            \
    .i2s_config = {                                                             \
        .mode = I2S_MODE_SLAVE | I2S_MODE_TX,				                    \
        .sample_rate = 48000,                                                   \
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,                                                  \
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           \
        .communication_format = I2S_COMM_FORMAT_I2S,                             \
        .dma_buf_count = 3,                                                     \
        .dma_buf_len = 300,                                                     \
        .use_apll = 1,                                                          \
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,                               \
    },                                                                          \
    .i2s_port = 0,     
Thanks!

embedded-creations
Posts: 13
Joined: Thu Feb 15, 2018 11:22 am

Re: I2S Slave mode with external MCLK

Postby embedded-creations » Thu Dec 17, 2020 11:40 am

I'm looking for working I2S Slave example code and striking out... My guess on your issue is that you're trying to clock the data out too fast. I think ESP32 GPIO can only handle around 7MHz (I don't have the figure handy), unless you're using a special pin mapped directly to a peripheral. Have you tried lowering the clock and do you get the data you expect?

Volvox
Posts: 19
Joined: Sat Mar 18, 2017 7:54 pm

Re: I2S Slave mode with external MCLK

Postby Volvox » Thu Dec 17, 2020 12:27 pm

I am not sure if I understand you correctly, but the I2S is a peripheral and the GPIOs are surely mapped to it.
I think I can not change the clock, because its audio, 44100 or 48000 is the necessary fs :)
And in Slave mode we have no signal faster than BCLK (for example: 48k * 64 bit = 3072kHz or 1/2 if using 16 bit audio per channel). That is far below 7MHz on a common fs.
(The external MCLK from the codec or AD/DA cannot be synchronized with the ESP I think when ESP must be be master, so it seems this is the problem)
I read somewhere (don't know where, too long ago) that I2S Slave mode is simply not working as expected with external MCLK.
So I have no solution until now for the I2S Slave. In my solution I now use a very expensive external SRC IC to reclock the whole audio... not the best solution, but the only possible one here it seems.

If you figured something out that works it would be a pleasure to share it here!

escalator
Posts: 5
Joined: Tue May 29, 2018 11:01 am

Re: I2S Slave mode with external MCLK

Postby escalator » Sun Jan 03, 2021 1:25 pm

  1.  
  2.         audio_element_handle_t i2s_stream_writer = NULL;
  3.         ESP_LOGI(TAG, "[4] Create i2s stream to write data to codec chip");
  4.         i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
  5.         i2s_cfg.i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
  6.         i2s_cfg.type = AUDIO_STREAM_WRITER;
  7.         i2s_cfg.i2s_config.sample_rate = 48000;
  8.         i2s_cfg.i2s_config.bits_per_sample = 16;//
  9.         i2s_cfg.i2s_config.mode = I2S_MODE_SLAVE | I2S_MODE_TX;//i2s slave + only transmit
  10.         i2s_cfg.i2s_config.communication_format =  I2S_COMM_FORMAT_STAND_I2S;
  11.         i2s_cfg.i2s_config.use_apll = 1;//use audio pll as clock generator for i2s
  12.         i2s_cfg.i2s_config.fixed_mclk = 12288000;//THIS IS MANDATORY: GENERATE MCLK ON GPIO 0 FROM ESP32 TO THE DAC
  13.         i2s_stream_writer = i2s_stream_init(&i2s_cfg);
This way you dont need an external xtal or oscillator for your DAC, the ESP32 will generate it for you, the MCLK comes from apll clock and it is fixed and rock solid and you can omit the external src. DAC is i2s master (LRclk and Bclk come from DAC) , esp32 i2s is slave, both clocked by this MCLK.
If you need to switch from different audio sources (like using Bluetooth or sd card files with different sample rates you can use the software rate converter in esp-adf as audio element just before the i2s writer element with a fixed 48000kz sample rate so you dont need to change the mclk or the sample rate of your i2s writer audio element on the fly.
The mclk output from esp32 is independent of i2s being slave or master so it can be used as master clock for all your digital audio devices.
It uses GPIO 0 by default as MCLK out (but you can also change the output gpio pin for it if needed). If you want to use a different sample rate modify "i2s_cfg.i2s_config.fixed_mclk" line with the right value (sample rate x 256).

Volvox
Posts: 19
Joined: Sat Mar 18, 2017 7:54 pm

Re: I2S Slave mode with external MCLK

Postby Volvox » Sun Jan 03, 2021 2:06 pm

Thanks for sharing,
Yes, that is clear to use the APLL for generating a synchronous MCLK. Then Slave and Master modes will work.
My problem was that I need to synchronize a mandatory external MCLK from a second device to the ESP I2S peripheral. That seems not to be possible. The I2S in ESP32 works only properly if the MCLK is synchronized to the internal clock.

Vladislav Knyazkov
Posts: 4
Joined: Sun Apr 16, 2017 12:46 pm

Re: I2S Slave mode with external MCLK

Postby Vladislav Knyazkov » Thu Jan 14, 2021 2:52 pm

Then you'll need to generate BCLK and WCLK for esp32 too.
I did that with help of FPGA, that works.

I'm not sure if ESP32 can sync to external MCLK and generate BCLK and WCLK internally, AFAIK there was a bug in the chip with this mode. Please double check ERRATA.
Best Regards,
Vladislav

Senior ASIC designer at SiTime

Volvox
Posts: 19
Joined: Sat Mar 18, 2017 7:54 pm

Re: I2S Slave mode with external MCLK

Postby Volvox » Fri Mar 22, 2024 8:30 pm

Long time ago. We realized this issue with a (in fact expensive) sample rate converter IC due to the fact that there was no solution to that issue.
But what I discovered now is that with the SOC_I2S_HW_VERSION_2 Capability it seems that the ESP is able to sync to external MCLK. This seems to be possible for the ESP32-S3 and ESP32-H2.

Can anybody from Espressif tell me if it is now possible to sync the I2S peripheral to external I2S Master with Hardware S3 or H2?

I found this in IDF master today: (esp-idf/components/esp_driver_i2s/i2s_common.c)

Code: Select all

#if CONFIG_IDF_TARGET_ESP32
    bool is_i2s0 = id == I2S_NUM_0;
    bool is_apll = clk_src == I2S_CLK_SRC_APLL;
    if (g_i2s.controller[id]->mclk_out_hdl == NULL) {
        soc_clkout_sig_id_t clkout_sig = is_apll ? CLKOUT_SIG_APLL : (is_i2s0 ? CLKOUT_SIG_I2S0 : CLKOUT_SIG_I2S1);
        ESP_RETURN_ON_ERROR(esp_clock_output_start(clkout_sig, gpio_num, &(g_i2s.controller[id]->mclk_out_hdl)), TAG, "mclk configure failed");
    }
#else
    ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid");
#if SOC_I2S_HW_VERSION_2
    if (clk_src == I2S_CLK_SRC_EXTERNAL) {
        i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_in_sig, true, is_invert);
    } else
#endif  // SOC_I2S_HW_VERSION_2
    {
        i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, false, is_invert);
    }
#endif  // CONFIG_IDF_TARGET_ESP32
    ESP_LOGD(TAG, "MCLK is pinned to GPIO%d on I2S%d", gpio_num, id);
    return ESP_OK;
}

Who is online

Users browsing this forum: No registered users and 33 guests