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.