From 26f5441e0e0712fce427e3c7de42febb3cb0ad45 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Fri, 6 Jan 2023 12:13:21 +0800 Subject: [PATCH] i2s: support bytes_read / bytes_written to be NULL --- components/driver/i2s/i2s_common.c | 59 +++++++++++-------- .../driver/i2s/include/driver/i2s_common.h | 16 ++--- .../i2s_std/main/i2s_std_example_main.c | 4 +- .../i2s/i2s_basic/i2s_std/pytest_i2s_std.py | 10 ++-- .../i2s_tdm/main/i2s_tdm_example_main.c | 4 +- .../i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py | 10 ++-- .../i2s_es8311/main/i2s_es8311_example.c | 5 +- 7 files changed, 61 insertions(+), 47 deletions(-) diff --git a/components/driver/i2s/i2s_common.c b/components/driver/i2s/i2s_common.c index 705efcf664..c3dc28124f 100644 --- a/components/driver/i2s/i2s_common.c +++ b/components/driver/i2s/i2s_common.c @@ -1039,12 +1039,13 @@ esp_err_t i2s_channel_disable(i2s_chan_handle_t handle) xSemaphoreTake(handle->mutex, portMAX_DELAY); ESP_GOTO_ON_FALSE(handle->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "the channel has not been enabled yet"); - /* Update the state to force quit the current reading/wrinting operation */ + /* Update the state to force quit the current reading/writing operation */ handle->state = I2S_CHAN_STATE_READY; - /* Waiting for reading/wrinting operation quit */ - xSemaphoreTake(handle->binary, portMAX_DELAY); + /* Reset the descriptor pointer */ handle->dma.curr_ptr = NULL; handle->dma.rw_pos = 0; + /* Waiting for reading/wrinting operation quit */ + xSemaphoreTake(handle->binary, portMAX_DELAY); handle->stop(handle); #if CONFIG_PM_ENABLE esp_pm_lock_release(handle->pm_lock); @@ -1058,48 +1059,48 @@ err: return ret; } -esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_loaded) +esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, size_t size, size_t *bytes_loaded) { - I2S_NULL_POINTER_CHECK(TAG, handle); - ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "this channel is not tx channel"); - ESP_RETURN_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, TAG, "data can only be preloaded when the channel is READY"); + I2S_NULL_POINTER_CHECK(TAG, tx_handle); + ESP_RETURN_ON_FALSE(tx_handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "this channel is not tx channel"); + ESP_RETURN_ON_FALSE(tx_handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, TAG, "data can only be preloaded when the channel is READY"); uint8_t *data_ptr = (uint8_t *)src; size_t remain_bytes = size; size_t total_loaded_bytes = 0; - xSemaphoreTake(handle->mutex, portMAX_DELAY); + xSemaphoreTake(tx_handle->mutex, portMAX_DELAY); /* The pre-load data will be loaded from the first descriptor */ - if (handle->dma.curr_ptr == NULL) { - handle->dma.curr_ptr = handle->dma.desc[0]; - handle->dma.rw_pos = 0; + if (tx_handle->dma.curr_ptr == NULL) { + tx_handle->dma.curr_ptr = tx_handle->dma.desc[0]; + tx_handle->dma.rw_pos = 0; } - lldesc_t *desc_ptr = (lldesc_t *)handle->dma.curr_ptr; + lldesc_t *desc_ptr = (lldesc_t *)tx_handle->dma.curr_ptr; /* Loop until no bytes in source buff remain or the descriptors are full */ while (remain_bytes) { - size_t bytes_can_load = remain_bytes > (handle->dma.buf_size - handle->dma.rw_pos) ? - (handle->dma.buf_size - handle->dma.rw_pos) : remain_bytes; + size_t bytes_can_load = remain_bytes > (tx_handle->dma.buf_size - tx_handle->dma.rw_pos) ? + (tx_handle->dma.buf_size - tx_handle->dma.rw_pos) : remain_bytes; /* When all the descriptors has loaded data, no more bytes can be loaded, break directly */ if (bytes_can_load == 0) { break; } /* Load the data from the last loaded position */ - memcpy((uint8_t *)(desc_ptr->buf + handle->dma.rw_pos), data_ptr, bytes_can_load); + memcpy((uint8_t *)(desc_ptr->buf + tx_handle->dma.rw_pos), data_ptr, bytes_can_load); data_ptr += bytes_can_load; // Move forward the data pointer total_loaded_bytes += bytes_can_load; // Add to the total loaded bytes remain_bytes -= bytes_can_load; // Update the remaining bytes to be loaded - handle->dma.rw_pos += bytes_can_load; // Move forward the dma buffer position + tx_handle->dma.rw_pos += bytes_can_load; // Move forward the dma buffer position /* When the current position reach the end of the dma buffer */ - if (handle->dma.rw_pos == handle->dma.buf_size) { + if (tx_handle->dma.rw_pos == tx_handle->dma.buf_size) { /* If the next descriptor is not the first descriptor, keep load to the first descriptor * otherwise all descriptor has been loaded, break directly, the dma buffer position * will remain at the end of the last dma buffer */ - if (desc_ptr->empty != (uint32_t)handle->dma.desc[0]) { + if (desc_ptr->empty != (uint32_t)tx_handle->dma.desc[0]) { desc_ptr = (lldesc_t *)desc_ptr->empty; - handle->dma.curr_ptr = (void *)desc_ptr; - handle->dma.rw_pos = 0; + tx_handle->dma.curr_ptr = (void *)desc_ptr; + tx_handle->dma.rw_pos = 0; } else { break; } @@ -1107,7 +1108,7 @@ esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void } *bytes_loaded = total_loaded_bytes; - xSemaphoreGive(handle->mutex); + xSemaphoreGive(tx_handle->mutex); return ESP_OK; } @@ -1121,7 +1122,9 @@ esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t si char *data_ptr; char *src_byte; size_t bytes_can_write; - *bytes_written = 0; + if (bytes_written) { + *bytes_written = 0; + } /* The binary semaphore can only be taken when the channel has been enabled and no other writing operation in progress */ ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->binary, pdMS_TO_TICKS(timeout_ms)) == pdTRUE, ESP_ERR_INVALID_STATE, TAG, "The channel is not enabled"); @@ -1144,7 +1147,9 @@ esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t si size -= bytes_can_write; src_byte += bytes_can_write; handle->dma.rw_pos += bytes_can_write; - (*bytes_written) += bytes_can_write; + if (bytes_written) { + (*bytes_written) += bytes_can_write; + } } xSemaphoreGive(handle->binary); @@ -1160,7 +1165,9 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si uint8_t *data_ptr; uint8_t *dest_byte; int bytes_can_read; - *bytes_read = 0; + if (bytes_read) { + *bytes_read = 0; + } dest_byte = (uint8_t *)dest; /* The binary semaphore can only be taken when the channel has been enabled and no other reading operation in progress */ ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->binary, pdMS_TO_TICKS(timeout_ms)) == pdTRUE, ESP_ERR_INVALID_STATE, TAG, "The channel is not enabled"); @@ -1182,7 +1189,9 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si size -= bytes_can_read; dest_byte += bytes_can_read; handle->dma.rw_pos += bytes_can_read; - (*bytes_read) += bytes_can_read; + if (bytes_read) { + (*bytes_read) += bytes_can_read; + } } xSemaphoreGive(handle->binary); diff --git a/components/driver/i2s/include/driver/i2s_common.h b/components/driver/i2s/include/driver/i2s_common.h index 31c1c4e1cd..819ab0d23f 100644 --- a/components/driver/i2s/include/driver/i2s_common.h +++ b/components/driver/i2s/include/driver/i2s_common.h @@ -158,14 +158,14 @@ esp_err_t i2s_channel_disable(i2s_chan_handle_t handle); * @brief Preload the data into TX DMA buffer * @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started) * @note As the initial DMA buffer has no data inside, it will transmit the empty buffer after enabled the channel, - * this function is used to preload the data into the DMA buffer, so that the valid data can be transmit immediately - * when the channel is enabled. + * this function is used to preload the data into the DMA buffer, so that the valid data can be transmitted immediately + * after the channel is enabled. * @note This function can be called multiple times before enabling the channel, the buffer that loaded later will be concatenated * behind the former loaded buffer. But when all the DMA buffers have been loaded, no more data can be preload then, please * check the `bytes_loaded` parameter to see how many bytes are loaded successfully, when the `bytes_loaded` is smaller than * the `size`, it means the DMA buffers are full. * - * @param[in] handle I2S TX channel handler + * @param[in] tx_handle I2S TX channel handler * @param[in] src The pointer of the source buffer to be loaded * @param[in] size The source buffer size * @param[out] bytes_loaded The bytes that successfully been loaded into the TX DMA buffer @@ -174,7 +174,7 @@ esp_err_t i2s_channel_disable(i2s_chan_handle_t handle); * - ESP_ERR_INVALID_ARG NULL pointer or not TX direction * - ESP_ERR_INVALID_STATE This channel has not stated */ -esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_loaded); +esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, size_t size, size_t *bytes_loaded); /** * @brief I2S write data @@ -184,7 +184,7 @@ esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void * @param[in] handle I2S channel handler * @param[in] src The pointer of sent data buffer * @param[in] size Max data buffer length - * @param[out] bytes_written Byte number that actually be sent + * @param[out] bytes_written Byte number that actually be sent, can be NULL if not needed * @param[in] timeout_ms Max block time * @return * - ESP_OK Write successfully @@ -202,7 +202,7 @@ esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t si * @param[in] handle I2S channel handler * @param[in] dest The pointer of receiving data buffer * @param[in] size Max data buffer length - * @param[out] bytes_read Byte number that actually be read + * @param[out] bytes_read Byte number that actually be read, can be NULL if not needed * @param[in] timeout_ms Max block time * @return * - ESP_OK Read successfully @@ -215,7 +215,7 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si /** * @brief Set event callbacks for I2S channel * - * @note Only allowed to be called when the channel state is REGISTARED / READY, (i.e., before channel starts) + * @note Only allowed to be called when the channel state is REGISTERED / READY, (i.e., before channel starts) * @note User can deregister a previously registered callback by calling this function and setting the callback member in the `callbacks` structure to NULL. * @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. * The variables used in the function should be in the SRAM as well. The `user_data` should also reside in SRAM or internal RAM as well. @@ -226,7 +226,7 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si * @return * - ESP_OK Set event callbacks successfully * - ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument - * - ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTARED or READY + * - ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTERED or READY */ esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data); diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c b/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c index e37f55022f..8276022983 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c +++ b/examples/peripherals/i2s/i2s_basic/i2s_std/main/i2s_std_example_main.c @@ -94,10 +94,10 @@ static void i2s_example_write_task(void *args) size_t w_bytes = EXAMPLE_BUFF_SIZE; - /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmit immediately */ + /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmitted immediately */ while (w_bytes == EXAMPLE_BUFF_SIZE) { /* Here we load the target buffer repeatedly, until all the DMA buffers are preloaded */ - ESP_ERROR_CHECK(i2s_channel_preload_writing_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes)); + ESP_ERROR_CHECK(i2s_channel_preload_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes)); } /* Enable the TX channel */ diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py b/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py index 9ee532dab2..c6208160c8 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py +++ b/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py @@ -26,12 +26,14 @@ def test_i2s_basic_example(dut: Dut) -> None: dut.expect(r'D \(([0-9]+)\) i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz ' r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'D \(([0-9]+)\) i2s_std: The rx channel on I2S0 has been initialized to STD mode successfully', timeout=5) - dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5) - dut.expect(r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled', timeout=5) + chan_enable_pattern = [ + r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', + r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled' + ] + dut.expect(chan_enable_pattern, timeout=5) + dut.expect(chan_enable_pattern, timeout=5) dut.expect(r'Write Task: i2s write ([0-9]+) bytes', timeout=5) dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5) dut.expect(r'-----------------------------------', timeout=5) - dut.expect(r'\[0\] 0 \[1\] 0 \[2\] 0 \[3\] 0', timeout=5) - dut.expect(r'\[4\] 0 \[5\] 0 \[6\] 0 \[7\] 0', timeout=5) dut.expect(r'\[0\] 12 \[1\] 34 \[2\] 56 \[3\] 78', timeout=10) dut.expect(r'\[4\] 9a \[5\] bc \[6\] de \[7\] f0', timeout=10) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c b/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c index f841484b76..38a132a1df 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c +++ b/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c @@ -79,10 +79,10 @@ static void i2s_example_write_task(void *args) size_t w_bytes = EXAMPLE_BUFF_SIZE; - /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmit immediately */ + /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmitted immediately */ while (w_bytes == EXAMPLE_BUFF_SIZE) { /* Here we load the target buffer repeatedly, until all the DMA buffers are preloaded */ - ESP_ERROR_CHECK(i2s_channel_preload_writing_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes)); + ESP_ERROR_CHECK(i2s_channel_preload_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes)); } /* Enable the TX channel */ diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py b/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py index cd59107d90..1fa51c8261 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py +++ b/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py @@ -24,12 +24,14 @@ def test_i2s_tdm_example(dut: Dut) -> None: dut.expect(r'D \(([0-9]+)\) i2s_tdm: Clock division info: \[sclk\] ([0-9]+) Hz ' r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'D \(([0-9]+)\) i2s_tdm: The rx channel on I2S0 has been initialized to TDM mode successfully', timeout=5) - dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5) - dut.expect(r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled', timeout=5) + chan_enable_pattern = [ + r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', + r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled' + ] + dut.expect(chan_enable_pattern, timeout=5) + dut.expect(chan_enable_pattern, timeout=5) dut.expect(r'Write Task: i2s write ([0-9]+) bytes', timeout=5) dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5) dut.expect(r'-----------------------------------', timeout=5) - dut.expect(r'\[0\] 0 \[1\] 0 \[2\] 0 \[3\] 0', timeout=5) - dut.expect(r'\[4\] 0 \[5\] 0 \[6\] 0 \[7\] 0', timeout=5) dut.expect(r'\[0\] 12 \[1\] 34 \[2\] 56 \[3\] 78', timeout=10) dut.expect(r'\[4\] 9a \[5\] bc \[6\] de \[7\] f0', timeout=10) diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c index f633b885a1..66012e47b1 100644 --- a/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c +++ b/examples/peripherals/i2s/i2s_codec/i2s_es8311/main/i2s_es8311_example.c @@ -117,9 +117,10 @@ static void i2s_music(void *args) size_t bytes_write = 0; uint8_t *data_ptr = (uint8_t *)music_pcm_start; - /* (Optional) Disable TX channel and preload the data before enabling the TX channel, so that the valid data can be transmit immediately */ + /* (Optional) Disable TX channel and preload the data before enabling the TX channel, + * so that the valid data can be transmitted immediately */ ESP_ERROR_CHECK(i2s_channel_disable(tx_handle)); - ESP_ERROR_CHECK(i2s_channel_preload_writing_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write)); + ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write)); data_ptr += bytes_write; // Move forward the data pointer /* Enable the TX channel */