I2S driver for ESP32 is not working [IDFGH-5854]

shailesh.korat
Posts: 15
Joined: Thu Jul 12, 2018 11:09 am

Re: I2S driver for ESP32 is not working [IDFGH-5854]

Postby shailesh.korat » Tue Nov 23, 2021 7:17 am

Dear L-KAYA,

I have the result of your provided code. I did not change anything in your configuration. However, still I am getting missing sample in data.
Following are the log message from my device.

Code: Select all

W (28) bootloader_random: RNG for ESP32-S3 not currently supported
W (79) bootloader_random: RNG for ESP32-S3 not currently supported
I (86) cpu_start: Pro cpu up.
I (86) cpu_start: Starting app cpu, entry point is 0x403790a4
I (0) cpu_start: App cpu up.
I (100) cpu_start: Pro cpu start user code
I (100) cpu_start: cpu freq: 240000000
I (100) cpu_start: Application information:
I (103) cpu_start: Project name:     rap_mlm
I (108) cpu_start: App version:      42db5bd-dirty
I (113) cpu_start: Compile time:     Nov 19 2021 16:05:35
I (119) cpu_start: ELF file SHA256:  9e1767beb838d6b9...
I (125) cpu_start: ESP-IDF:          v4.4-dev-3235-g3e370c4296-dirty
I (132) heap_init: Initializing. RAM available for dynamic allocation:
I (139) heap_init: At 3FC97C70 len 00048390 (288 KiB): D/IRAM
I (146) heap_init: At 3FCE0000 len 0000EE34 (59 KiB): STACK/DRAM
I (152) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (159) spi_flash: detected chip: generic
I (163) spi_flash: flash io: dio
I (168) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (201) mlm_main: Initializing SPIFFS
I (508) mlm_main: Partition size: total: 4811921, used: 49196
I (508) mlm_main: Command history enabled
GDMA is supported
new rx channel (0,0) at 0x3fce17e8bytes_per_sample = 4,bytes_per_frame = 8
 I (517) I2S: queue free spaces: 2
bytes_per_sample = 4,bytes_per_frame = 8
 buf_size = 1600
I (526) I2S: DMA Malloc info, datalen=blocksize=1600, dma_buf_count=8
I (533) I2S: I2S0, MCLK output by GPIO18
I (538) rap_adc: Opening new file
Filename: /spiffs/adc.wav
i2s_read block time: 7862 us
i2s_read block time: 11607 us
i2s_read block time: 12259 us
i2s_read block time: 23 us
i2s_read block time: 21 us
i2s_read block time: 103 us
i2s_read block time: 21 us
i2s_read block time: 103 us
i2s_read block time: 22 us
i2s_read block time: 102 us
i2s_read block time: 21 us
i2s_read block time: 102 us
i2s_read block time: 21 us
I (1258) rap_adc: Recording done!
I (1259) rap_adc: File written on SDCard
Let me know if you need any kind of information.
Looking forward to hearing from you soon.
Attachments
sweep_1k_to_15K.png
sweep_1k_to_15K.png (76.8 KiB) Viewed 5456 times

L-KAYA
Posts: 22
Joined: Thu Aug 12, 2021 3:44 am

Re: I2S driver for ESP32 is not working [IDFGH-5854]

Postby L-KAYA » Wed Nov 24, 2021 6:49 am

Hi shailesh.korat,

Glad to see you!
For the first question, here is the explanation for overtime_tick:

Meaning
1. Generally speaking, 'overtime_tick' is the ticks that 'i2s_read' should cost, that means, if the i2s_read task wait for more than 'overtime_tick', there must be data dropped.
2. 'overtime_tick' can also be treat as 'i2s_read' blocking time when there is no data lost.

Calculation
1. Since we have knew the channel number and the bits in one channel, then we can calculate the total bytes in one frame is:
frame_bytes = chan_num * bits_per_sample / 8
2. Then we can know the how many bytes there will be in one second (i.e. byte rate)
byte_rate = frame_bytes * sample_rate
3. Finally we can get the time for receiving EXAMPLE_RECV_BUF_SIZE will cost:
recv_time = EXAMPLE_RECV_BUF_SIZE / byte_rate
4. Transfer to miliseconds
overtime_tick = recv_time * 1000

-----------------------------------------------------

For the test result,

I notice that i2s_read block time is short. In the normal case, the block time should always be around 'recv_time' that mentioned above, that means the received data are taken from message queue immediately, the queue is always empty, i2s_read has to keep waiting for the data from message queue until the receive buffer is filled up. So no data will be dropped, and the 'recv_time' is equal to 'i2s_read' block time. But when the i2s_read block time is short, it means the message queue is always full, so that i2s_read can get data immediately from message queue without any block. Since the data in message queue can't be taken timely, the old data will be dropped to input new data, then you can see the data lost.

I see your wave looks much better after allocating a bigger receive buffer, my suggestion is keeping increase the receive buffer, a big buffer should always help.

shailesh.korat
Posts: 15
Joined: Thu Jul 12, 2018 11:09 am

Re: I2S driver for ESP32 is not working [IDFGH-5854]

Postby shailesh.korat » Wed Nov 24, 2021 4:16 pm

Dear L-KAYA,

Thank you for giving nice explanation.

Few question regarding i2s APIs function call.

1) what is the meaning of dma_buf_count = 8 which are passing during driver_install call?
Does it mean that DMA will create message queue ( size 8 buffer) of dma_buf_len to read data from i2s ?

2)You said that i2s_read block time is short, it means the message queue is always full, However I could not see any buffer overflow with following method. It should print right?

Code: Select all

