mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
Merge branch 'feature/i2s_support_merge_simplex_to_duplex' into 'master'
feat(i2s): support to lazy constitute full-duplex mode Closes IDF-11696 See merge request espressif/esp-idf!39428
This commit is contained in:
@@ -297,12 +297,6 @@ static inline bool i2s_take_available_channel(i2s_controller_t *i2s_obj, uint8_t
|
||||
{
|
||||
bool is_available = false;
|
||||
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
/* In ESP32 and ESP32-S2, tx channel and rx channel are not totally separated
|
||||
* Take both two channels in case one channel can affect another
|
||||
*/
|
||||
chan_search_mask = I2S_DIR_RX | I2S_DIR_TX;
|
||||
#endif
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
|
||||
i2s_obj->chan_occupancy |= chan_search_mask;
|
||||
|
@@ -164,6 +164,7 @@ struct i2s_channel_obj_t {
|
||||
bool is_etm_stop: 1; /*!< Whether stop by etm tasks */
|
||||
bool is_raw_pdm: 1; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */
|
||||
bool is_external: 1; /*!< Whether use external clock */
|
||||
bool full_duplex_slave: 1; /*!< whether the channel is forced to switch to slave role for full duplex */
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
bool apll_en: 1; /*!< Flag of whether APLL enabled */
|
||||
#endif
|
||||
|
@@ -33,9 +33,10 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
|
||||
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
|
||||
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
slot_cfg->slot_bit_width = slot_bits;
|
||||
/* Calculate multiple
|
||||
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
|
||||
if (handle->role == I2S_ROLE_MASTER) {
|
||||
if (handle->role == I2S_ROLE_MASTER || handle->full_duplex_slave) {
|
||||
clk_info->bclk = rate * handle->total_slot * slot_bits;
|
||||
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
||||
@@ -122,18 +123,13 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c
|
||||
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
|
||||
TAG, "allocate memory for dma descriptor failed");
|
||||
}
|
||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||
/* Share bck and ws signal in full-duplex mode */
|
||||
if (handle->controller->full_duplex) {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
||||
/* Since bck and ws are shared, only tx or rx can be master
|
||||
Force to set rx as slave to avoid conflict of clock signal */
|
||||
if (handle->dir == I2S_DIR_RX) {
|
||||
is_slave = true;
|
||||
}
|
||||
} else {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||
}
|
||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply STD format */
|
||||
@@ -178,43 +174,101 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c
|
||||
/* Set mclk pin */
|
||||
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(handle, id, gpio_cfg->mclk, std_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + slave" mode, select TX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
/* Bind the MCLK signal to the TX or RX clock source */
|
||||
if (!handle->controller->full_duplex) {
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
} else {
|
||||
/* For "rx + master" mode, select RX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
||||
}
|
||||
} else if (handle->role == I2S_ROLE_MASTER) {
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
} else {
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t ws_sig = 0;
|
||||
uint32_t bck_sig = 0;
|
||||
bool is_input = handle->role == I2S_ROLE_SLAVE;
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
// Assign slave signals
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
ws_sig = i2s_periph_signal[id].s_tx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].s_tx_bck_sig;
|
||||
} else {
|
||||
ws_sig = i2s_periph_signal[id].s_rx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].s_rx_bck_sig;
|
||||
}
|
||||
} else {
|
||||
// Assign master signals
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
ws_sig = i2s_periph_signal[id].m_tx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].m_tx_bck_sig;
|
||||
} else {
|
||||
ws_sig = i2s_periph_signal[id].m_rx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].m_rx_bck_sig;
|
||||
}
|
||||
}
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, ws_sig, is_input, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, bck_sig, is_input, gpio_cfg->invert_flags.bclk_inv);
|
||||
|
||||
/* Update the mode info: gpio configuration */
|
||||
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
|
||||
{
|
||||
/* Get another direction handle */
|
||||
i2s_chan_handle_t another_handle = handle->dir == I2S_DIR_RX ? handle->controller->tx_chan : handle->controller->rx_chan;
|
||||
/* Condition: 1. Another direction channel is registered
|
||||
* 2. Not a full-duplex channel yet
|
||||
* 3. Another channel is initialized, try to compare the configurations */
|
||||
if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) {
|
||||
/* Judge if the two channels can constitute full-duplex */
|
||||
if (!handle->controller->full_duplex) {
|
||||
i2s_std_config_t curr_cfg = *std_cfg;
|
||||
/* Override the slot bit width to the actual slot bit width */
|
||||
curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ?
|
||||
curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width;
|
||||
/* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */
|
||||
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);
|
||||
}
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Switch to the slave role if needed */
|
||||
if (handle->controller->full_duplex &&
|
||||
handle->role == I2S_ROLE_MASTER &&
|
||||
another_handle->role == I2S_ROLE_MASTER) {
|
||||
/* The later initialized channel must be slave for full duplex */
|
||||
handle->role = I2S_ROLE_SLAVE;
|
||||
handle->full_duplex_slave = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
@@ -232,6 +286,11 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
|
||||
handle->mode_info = calloc(1, sizeof(i2s_std_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
|
||||
/* Try to constitute full-duplex mode if the STD configuration is totally same as another channel */
|
||||
ret = s_i2s_channel_try_to_constitude_std_duplex(handle, std_cfg);
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "Failed to constitute full-duplex mode");
|
||||
#endif
|
||||
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
|
@@ -34,9 +34,10 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
|
||||
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
|
||||
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
slot_cfg->slot_bit_width = slot_bits;
|
||||
/* Calculate multiple
|
||||
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
|
||||
if (handle->role == I2S_ROLE_MASTER) {
|
||||
if (handle->role == I2S_ROLE_MASTER || handle->full_duplex_slave) {
|
||||
clk_info->bclk = rate * handle->total_slot * slot_bits;
|
||||
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
||||
@@ -126,18 +127,13 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c
|
||||
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
|
||||
TAG, "allocate memory for dma descriptor failed");
|
||||
}
|
||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||
/* Share bck and ws signal in full-duplex mode */
|
||||
if (handle->controller->full_duplex) {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
||||
/* Since bck and ws are shared, only tx or rx can be master
|
||||
Force to set rx as slave to avoid conflict of clock signal */
|
||||
if (handle->dir == I2S_DIR_RX) {
|
||||
is_slave = true;
|
||||
}
|
||||
} else {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||
}
|
||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply TDM format */
|
||||
@@ -183,43 +179,92 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
|
||||
/* Set mclk pin */
|
||||
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(handle, id, gpio_cfg->mclk, tdm_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + slave" mode, select TX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
/* Bind the MCLK signal to the TX or RX clock source */
|
||||
if (!handle->controller->full_duplex) {
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
} else {
|
||||
/* For "rx + master" mode, select RX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
||||
}
|
||||
} else if (handle->role == I2S_ROLE_MASTER) {
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
} else {
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t ws_sig = 0;
|
||||
uint32_t bck_sig = 0;
|
||||
bool is_input = handle->role == I2S_ROLE_SLAVE;
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
// Assign slave signals
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
ws_sig = i2s_periph_signal[id].s_tx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].s_tx_bck_sig;
|
||||
} else {
|
||||
ws_sig = i2s_periph_signal[id].s_rx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].s_rx_bck_sig;
|
||||
}
|
||||
} else {
|
||||
// Assign master signals
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
ws_sig = i2s_periph_signal[id].m_tx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].m_tx_bck_sig;
|
||||
} else {
|
||||
ws_sig = i2s_periph_signal[id].m_rx_ws_sig;
|
||||
bck_sig = i2s_periph_signal[id].m_rx_bck_sig;
|
||||
}
|
||||
}
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->ws, ws_sig, is_input, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, bck_sig, is_input, gpio_cfg->invert_flags.bclk_inv);
|
||||
|
||||
/* Update the mode info: gpio configuration */
|
||||
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
|
||||
{
|
||||
/* Get another direction handle */
|
||||
i2s_chan_handle_t another_handle = handle->dir == I2S_DIR_RX ? handle->controller->tx_chan : handle->controller->rx_chan;
|
||||
/* Condition: 1. Another direction channel is registered
|
||||
* 2. Not a full-duplex channel yet
|
||||
* 3. Another channel is initialized, try to compare the configurations */
|
||||
if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) {
|
||||
if (!handle->controller->full_duplex) {
|
||||
i2s_tdm_config_t curr_cfg = *tdm_cfg;
|
||||
/* Override the slot bit width to the actual slot bit width */
|
||||
curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ?
|
||||
curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width;
|
||||
/* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* Switch to the slave role if needed */
|
||||
if (handle->controller->full_duplex &&
|
||||
handle->role == I2S_ROLE_MASTER &&
|
||||
another_handle->role == I2S_ROLE_MASTER) {
|
||||
/* The later initialized channel must be slave for full duplex */
|
||||
handle->role = I2S_ROLE_SLAVE;
|
||||
handle->full_duplex_slave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
@@ -237,6 +282,8 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
|
||||
}
|
||||
handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
/* Try to constitute full-duplex mode if the TDM configuration is totally same as another channel */
|
||||
s_i2s_channel_try_to_constitude_tdm_duplex(handle, tdm_cfg);
|
||||
/* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
|
@@ -299,6 +299,8 @@ typedef struct {
|
||||
* @brief Initialize I2S channel to standard mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
* @note When initialize the STD mode with a same configuration as another channel on a same port,
|
||||
* these two channels can constitude as full-duplex mode automatically
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] std_cfg Configurations for standard mode, including clock, slot and GPIO
|
||||
|
@@ -196,6 +196,8 @@ typedef struct {
|
||||
* @brief Initialize I2S channel to TDM mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
* @note When initialize the TDM mode with a same configuration as another channel on a same port,
|
||||
* these two channels can constitude as full-duplex mode automatically
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] tdm_cfg Configurations for TDM mode, including clock, slot and GPIO
|
||||
|
@@ -191,6 +191,38 @@ TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
/* Lazy initialize std duplex test */
|
||||
chan_cfg.id = I2S_NUM_0; // Specify port id to I2S port 0
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.pair_chan == NULL);
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.pair_chan == rx_handle);
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
/* Lazy initialize tdm duplex test */
|
||||
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_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.pair_chan == NULL);
|
||||
i2s_tdm_config_t tdm_cfg = {
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO, 0x0F),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_init_tdm_mode(tx_handle, &tdm_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_tdm_mode(rx_handle, &tdm_cfg));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.pair_chan == rx_handle);
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
#endif
|
||||
|
||||
/* Repeat to check if a same port can be allocated again */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
@@ -206,6 +238,24 @@ TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
|
||||
|
||||
static volatile bool task_run_flag;
|
||||
|
||||
#define TEST_I2S_DATA 0x78
|
||||
|
||||
static void i2s_read_check_task(void *args)
|
||||
{
|
||||
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)args;
|
||||
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
|
||||
TEST_ASSERT(recv_buf);
|
||||
size_t recv_size = 0;
|
||||
|
||||
while (task_run_flag) {
|
||||
TEST_ASSERT_EQUAL(i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300), ESP_OK);
|
||||
TEST_ASSERT_EQUAL(recv_buf[0], TEST_I2S_DATA);
|
||||
}
|
||||
|
||||
free(recv_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void i2s_read_task(void *args)
|
||||
{
|
||||
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)args;
|
||||
@@ -231,6 +281,7 @@ static void i2s_write_task(void *args)
|
||||
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
|
||||
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
|
||||
TEST_ASSERT(send_buf);
|
||||
memset(send_buf, TEST_I2S_DATA, 2000);
|
||||
size_t send_size = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t cnt = 1;
|
||||
@@ -358,6 +409,65 @@ TEST_CASE("I2S_thread_concurrent_safety_test", "[i2s]")
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_lazy_duplex_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = MASTER_MCK_IO,
|
||||
.bclk = MASTER_BCK_IO,
|
||||
.ws = MASTER_WS_IO,
|
||||
.dout = DATA_OUT_IO,
|
||||
.din = DATA_OUT_IO,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
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));
|
||||
printf("Enabled TX channel\n");
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
/* Enable the channels before creating reading/writing task*/
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
printf("Enabled RX channel\n");
|
||||
|
||||
task_run_flag = 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_check_task, "i2s_read_check_task", 4096, rx_handle, 5, NULL);
|
||||
printf("RX started\n");
|
||||
|
||||
/* Wait 3 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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint32_t val = start_val;
|
||||
|
@@ -905,7 +905,9 @@ Full-duplex mode registers TX and RX channel in an I2S port at the same time, an
|
||||
|
||||
Note that one handle can only stand for one channel. Therefore, it is still necessary to configure the slot and clock for both TX and RX channels one by one.
|
||||
|
||||
Here is an example of how to allocate a pair of full-duplex channels:
|
||||
There are two methods to allocate a pair of full-duplex channels:
|
||||
|
||||
1. Allocate both TX and RX handles in a single call of :cpp:func:`i2s_new_channel`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -945,6 +947,48 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
|
||||
...
|
||||
|
||||
2. Allocate TX and RX handles separately, and initialize them with the same configuration.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
/* Allocate a pair of I2S channels on a same port */
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
/* Allocate for TX and RX channel separately, they are not full-duplex yet */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
|
||||
/* Set the configurations for BOTH TWO channels, they will constitute in full-duplex mode automatically */
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(32000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = GPIO_NUM_4,
|
||||
.ws = GPIO_NUM_5,
|
||||
.dout = GPIO_NUM_18,
|
||||
.din = GPIO_NUM_19,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
// ...
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
...
|
||||
|
||||
|
||||
.. only:: SOC_I2S_HW_VERSION_1
|
||||
|
||||
Simplex Mode
|
||||
@@ -961,7 +1005,7 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
i2s_std_config_t std_tx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -979,12 +1023,12 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
},
|
||||
};
|
||||
/* Initialize the channel */
|
||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
||||
i2s_channel_enable(tx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
|
||||
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
||||
* it will return ESP_ERR_NOT_FOUND */
|
||||
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
i2s_std_config_t std_rx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1001,8 +1045,8 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_channel_init_std_mode(rx_handle, &std_rx_cfg);
|
||||
i2s_channel_enable(rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
.. only:: SOC_I2S_HW_VERSION_2
|
||||
|
||||
@@ -1021,7 +1065,7 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
i2s_std_config_t std_tx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1039,12 +1083,12 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
},
|
||||
};
|
||||
/* Initialize the channel */
|
||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
||||
i2s_channel_enable(tx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
|
||||
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
||||
* it will return ESP_ERR_NOT_FOUND */
|
||||
i2s_new_channel(&chan_cfg, NULL, &rx_handle); // Both RX and TX channel will be registered on I2S0, but they can work with different configurations.
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle)); // Both RX and TX channel will be registered on I2S0, but they can work with different configurations.
|
||||
i2s_std_config_t std_rx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1061,8 +1105,8 @@ Here is an example of how to allocate a pair of full-duplex channels:
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_channel_init_std_mode(rx_handle, &std_rx_cfg);
|
||||
i2s_channel_enable(rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
.. only:: SOC_I2S_SUPPORTS_ETM
|
||||
|
||||
|
@@ -905,7 +905,9 @@ STD RX 模式
|
||||
|
||||
请注意,一个句柄只能代表一个通道,因此仍然需要对 TX 和 RX 通道逐个进行声道和时钟配置。
|
||||
|
||||
以下示例展示了如何分配两个全双工通道:
|
||||
驱动支持两种分配全双工通道的方法:
|
||||
|
||||
1. 在调用 :cpp:func:`i2s_new_channel` 函数时,同时分配 TX 和 RX 通道两个通道。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -945,6 +947,47 @@ STD RX 模式
|
||||
|
||||
...
|
||||
|
||||
2. 调用两次 :cpp:func:`i2s_new_channel` 函数分别分配 TX 和 RX 通道,但使用相同配置初始化 TX 和 RX 通道。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
/* 分配两个 I2S 通道 */
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
/* 分别分配给 TX 和 RX 通道 */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
|
||||
/* 为两个通道设置完全相同的配置,TX 和 RX 将自动组成全双工模式 */
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(32000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = GPIO_NUM_4,
|
||||
.ws = GPIO_NUM_5,
|
||||
.dout = GPIO_NUM_18,
|
||||
.din = GPIO_NUM_19,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
// ...
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
...
|
||||
|
||||
.. only:: SOC_I2S_HW_VERSION_1
|
||||
|
||||
单工模式
|
||||
@@ -961,7 +1004,7 @@ STD RX 模式
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
i2s_std_config_t std_tx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -979,12 +1022,12 @@ STD RX 模式
|
||||
},
|
||||
};
|
||||
/* 初始化通道 */
|
||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
||||
i2s_channel_enable(tx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
|
||||
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
||||
* 并返回 ESP_ERR_NOT_FOUND */
|
||||
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
i2s_std_config_t std_rx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1001,8 +1044,8 @@ STD RX 模式
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_channel_init_std_mode(rx_handle, &std_rx_cfg);
|
||||
i2s_channel_enable(rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
.. only:: SOC_I2S_HW_VERSION_2
|
||||
|
||||
@@ -1021,7 +1064,7 @@ STD RX 模式
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
i2s_std_config_t std_tx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1039,12 +1082,12 @@ STD RX 模式
|
||||
},
|
||||
};
|
||||
/* 初始化通道 */
|
||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
||||
i2s_channel_enable(tx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
|
||||
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
||||
* 并返回 ESP_ERR_NOT_FOUND */
|
||||
i2s_new_channel(&chan_cfg, NULL, &rx_handle); // RX 和 TX 通道都将注册在 I2S0 上,但配置可以不同
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle)); // RX 和 TX 通道都将注册在 I2S0 上,但配置可以不同
|
||||
i2s_std_config_t std_rx_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
@@ -1061,8 +1104,8 @@ STD RX 模式
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_channel_init_std_mode(rx_handle, &std_rx_cfg);
|
||||
i2s_channel_enable(rx_handle);
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
|
||||
.. only:: SOC_I2S_SUPPORTS_ETM
|
||||
|
||||
|
Reference in New Issue
Block a user