How to use i2s in full-duplex mode

andrej32
Posts: 28
Joined: Fri Apr 19, 2019 8:22 am

How to use i2s in full-duplex mode

Postby andrej32 » Wed Sep 16, 2020 10:39 am

Hello. How to use i2s in full-duplex mode?
I want to use ESP 32 with an external audio codec chip, but only manage to work in half-duplex mode.
I tried a configuration with two i2cs, but the transfer on slave I2C 1 does not work.
It is assumed that both I2S use the same connection lines to the audio codec, but I2S0 leads and controls the outputs of WS and BCK and uses DATA_IN, and I2S1 takes as inputs WS and BCK and controls the output DATA_OUT.

[Codebox]
void ConfigI2S (void)
{
// top mic - RIGHT, lower mic - LEFT

i2s_config_t i2s_config;
i2s_pin_config_t pin_config;

// config I2S0, as TX

i2s_config.mode = I2S_MODE_SLAVE | I2S_MODE_TX;
i2s_config.sample_rate = AUDIO_SAMPLING_FREQUENCY;
i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
i2s_config.communication_format = I2S_COMM_FORMAT_I2S;
i2s_config.dma_buf_count = 4;
i2s_config.dma_buf_len = 256;
i2s_config.use_apll = 0; //apll enable
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //interrupt level 1(lowest priority)
i2s_config.fixed_mclk =4096000;

pin_config.bck_io_num = BCK;
pin_config.ws_io_num = WS;
pin_config.data_out_num = I2S_PIN_NO_CHANGE;
pin_config.data_in_num = I2SIN;

i2s_driver_install(I2S_NUM_1, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_1, &pin_config);

// config I2S1, as RX

i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_RX;
i2s_config.sample_rate = AUDIO_SAMPLING_FREQUENCY;
i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
i2s_config.communication_format = I2S_COMM_FORMAT_I2S;
i2s_config.dma_buf_count = 4;
i2s_config.dma_buf_len = 256;
i2s_config.use_apll = 1; //apll enable
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //interrupt level 1(lowest priority)
i2s_config.fixed_mclk = 4096000;

pin_config.bck_io_num = BCK;
pin_config.ws_io_num = WS;
pin_config.data_out_num = I2SOUT;
pin_config.data_in_num = I2S_PIN_NO_CHANGE;

i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);

i2s_start(I2S_NUM_1);
i2s_start(I2S_NUM_0);
}
[/Codebox]

The program performs two tasks, one reads from I2S0, and the second writes to I2S1. I assumed that full-duplex data exchange, in which I2 S1 puts data on the bus at the moment when I2S0 takes data from the bus (while controlling the WS and BCK signals).
But this mechanism does not work, in particular, data on I2S 1 is not sent, the function

[Codebox]
i2c_write (I2S_NUM_1, ... , ... , ... , portMAX_DELAY)
[/Codebox]

is never returned;

Plese help me start I2S in full duplex mode/

ESP_Sprite
Posts: 8921
Joined: Thu Nov 26, 2015 4:08 am

Re: How to use i2s in full-duplex mode

Postby ESP_Sprite » Wed Sep 16, 2020 1:40 pm

Not sure where you got the idea that you need both I2S devices to get full-duplex... each of the two I2S peripherals is fully duplex by itself.

andrej32
Posts: 28
Joined: Fri Apr 19, 2019 8:22 am

Re: How to use i2s in full-duplex mode

Postby andrej32 » Thu Sep 17, 2020 5:49 am

ESP_Sprite wrote:
Wed Sep 16, 2020 1:40 pm
Not sure where you got the idea that you need both I2S devices to get full-duplex... each of the two I2S peripherals is fully duplex by itself.
https://docs.espressif.com/projects/esp ... s/i2s.html

"Each controller can operate in half-duplex communication mode. Thus, the two controllers can be combined to establish full-duplex communication."

ESP_Sprite
Posts: 8921
Joined: Thu Nov 26, 2015 4:08 am

Re: How to use i2s in full-duplex mode

Postby ESP_Sprite » Thu Sep 17, 2020 8:36 am

Hm, interesting as the hardware can run full-duplex... perhaps a limitation of the driver.

andrej32
Posts: 28
Joined: Fri Apr 19, 2019 8:22 am

Re: How to use i2s in full-duplex mode

Postby andrej32 » Fri Sep 18, 2020 8:54 am

ESP_Sprite wrote:
Thu Sep 17, 2020 8:36 am
Hm, interesting as the hardware can run full-duplex... perhaps a limitation of the driver.
I'm as surprised as you are. Using I2S for full-duplex audio data transfer to / from an external codec is an obvious necessity. But I didn't find any examples and no answers to the question how to use I2S in full-duplex mode in IDF-ESP.
If anyone knows how to do this, please tell me.

zxperi
Posts: 1
Joined: Wed Nov 25, 2020 6:42 pm

Re: How to use i2s in full-duplex mode

Postby zxperi » Wed Nov 25, 2020 6:48 pm

Should you not be using i2s_write rather than i2c_write? https://docs.espressif.com/projects/esp ... /i2s.html

Thanks for posting the code for making the full duplex out of the two ports, I was looking for just that, it looks like it should work

Don Warr
Posts: 11
Joined: Fri Oct 19, 2018 10:01 am

Re: How to use i2s in full-duplex mode

Postby Don Warr » Sat Feb 27, 2021 9:22 am

i2s_write rather than i2c_write... Can you reply if your code worked...