static void i2s_event(void *args)
{
    i2s_event_t evt;
    uint32_t cnt = 0;
    while (!record_finish) {
        xQueueReceive(evt_que, &evt, portMAX_DELAY);
        if (evt.type == I2S_EVENT_RX_Q_OVF) {
            ESP_LOGW(TAG,"[%d] i2s queue overflow, data dropped", cnt++);
        }
    }
    vTaskDelete(NULL);
}
3) You said allocating a bigger receive buffer should always help. Is this kind of limitation from ESP IDF that I need to keep bigger buffer?

4) i2s_driver_installcall already started DMA , reading data from I2S and storing into DMA message queue,if I will use i2s_read call sometime after it means the message queue is always full, so that i2s_read can get data immediately from message queue without any block. Since the data in message queue can't be taken timely, the old data will be dropped to input new data? Am I understand correctly?

5) Lets say I want to read 192 samples at time (192 * 8= 1536 bytes, where frame_bytes = 8) from i2s_read call, how could it possible?

Happy to provide more information if need.

Thanks.

L-KAYA
Posts: 22
Joined: Thu Aug 12, 2021 3:44 am

Re: I2S driver for ESP32 is not working [IDFGH-5854]

Postby L-KAYA » Thu Nov 25, 2021 10:10 am

Dear shailesh.korat,

You asked really good questions! Actually some answers already on the way, you will see them in programming guide soon.

A1: dma_buf_count: The total number of descriptors used by I2S DMA to receive/transmit data. A descriptor includes some information such as buffer address, the address of the next descriptor, and the buffer length. Since one descriptor points to one buffer, therefore, dma_buf_count can be interpreted as the total number of DMA buffers used to store data from DMA intterrupt. Notice that these buffers internal to i2s_read and descriptors are created automatically inside of the I2S driver, users only need to set their number while the length is derived from the parameter described below. In addition, the DMA message queue length is equal to dma_buf_count - 1, in case of covering the data that haven't read.

dma_buf_len: The number of frames for one-time sampling. The frame here means the total data from all the channels in one WS cycle. For example, two channels in stereo mode (i.e., channel_format is set to I2S_CHANNEL_FMT_RIGHT_LEFT) are active, and each channel transfers 32 bits (i.e., bits_per_sample is set to I2S_BITS_PER_CHAN_32BIT). Then the total number of bytes of a frame is channel_format * bits_per_sample = 2 * 32 / 8 = 8 bytes. For example, we assume that the current dma_frame_num is 100, then the length of the DMA buffer is 8 * 100 = 800 bytes. In addition, the length of an internal DMA buffer shouldn't be greater than 4092.

A2: Yes, your method is right, but this is a bug, it will be fixed together with the update of programming guide, then you can catch the overflow event.

A3: From my understanding, the slower you process the data, the bigger buffer you need. So I think it is mainly depends on the complexity rather than the limitation of IDF.

A4: Partly right. Although the message queue will overflow if i2s_read not called, it can still be taken to underflow as long as the speed of i2s_read is faster than receive. Otherwise, the message queue will always be overflow if the speed of i2s_read can't catch the speed of data receiving from ISR.

A5: If you want to read 192 samples for one-time i2s_read, firstly you should allocate a 1536 bytes buffer for reading. Secondly, dma_buf_len should be smaller than 192, smaller than 192/2 = 96 will be better, here is the reason:

If the dma buffer size is greater than the user given buffer size, it means we need several loops of i2s_read to take all the data in one dma buffer, the max_wait_time will be squeezed to avoid message queue overflow. Therefore it is quite risky to give a small buffer for data receiving when calling i2s_read.

----------------------------------------------------------------------
There are couple of tips:

1. Increasing dma_buf_len can help to reduce the I2S DMA interrupt frequency;

2. Increasing dma_buf_count can help to increase the max wait time that spent by other operations in i2s_read loop;

Code: Select all

        while (1) {
            ... // Other operations (e.g. Waiting semaphore)
            i2s_read(I2S_NUM, user_given_buffer, user_given_buffer_size, &i2s_bytes_read, 100);
            ... // Other operations (e.g. Sending the data to another thread. Not suggest processing data here)
        }
3. Increasing the receive buffer size that give into i2s_read can help to increase the block time of i2s_read function . When you process I2S data (like store them to a SD card) in another thread, the time that spent in processing thread for one loop should not beyond this block time, otherwise there may have data lost. So if it's really slow on dealing with I2S data, please allocate a big receive buffer for storing the read data;

4. Allocating at least two receiving buffers can improve the efficiency. One can continue to receive data while another buffer is under processing. But we have to guarantee the buffer that going to read data is not under processing in other thread, we can use semaphore or other lock to avoid covering the data in the buffer while it is still under processing;

Code: Select all

        uint8_t **user_given_buffers = (uint8_t **)calloc(buffer_num, sizeof(uint8_t *));
        // Don't forget to check if user_given_buffers is NULL here
        for (int i = 0; i < buffer_num; i++) {
            user_given_buffers[i] = (uint8_t *)calloc(user_given_buffer_size, sizeof(uint8_t));
            // Don't forget to check if user_given_buffers[i] is NULL here
        }
        int cnt = 0;
        while (1) {
            ... // Other operations (e.g. Waiting semaphore)
            i2s_read(I2S_NUM, user_given_buffer[cnt], user_given_buffer_size, &i2s_bytes_read, 100);
            ... // Other operations (e.g. Sending the data to another thread. Not suggest processing data here)
            cnt++;
            cnt %= buffer_num;
        }
5. Increasing the priority of the thread that contains i2s_read can help the data in message queue to be taken more timely.

Who is online

Users browsing this forum: No registered users and 237 guests