mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 10:30:58 +02:00
feat(i2s): support to lazy constitute full-duplex mode
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;
|
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);
|
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||||
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
|
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
|
||||||
i2s_obj->chan_occupancy |= chan_search_mask;
|
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_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_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 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
|
#if SOC_I2S_SUPPORTS_APLL
|
||||||
bool apll_en: 1; /*!< Flag of whether APLL enabled */
|
bool apll_en: 1; /*!< Flag of whether APLL enabled */
|
||||||
#endif
|
#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) ||
|
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) ?
|
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
|
||||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||||
|
slot_cfg->slot_bit_width = slot_bits;
|
||||||
/* Calculate multiple
|
/* Calculate multiple
|
||||||
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
|
* 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->bclk = rate * handle->total_slot * slot_bits;
|
||||||
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
||||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
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),
|
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
|
||||||
TAG, "allocate memory for dma descriptor failed");
|
TAG, "allocate memory for dma descriptor failed");
|
||||||
}
|
}
|
||||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
|
||||||
/* Share bck and ws signal in full-duplex mode */
|
/* Share bck and ws signal in full-duplex mode */
|
||||||
if (handle->controller->full_duplex) {
|
if (handle->controller->full_duplex) {
|
||||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
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 {
|
} else {
|
||||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||||
}
|
}
|
||||||
|
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||||
|
|
||||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||||
/* Configure the hardware to apply STD format */
|
/* 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 */
|
/* 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");
|
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
|
#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_CLOCK_SRC_ATOMIC() {
|
||||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
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 {
|
} 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_CLOCK_SRC_ATOMIC() {
|
||||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
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);
|
} else if (handle->role == I2S_ROLE_MASTER) {
|
||||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
if (handle->dir == I2S_DIR_TX) {
|
||||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
I2S_CLOCK_SRC_ATOMIC() {
|
||||||
|
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||||
|
}
|
||||||
} else {
|
} 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_CLOCK_SRC_ATOMIC() {
|
||||||
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_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 */
|
/* Update the mode info: gpio configuration */
|
||||||
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
|
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
|
||||||
|
|
||||||
return ESP_OK;
|
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)
|
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
|
#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));
|
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->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");
|
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 */
|
/* 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");
|
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
|
#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) ||
|
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) ?
|
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
|
||||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||||
|
slot_cfg->slot_bit_width = slot_bits;
|
||||||
/* Calculate multiple
|
/* Calculate multiple
|
||||||
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
|
* 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->bclk = rate * handle->total_slot * slot_bits;
|
||||||
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
clk_info->mclk = rate * clk_cfg->mclk_multiple;
|
||||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
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),
|
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
|
||||||
TAG, "allocate memory for dma descriptor failed");
|
TAG, "allocate memory for dma descriptor failed");
|
||||||
}
|
}
|
||||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
|
||||||
/* Share bck and ws signal in full-duplex mode */
|
/* Share bck and ws signal in full-duplex mode */
|
||||||
if (handle->controller->full_duplex) {
|
if (handle->controller->full_duplex) {
|
||||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
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 {
|
} else {
|
||||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||||
}
|
}
|
||||||
|
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||||
|
|
||||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||||
/* Configure the hardware to apply TDM format */
|
/* 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 */
|
/* 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");
|
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
|
#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_CLOCK_SRC_ATOMIC() {
|
||||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
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 {
|
} 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_CLOCK_SRC_ATOMIC() {
|
||||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
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);
|
} else if (handle->role == I2S_ROLE_MASTER) {
|
||||||
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
if (handle->dir == I2S_DIR_TX) {
|
||||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
I2S_CLOCK_SRC_ATOMIC() {
|
||||||
|
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||||
|
}
|
||||||
} else {
|
} 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_CLOCK_SRC_ATOMIC() {
|
||||||
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_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 */
|
/* Update the mode info: gpio configuration */
|
||||||
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
|
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
|
||||||
|
|
||||||
return ESP_OK;
|
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)
|
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
|
#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));
|
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");
|
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 */
|
/* 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");
|
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
|
#if SOC_I2S_SUPPORTS_APLL
|
||||||
|
@@ -299,6 +299,8 @@ typedef struct {
|
|||||||
* @brief Initialize I2S channel to standard mode
|
* @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)
|
* @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.
|
* 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] handle I2S channel handler
|
||||||
* @param[in] std_cfg Configurations for standard mode, including clock, slot and GPIO
|
* @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
|
* @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)
|
* @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.
|
* 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] handle I2S channel handler
|
||||||
* @param[in] tdm_cfg Configurations for TDM mode, including clock, slot and GPIO
|
* @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(tx_handle));
|
||||||
TEST_ESP_OK(i2s_del_channel(rx_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 */
|
/* 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_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||||
TEST_ESP_OK(i2s_del_channel(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;
|
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)
|
static void i2s_read_task(void *args)
|
||||||
{
|
{
|
||||||
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)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;
|
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
|
||||||
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
|
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
|
||||||
TEST_ASSERT(send_buf);
|
TEST_ASSERT(send_buf);
|
||||||
|
memset(send_buf, TEST_I2S_DATA, 2000);
|
||||||
size_t send_size = 0;
|
size_t send_size = 0;
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
uint32_t cnt = 1;
|
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_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)
|
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;
|
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.
|
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
|
.. 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
|
.. only:: SOC_I2S_HW_VERSION_1
|
||||||
|
|
||||||
Simplex Mode
|
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_handle_t rx_handle;
|
||||||
|
|
||||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
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 = {
|
i2s_std_config_t std_tx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
.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),
|
.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 */
|
/* Initialize the channel */
|
||||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||||
i2s_channel_enable(tx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
|
||||||
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
||||||
* it will return ESP_ERR_NOT_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 = {
|
i2s_std_config_t std_rx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||||
i2s_channel_enable(rx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||||
|
|
||||||
.. only:: SOC_I2S_HW_VERSION_2
|
.. 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 tx_handle;
|
||||||
i2s_chan_handle_t rx_handle;
|
i2s_chan_handle_t rx_handle;
|
||||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
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 = {
|
i2s_std_config_t std_tx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
.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),
|
.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 */
|
/* Initialize the channel */
|
||||||
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||||
i2s_channel_enable(tx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
|
||||||
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
/* RX channel will be registered on another I2S, if no other available I2S unit found
|
||||||
* it will return ESP_ERR_NOT_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 = {
|
i2s_std_config_t std_rx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||||
i2s_channel_enable(rx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||||
|
|
||||||
.. only:: SOC_I2S_SUPPORTS_ETM
|
.. only:: SOC_I2S_SUPPORTS_ETM
|
||||||
|
|
||||||
|
@@ -905,7 +905,9 @@ STD RX 模式
|
|||||||
|
|
||||||
请注意,一个句柄只能代表一个通道,因此仍然需要对 TX 和 RX 通道逐个进行声道和时钟配置。
|
请注意,一个句柄只能代表一个通道,因此仍然需要对 TX 和 RX 通道逐个进行声道和时钟配置。
|
||||||
|
|
||||||
以下示例展示了如何分配两个全双工通道:
|
驱动支持两种分配全双工通道的方法:
|
||||||
|
|
||||||
|
1. 在调用 :cpp:func:`i2s_new_channel` 函数时,同时分配 TX 和 RX 通道两个通道。
|
||||||
|
|
||||||
.. code-block:: c
|
.. 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
|
.. only:: SOC_I2S_HW_VERSION_1
|
||||||
|
|
||||||
单工模式
|
单工模式
|
||||||
@@ -961,7 +1004,7 @@ STD RX 模式
|
|||||||
i2s_chan_handle_t rx_handle;
|
i2s_chan_handle_t rx_handle;
|
||||||
|
|
||||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
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 = {
|
i2s_std_config_t std_tx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||||
i2s_channel_enable(tx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
|
||||||
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
||||||
* 并返回 ESP_ERR_NOT_FOUND */
|
* 并返回 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 = {
|
i2s_std_config_t std_rx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||||
i2s_channel_enable(rx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||||
|
|
||||||
.. only:: SOC_I2S_HW_VERSION_2
|
.. only:: SOC_I2S_HW_VERSION_2
|
||||||
|
|
||||||
@@ -1021,7 +1064,7 @@ STD RX 模式
|
|||||||
i2s_chan_handle_t tx_handle;
|
i2s_chan_handle_t tx_handle;
|
||||||
i2s_chan_handle_t rx_handle;
|
i2s_chan_handle_t rx_handle;
|
||||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
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 = {
|
i2s_std_config_t std_tx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_tx_cfg));
|
||||||
i2s_channel_enable(tx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
|
||||||
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
|
||||||
* 并返回 ESP_ERR_NOT_FOUND */
|
* 并返回 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 = {
|
i2s_std_config_t std_rx_cfg = {
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
.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),
|
.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);
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_rx_cfg));
|
||||||
i2s_channel_enable(rx_handle);
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||||
|
|
||||||
.. only:: SOC_I2S_SUPPORTS_ETM
|
.. only:: SOC_I2S_SUPPORTS_ETM
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user