Page 1 of 1

ESP32S3 I2S strange problem, One Shot

Posted: Sat Jan 14, 2023 11:57 am
by bnarit
I have tried to use ESP32S3 I2S to generate the precise pulse. However, I still have a problem on how to correctly use I2S driver (IDF4.4).

In theory, if the buffer was written by i2s_write , it should fill the DMA and the shift out to I2S and sent out after the last block of DMA has been sent.

I have tried to generate a pattern and shift out to HC595, which the bit alignment seems to be correct.

The buffer was filled with a toggle pattern for 10 times ( 20 samples in total). but the capture shows that sometimes it only sent out the fragment, and some of the data, in the beginning, was dismissed.

The green pulse in the scope is when the i2s_write was called, and only one time called since it started. Why does some data was dismissed?

I have enabled .tx_desc_auto_clear = true, because I don't want a circular buffer. Each buffer should be sent out once and should not be resent.

Secondly, i2s_write should be blocked, but it means the blocking on writing to DMA , NOT blocking on transmission, How can I know the current status of the DMA buffer? How to know how much buffer is left?

In conclusion, how can I implement the TX buffer, which precisely sends out the data? I have looked on the event mechanism, but getting the data dropped isn't helpful since we can't lost any data.

here is the initial code

Code: Select all

const i2s_config_t i2s_config = {
      .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_TX),
      .sample_rate = I2S_FREQ*2,      // ONE PULSE OF i2s IS A DOUBLE SAMPLING
      .bits_per_sample = i2s_bits_per_sample_t(32),
      .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
      .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_MSB),
      .dma_buf_count = 2,
      .dma_buf_len = I2S_BUFFER_LEN,
      .tx_desc_auto_clear = true // enable one shot
    };
    i2s_driver_install(i2s_port, &i2s_config, 0, NULL);
    const i2s_pin_config_t pin_config = {
      .bck_io_num = pin_sck,
      .ws_io_num = pin_ws,
      .data_out_num = pin_sd,
      .data_in_num = I2S_PIN_NO_CHANGE

    };
    i2s_set_pin(i2s_port, &pin_config);
    i2s_start(i2s_port);
And the TX code

Code: Select all

  uint32_t rtBuffer[I2S_BUFFER_LEN];
  digitalWrite(42, HIGH);
  for(uint32_t * _p_buf = &rtBuffer[0];_p_buf < &rtBuffer[I2S_BUFFER_LEN] ;_p_buf++)
  {
    *_p_buf = 0x55;
    _p_buf++;
    *_p_buf =0;
  }
  for(uint32_t * _p_buf = &rtBuffer[20];_p_buf < &rtBuffer[I2S_BUFFER_LEN] ;_p_buf++)
  {
    *_p_buf = 0x500;
    _p_buf++;
    *_p_buf =0;
  }         
  size_t  wr_b;
  esp_err_t ret = i2s_write(I2S_PORT,rtBuffer,(I2S_BUFFER_LEN)*4  ,&wr_b,portMAX_DELAY );
  digitalWrite(42, LOW);
  if(ret == ESP_OK )
  {
    log_i("wr ok %d",wr_b);
  }else{
    log_i("wr err %d",wr_b);
  }

Re: ESP32S3 I2S strange problem, One Shot

Posted: Sat Jan 14, 2023 6:12 pm
by bnarit
I think I found the problem in ESP32 driver.

According to https://github.com/espressif/esp-idf/bl ... s_common.c, line 1075.

When we put data to i2s_channel_write (IDF 5.0) or i2s_write (IDF 4.x), which I believe they use the same mechanism.

It will be put on the current DMA slot, which is located by handle->dma.curr_ptr.

There is a chance that the data was put in after the whole DMA slot has been sent out, and the data which has been put in before the beginning of the next slot will be dismissed.

What it should do is they should calculate for the next of the next DMA buffer, which guarantees that the slot will not be used before the end of the writing process.

Also, the circular DMA should not be a default, as shown in line 435. The end of the buffer linklist should be on the NULL buffer.
There should be the NULL buffer, which contains the default value and link to itself.

Re: ESP32S3 I2S strange problem, One Shot

Posted: Sun Jan 15, 2023 8:14 am
by bnarit
This code helped me a lot, the synchronization can be done with if (xQueueReceive(output->m_i2sQueue, &evt, portMAX_DELAY) == pdPASS)

https://github.com/atomic14/esp32_audio ... Output.cpp