I2S_write blocks with unfilled DMA buffers
Posted: Wed Dec 19, 2018 1:00 am
The i2s_write() call blocks when I try to pre-fill the TX DMA buffers.
I'd like to fill the DMA buffers before I call I2S_start() to minimize the possibility of underruns. However, i2s_write() doesn't return unless I2S is already started. Upon further testing and inspecting the i2s.c code, it seems, even if I2S is started, the i2s_write() api allows the filling of only one TX buffer at a time. After a single buffer is filled, a subsequent call will block from filling the next buffer until a i2s_reg->int_st.out_eof interrupt is received. Is my understanding correct? Is this the intended behavior? If so, what is the purpose of allocating multiple buffers?
Here is some sample code to illustrate the simplest case:
#define I2S_NUM (0)
void app_main()
{
size_t bytes_written;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = 16000,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 8,
.dma_buf_len = 400,
.use_apll = true,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.tx_desc_auto_clear = false,
};
i2s_pin_config_t pin_config = {
.bck_io_num = 19,
.ws_io_num = 23,
.data_out_num = 22,
.data_in_num = -1
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
i2s_stop(I2S_NUM);
ESP_LOGI(TAG, "begin write");
i2s_write(I2S_NUM, "X", 1, &bytes_written, portMAX_DELAY);
ESP_LOGI(TAG, "write complete"); /* never reaches here */
}
I'd like to fill the DMA buffers before I call I2S_start() to minimize the possibility of underruns. However, i2s_write() doesn't return unless I2S is already started. Upon further testing and inspecting the i2s.c code, it seems, even if I2S is started, the i2s_write() api allows the filling of only one TX buffer at a time. After a single buffer is filled, a subsequent call will block from filling the next buffer until a i2s_reg->int_st.out_eof interrupt is received. Is my understanding correct? Is this the intended behavior? If so, what is the purpose of allocating multiple buffers?
Here is some sample code to illustrate the simplest case:
#define I2S_NUM (0)
void app_main()
{
size_t bytes_written;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = 16000,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 8,
.dma_buf_len = 400,
.use_apll = true,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.tx_desc_auto_clear = false,
};
i2s_pin_config_t pin_config = {
.bck_io_num = 19,
.ws_io_num = 23,
.data_out_num = 22,
.data_in_num = -1
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
i2s_stop(I2S_NUM);
ESP_LOGI(TAG, "begin write");
i2s_write(I2S_NUM, "X", 1, &bytes_written, portMAX_DELAY);
ESP_LOGI(TAG, "write complete"); /* never reaches here */
}