mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(i2s): fix the auto port failure when use simplex on ESP32
This commit is contained in:
@@ -315,6 +315,40 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
ESP_RETURN_ON_FALSE(id >= 0 && id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid I2S port id");
|
||||
if (id == handle->controller->id) {
|
||||
return ESP_OK;
|
||||
}
|
||||
i2s_controller_t *i2s_obj = i2s_acquire_controller_obj(id);
|
||||
if (!i2s_obj || !i2s_take_available_channel(i2s_obj, handle->dir)) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
i2s_controller_t *old_i2s_obj = handle->controller;
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_obj->tx_chan = handle;
|
||||
i2s_obj->chan_occupancy |= I2S_DIR_TX;
|
||||
old_i2s_obj->tx_chan = NULL;
|
||||
old_i2s_obj->full_duplex = false;
|
||||
old_i2s_obj->chan_occupancy &= ~I2S_DIR_TX;
|
||||
} else {
|
||||
i2s_obj->rx_chan = handle;
|
||||
i2s_obj->chan_occupancy |= I2S_DIR_RX;
|
||||
old_i2s_obj->rx_chan = NULL;
|
||||
old_i2s_obj->full_duplex = false;
|
||||
old_i2s_obj->chan_occupancy &= ~I2S_DIR_RX;
|
||||
}
|
||||
handle->controller = i2s_obj;
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
@@ -819,6 +853,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *
|
||||
ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_TX, chan_cfg->dma_desc_num),
|
||||
err, TAG, "register I2S tx channel failed");
|
||||
i2s_obj->tx_chan->role = chan_cfg->role;
|
||||
i2s_obj->tx_chan->is_port_auto = id == I2S_NUM_AUTO;
|
||||
i2s_obj->tx_chan->dma.auto_clear = chan_cfg->auto_clear;
|
||||
i2s_obj->tx_chan->dma.desc_num = chan_cfg->dma_desc_num;
|
||||
i2s_obj->tx_chan->dma.frame_num = chan_cfg->dma_frame_num;
|
||||
@@ -832,6 +867,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *
|
||||
ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_RX, chan_cfg->dma_desc_num),
|
||||
err, TAG, "register I2S rx channel failed");
|
||||
i2s_obj->rx_chan->role = chan_cfg->role;
|
||||
i2s_obj->rx_chan->is_port_auto = id == I2S_NUM_AUTO;
|
||||
i2s_obj->rx_chan->dma.desc_num = chan_cfg->dma_desc_num;
|
||||
i2s_obj->rx_chan->dma.frame_num = chan_cfg->dma_frame_num;
|
||||
i2s_obj->rx_chan->start = i2s_rx_channel_start;
|
||||
|
||||
@@ -91,6 +91,7 @@ struct i2s_channel_obj_t {
|
||||
/* Stored configurations */
|
||||
void *mode_info; /*!< Slot, clock and gpio information of each mode */
|
||||
bool full_duplex_slave; /*!< whether the channel is forced to switch to slave role for full duplex */
|
||||
bool is_port_auto; /*!< Whether the port is auto-assigned */
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
bool apll_en; /*!< Flag of wether APLL enabled */
|
||||
#endif
|
||||
@@ -215,6 +216,19 @@ esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, b
|
||||
*/
|
||||
void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_sig_idx);
|
||||
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
/**
|
||||
* @brief Change the port of the I2S channel
|
||||
*
|
||||
* @param handle I2S channel handle
|
||||
* @param id I2S port id
|
||||
* @return
|
||||
* - ESP_OK Change port success
|
||||
* - ESP_ERR_NOT_FOUND No available I2S port found
|
||||
*/
|
||||
esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -219,13 +219,34 @@ static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t ha
|
||||
if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) {
|
||||
handle->controller->full_duplex = true;
|
||||
ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id);
|
||||
}
|
||||
} else {
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
else {
|
||||
ESP_LOGE(TAG, "Can't set different channel configurations on a same port");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
bool port_changed = false;
|
||||
if (handle->is_port_auto) {
|
||||
ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id);
|
||||
for (int i = 0; i < SOC_I2S_NUM; i++) {
|
||||
if (i == handle->controller->id) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGD(TAG, "Trying to move %s channel from port %d to %d",
|
||||
handle->dir == I2S_DIR_TX ? "TX" : "RX", handle->controller->id, i);
|
||||
if (i2s_channel_change_port(handle, i) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "Move success!");
|
||||
port_changed = true;
|
||||
break;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Move failed...");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!port_changed) {
|
||||
ESP_LOGE(TAG, "Can't set different channel configurations on a same port");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#else
|
||||
ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Switch to the slave role if needed */
|
||||
if (handle->controller->full_duplex &&
|
||||
|
||||
@@ -231,6 +231,8 @@ static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle,
|
||||
if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_tdm_config_t)) == 0) {
|
||||
handle->controller->full_duplex = true;
|
||||
ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id);
|
||||
}
|
||||
}
|
||||
/* Switch to the slave role if needed */
|
||||
|
||||
@@ -247,6 +247,8 @@ TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
|
||||
}
|
||||
|
||||
static volatile bool task_run_flag;
|
||||
static volatile bool read_task_success = true;
|
||||
static volatile bool write_task_success = true;
|
||||
|
||||
#define TEST_I2S_DATA 0x78
|
||||
|
||||
@@ -279,6 +281,7 @@ static void i2s_read_task(void *args)
|
||||
ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("Read timeout count: %"PRIu32"\n", cnt++);
|
||||
read_task_success = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,6 +302,7 @@ static void i2s_write_task(void *args) {
|
||||
ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("Write timeout count: %"PRIu32"\n", cnt++);
|
||||
write_task_success = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +442,7 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]")
|
||||
},
|
||||
},
|
||||
};
|
||||
/* Part 1: test common lazy duplex mode */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
@@ -458,7 +463,7 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]")
|
||||
xTaskCreate(i2s_read_check_task, "i2s_read_check_task", 4096, rx_handle, 5, NULL);
|
||||
printf("RX started\n");
|
||||
|
||||
/* Wait 3 seconds to see if any failures occur */
|
||||
/* Wait 1 seconds to see if any failures occur */
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
printf("Finished\n");
|
||||
|
||||
@@ -474,6 +479,72 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]")
|
||||
/* Delete the channels */
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
/* Part 2: Test no lazy duplex mode with port auto assignment */
|
||||
chan_cfg.id = I2S_NUM_AUTO;
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
|
||||
/* Change the config to not constitute full-duplex */
|
||||
std_cfg.gpio_cfg.mclk = I2S_GPIO_UNUSED;
|
||||
std_cfg.gpio_cfg.bclk = I2S_GPIO_UNUSED;
|
||||
std_cfg.gpio_cfg.ws = I2S_GPIO_UNUSED;
|
||||
std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED;
|
||||
std_cfg.gpio_cfg.din = I2S_GPIO_UNUSED;
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
/* Delete the channels */
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
return;
|
||||
#else
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* On ESP32, if failed to constitute full-duplex with `I2S_NUM_AUTO`,
|
||||
the channel will be re-assigned to the next availableport */
|
||||
i2s_chan_info_t chan_info;
|
||||
TEST_ESP_OK(i2s_channel_get_info(rx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.id == I2S_NUM_0);
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.id == I2S_NUM_1);
|
||||
#endif
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
task_run_flag = true;
|
||||
read_task_success = true;
|
||||
write_task_success = true;
|
||||
/* writing task to keep writing */
|
||||
xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL);
|
||||
printf("TX started\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* reading task to keep reading */
|
||||
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL);
|
||||
printf("RX started\n");
|
||||
|
||||
/* Wait 1 seconds to see if any failures occur */
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
printf("Finished\n");
|
||||
|
||||
/* Stop those three tasks */
|
||||
task_run_flag = false;
|
||||
|
||||
/* Wait for the three thread deleted */
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
/* Disable the channels, they will keep waiting until the current reading / writing finished */
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
/* Delete the channels */
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
/* Check if the reading and writing tasks are successful */
|
||||
TEST_ASSERT(read_task_success);
|
||||
TEST_ASSERT(write_task_success);
|
||||
}
|
||||
|
||||
static bool whether_contains_exapected_data(uint16_t *src, uint32_t src_len, uint32_t src_step, uint32_t start_val, uint32_t val_step)
|
||||
|
||||
Reference in New Issue
Block a user