If anyone knows how to get Full Duplex to work,,,, Can you post an answer...

biterror
Posts: 30
Joined: Thu Apr 30, 2020 11:00 am

Re: How to use i2s in full-duplex mode

Postby biterror » Sun Jun 06, 2021 8:39 am

Does anyone here know how to use I2S in full duplex mode? I'm on idf-v4.2 and when trying to use one I2S for both RX and TX, only TX works (i2s_read() never returns any data). If I make no other changes in code but remove I2S_MODE_TX from the mode setting, RX begins to work. :roll: Somewhere I found a note that the driver doesn't support full-duplex mode, but if you look at the code (at least the pin configuration section), it is clearly _trying_ to support full-duplex.

Here's the config I was using when trying the full-duplex mode on I2S0:

Code: Select all

  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX),
    .sample_rate =  I2S_SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_MSB,
    .intr_alloc_flags = 0,
    .dma_buf_count = 4, // was 8, output delay was noticeable
    .dma_buf_len = 50,  // number of samples (2x32 bit), NOT bytes!
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };
  i2s_pin_config_t pin_config = {
    .bck_io_num = IO_SCK,       // this is BCK pin
    .ws_io_num =  IO_SPCS0,     // this is LRCK pin
    .data_out_num =  IO_MOSI,   // this is DATA output pin
    .data_in_num = IO_MISO      // this is DATA input pin
  };
Then I tried to use I2S0 in TX mode and I2S1 in RX mode (using the same pins) and depending on the order that the I2S pins are configured, either nothing or TX only works. If I call i2s_set_pin() for I2S0 first, then nothing works, but if I call it for I2S0 (the TX port) last, then TX works - and i2s_read() returns no data for I2S1. My guess is that i2s_set_pin() messes up with the pins somehow and the pins for one or the other I2S are not getting configured properly. I have checked some I2S configuration registers after setting up the driver and they look ok to me (and both RX and TX work using the driver, just not simultaneously).

Here is the configuration I am using for the two I2S's:

Code: Select all

  i2s_config_t i2s_config_0 = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate =  I2S_SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_MSB,
    .intr_alloc_flags = 0,
    .dma_buf_count = 4, // was 8, output delay was noticeable
    .dma_buf_len = 50,  // number of samples (2x32 bit), NOT bytes!
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };
  i2s_pin_config_t pin_config_0 = {
    .bck_io_num = IO_SCK,         // this is BCK pin
    .ws_io_num =  IO_SPCS0,     // this is LRCK pin
    .data_out_num =  IO_MOSI,   // this is DATA output pin
    .data_in_num = I2S_PIN_NO_CHANGE    // this is DATA input pin
  };

  i2s_config_t i2s_config_1 = {
    .mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_RX),
    .sample_rate =  I2S_SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_MSB,
    .intr_alloc_flags = 0,
    .dma_buf_count = 4, // was 8, output delay was noticeable
    .dma_buf_len = 50,  // number of samples (2x32 bit), NOT bytes!
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };
  i2s_pin_config_t pin_config_1 = {
    .bck_io_num = IO_SCK,         // this is BCK pin
    .ws_io_num =  IO_SPCS0,     // this is LRCK pin
    .data_out_num =  I2S_PIN_NO_CHANGE, // this is DATA output pin
    .data_in_num = IO_MISO      // this is DATA input pin
  };
Has _anyone_ in this universe got this working?

Thank you!

ns1668
Posts: 50
Joined: Tue Mar 16, 2021 2:00 pm

Re: How to use i2s in full-duplex mode

Postby ns1668 » Mon Jun 07, 2021 7:42 am

Running both I2S peripherals simultaneously does work, you can test it by setting up a loopback by connecting the TX port to the RX port.


You need to be using different pins for BCK/WS/TX/RX on both ports - do not duplicate the pins.
I wonder if I2S_PIN_NO_CHANGE is causing a pin conflict, could you try explicitly configuring the remaining pins, regardless of whether they are in use or not.

I noticed you are using the APLL on one port, but not the other, I would use the same clock source on both unless they are intended to be independent.

I am unsure on the specific implementation for I2S RX as it is driven by an external clock, but your i2s_read task should be periodically called to pull data from the receive queue.

biterror
Posts: 30
Joined: Thu Apr 30, 2020 11:00 am

Re: How to use i2s in full-duplex mode

Postby biterror » Mon Jun 07, 2021 12:25 pm

Thanks for the reply (if it was for me ;) ) I don't have enough pins for two separate I2S interfaces and I also designed the hardware in belief that you could use one I2S in full duplex mode (silly me, I was reading the hardware reference manual). The first batch of boards has been assembled, there's no changing the hardware now. I have to make either one I2S work in full duplex mode or get two I2S peripherals working on the same set of pins.

I don't use APLL for any of the I2S ports. I think the slave I2S interface should input its signals from the master I2S signals. I still believe this is possible, but I don't know how to configure the pins that way manually as i2s_set_pin() doesn't seem to understand this situation. I read somewhere that for the pin to function as both an output from one peripheral and an input to another, you need to configure the pin in a special way.

I don't know which way to go - whether to find out why the I2S driver doesn't work in full duplex mode or to try to make the two I2S peripherals work with the shared pins. I was expecting things to "just work", but then, I have had problems the the ESP32 drivers in every project, so I guess I just had this coming..

Who is online

Users browsing this forum: No registered users and 114 guests