diff --git a/components/driver/i2s.c b/components/driver/i2s.c index f2085bc5ee..e433510012 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -18,10 +18,13 @@ #include "driver/i2s.h" #include "hal/gpio_hal.h" #include "hal/i2s_hal.h" -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC #include "driver/dac.h" +#endif // SOC_I2S_SUPPORTS_DAC + +#if SOC_I2S_SUPPORTS_ADC #include "adc1_private.h" -#endif +#endif // SOC_I2S_SUPPORTS_ADC #if SOC_GDMA_SUPPORTED #include "esp_private/gdma.h" @@ -47,8 +50,8 @@ static const char *TAG = "I2S"; #define I2S_EXIT_CRITICAL_ISR(i2s_num) portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) #define I2S_ENTER_CRITICAL(i2s_num) portENTER_CRITICAL(&i2s_spinlock[i2s_num]) #define I2S_EXIT_CRITICAL(i2s_num) portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) -#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) -#define I2S_FULL_DUPLEX_MASTER_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) + +#define I2S_DMA_BUFFER_MAX_SIZE 4092 #if !SOC_GDMA_SUPPORTED #define I2S_INTR_IN_SUC_EOF BIT(9) @@ -65,8 +68,8 @@ static const char *TAG = "I2S"; typedef struct { char **buf; int buf_size; - int rw_pos; - void *curr_ptr; + volatile int rw_pos; + volatile void *curr_ptr; SemaphoreHandle_t mux; xQueueHandle queue; lldesc_t **desc; @@ -82,6 +85,7 @@ typedef struct { QueueHandle_t i2s_queue; /*!< I2S queue handler*/ int dma_buf_count; /*!< DMA buffer count, number of buffer*/ int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + uint32_t last_buf_size; /*!< DMA last buffer size */ i2s_dma_t *tx; /*!< DMA Tx buffer*/ i2s_dma_t *rx; /*!< DMA Rx buffer*/ #if SOC_GDMA_SUPPORTED @@ -90,16 +94,10 @@ typedef struct { #else i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ #endif - int channel_num; /*!< Number of channels*/ - int bytes_per_sample; /*!< Bytes per sample*/ - int bits_per_sample; /*!< Bits per sample*/ - i2s_comm_format_t communication_format; /*!bck_io_num == -1 || GPIO_IS_VALID_GPIO(pin->bck_io_num)), ESP_ERR_INVALID_ARG, TAG, "bck_io_num invalid"); ESP_RETURN_ON_FALSE((pin->ws_io_num == -1 || GPIO_IS_VALID_GPIO(pin->ws_io_num)), @@ -197,45 +299,51 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) ESP_RETURN_ON_FALSE((pin->data_in_num == -1 || GPIO_IS_VALID_GPIO(pin->data_in_num)), ESP_ERR_INVALID_ARG, TAG, "data_in_num invalid"); - if (p_i2s[i2s_num]->mode & I2S_MODE_SLAVE) { - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].tx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].tx_bck_sig, 0); + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_SLAVE) { + /* For "tx + rx + slave" or "rx + slave" mode, we should select RX signal index for ws and bck */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_rx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_rx_bck_sig, 0); + /* For "tx + slave" mode, we should select TX signal index for ws and bck */ } else { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].rx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].rx_bck_sig, 0); + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_tx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_tx_bck_sig, 0); } } else { + /* mclk only available in master mode */ ESP_RETURN_ON_ERROR(i2s_check_set_mclk(i2s_num, pin->mck_io_num), TAG, "mclk config failed"); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].tx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].tx_bck_sig, 0, 0); + /* For "tx + rx + master" or "tx + master" mode, we should select TX signal index for ws and bck */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_tx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_tx_bck_sig, 0, 0); + /* For "rx + master" mode, we should select RX signal index for ws and bck */ } else { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].rx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].rx_bck_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_rx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_rx_bck_sig, 0, 0); } } + /* Set data input/ouput GPIO */ gpio_matrix_out_check_and_set(pin->data_out_num, i2s_periph_signal[i2s_num].data_out_sig, 0, 0); gpio_matrix_in_check_and_set(pin->data_in_num, i2s_periph_signal[i2s_num].data_in_sig, 0); return ESP_OK; } - -/************************************************************** - * I2S DMA operation * - * - i2s_dma_rx_callback * - * - i2s_dma_tx_callback * - * - i2s_intr_handler_default * - * - i2s_tx_reset * - * - i2s_rx_reset * - * - i2s_tx_start * - * - i2s_rx_start * - * - i2s_tx_stop * - * - i2s_rx_stop * - **************************************************************/ - +/*------------------------------------------------------------- + I2S DMA operation + -------------------------------------------------------------*/ #if SOC_GDMA_SUPPORTED +/** + * @brief GDMA rx callback function + * @note This function is called by GDMA default ISR handler + * + * @param dma_chan GDMA channel handler + * @param event_data GDMA rx event data + * @param user_data GDMA user data + * @return + * - true need yield + * - false no need + */ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -262,6 +370,16 @@ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_e return high_priority_task_awoken; } +/** + * @brief GDMA tx callback function + * @note This function is called by GDMA default ISR handler + * + * @param dma_chan GDMA channel handler + * @param event_data GDMA tx event data + * @param user_data GDMA user data + * @return + * - whether need yield + */ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -291,6 +409,13 @@ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_e } #else + +/** + * @brief I2S defalut interrupt handler + * @note This function is triggered by I2S dedicated DMA interrupt + * + * @param arg Argument transport to ISR, here is the pointer to I2S object + */ static void IRAM_ATTR i2s_intr_handler_default(void *arg) { i2s_obj_t *p_i2s = (i2s_obj_t *) arg; @@ -360,40 +485,103 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) } #endif + +/** + * @brief I2S DMA interrupt initialization + * @note I2S will use GDMA if chip supports, and the interrupt is triggered by GDMA. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK I2S DMA interrupt initialize success + * - ESP_ERR_NOT_FOUND GDMA channel not found + * - ESP_ERR_INVALID_ARG Invalid arguments + * - ESP_ERR_INVALID_STATE GDMA state error + */ +static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) +{ +#if SOC_GDMA_SUPPORTED + /* Set GDMA trigger module */ + gdma_trigger_t trig = {.periph = GDMA_TRIG_PERIPH_I2S}; + + switch (i2s_num) { +#if SOC_I2S_NUM > 1 + case I2S_NUM_1: + trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S1; + break; +#endif + default: + trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0; + break; + } + + /* Set GDMA config */ + gdma_channel_alloc_config_t dma_cfg = {}; + if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; + /* Register a new GDMA tx channel */ + ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->tx_dma_chan), TAG, "Register tx dma channel error"); + ESP_RETURN_ON_ERROR(gdma_connect(p_i2s[i2s_num]->tx_dma_chan, trig), TAG, "Connect tx dma channel error"); + gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback}; + /* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */ + gdma_register_tx_event_callbacks(p_i2s[i2s_num]->tx_dma_chan, &cb, p_i2s[i2s_num]); + } + if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; + /* Register a new GDMA rx channel */ + ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->rx_dma_chan), TAG, "Register rx dma channel error"); + ESP_RETURN_ON_ERROR(gdma_connect(p_i2s[i2s_num]->rx_dma_chan, trig), TAG, "Connect rx dma channel error"); + gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback}; + /* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */ + gdma_register_rx_event_callbacks(p_i2s[i2s_num]->rx_dma_chan, &cb, p_i2s[i2s_num]); + } +#else + /* Initial I2S module interrupt */ + ESP_RETURN_ON_ERROR(esp_intr_alloc(i2s_periph_signal[i2s_num].irq, intr_flag, i2s_intr_handler_default, p_i2s[i2s_num], &p_i2s[i2s_num]->i2s_isr_handle), TAG, "Register I2S Interrupt error"); +#endif // SOC_GDMA_SUPPORTED + return ESP_OK; +} + +/** + * @brief I2S tx reset + * + * @param i2s_num I2S device number + */ static void i2s_tx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->tx->curr_ptr = NULL; p_i2s[i2s_num]->tx->rw_pos = 0; + i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED - // gdma_stop(p_i2s[i2s_num]->tx_dma_chan); - i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); gdma_reset(p_i2s[i2s_num]->tx_dma_chan); - i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); #else - // Reset I2S TX module first, and then, reset DMA and FIFO. - i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); i2s_hal_reset_txdma(&(p_i2s[i2s_num]->hal)); - i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); #endif + i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S rx reset + * + * @param i2s_num I2S device number + */ static void i2s_rx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->rx->curr_ptr = NULL; p_i2s[i2s_num]->rx->rw_pos = 0; + i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED - i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); gdma_reset(p_i2s[i2s_num]->rx_dma_chan); - i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); #else - - // Reset I2S RX module first, and then, reset DMA and FIFO. - i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); i2s_hal_reset_rxdma(&(p_i2s[i2s_num]->hal)); - i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); #endif + i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S tx start + * + * @param i2s_num I2S device number + */ static void i2s_tx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED @@ -406,6 +594,11 @@ static void i2s_tx_start(i2s_port_t i2s_num) i2s_hal_start_tx(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S rx start + * + * @param i2s_num I2S device number + */ static void i2s_rx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED @@ -418,193 +611,269 @@ static void i2s_rx_start(i2s_port_t i2s_num) i2s_hal_start_rx(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S tx stop + * + * @param i2s_num I2S device number + */ static void i2s_tx_stop(i2s_port_t i2s_num) { + i2s_hal_stop_tx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->tx_dma_chan); #else i2s_hal_stop_tx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_stop_tx(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_tx_intr(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_tx_dma(&(p_i2s[i2s_num]->hal)); #endif } +/** + * @brief I2S rx stop + * + * @param i2s_num I2S device number + */ static void i2s_rx_stop(i2s_port_t i2s_num) { + i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->rx_dma_chan); #else i2s_hal_stop_rx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_rx_intr(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_rx_dma(&(p_i2s[i2s_num]->hal)); #endif } -/************************************************************** - * I2S buffer operation * - * - i2s_alloc_dma_buffer * - * - i2s_destroy_dma_queue * - * - i2s_create_dma_queue * - * - i2s_zero_dma_buffer * - **************************************************************/ -static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, int data_bits, int ch) +/*------------------------------------------------------------- + I2S buffer operation + -------------------------------------------------------------*/ +/** + * @brief I2S get DMA buffer size + * + * @param i2s_num I2S device number + * @return + * - DMA buffer size + */ +static inline uint32_t i2s_get_buf_size(i2s_port_t i2s_num) { - if (p_i2s[i2s_num]->channel_num != ch) { - p_i2s[i2s_num]->channel_num = (ch == 2) ? 2 : 1; - } + /* Calculate bytes per sample, align to 16 bit */ + uint32_t bytes_per_sample = ((p_i2s[i2s_num]->hal_cfg.sample_bits + 15) / 16) * 2; + /* The DMA buffer limitation is 4092 bytes */ + uint32_t bytes_per_frame = bytes_per_sample * p_i2s[i2s_num]->hal_cfg.active_chan; + p_i2s[i2s_num]->dma_buf_len = (p_i2s[i2s_num]->dma_buf_len * bytes_per_frame > I2S_DMA_BUFFER_MAX_SIZE) ? + I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame : p_i2s[i2s_num]->dma_buf_len; + return p_i2s[i2s_num]->dma_buf_len * bytes_per_frame; +} - i2s_dma_t *save_tx = NULL, *save_rx = NULL; - - if (data_bits != p_i2s[i2s_num]->bits_per_sample) { - p_i2s[i2s_num]->bits_per_sample = data_bits; - - // Round bytes_per_sample up to next multiple of 16 bits - int halfwords_per_sample = (data_bits + 15) / 16; - p_i2s[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; - - // Because limited of DMA buffer is 4092 bytes - if (p_i2s[i2s_num]->dma_buf_len * p_i2s[i2s_num]->bytes_per_sample * p_i2s[i2s_num]->channel_num > 4092) { - p_i2s[i2s_num]->dma_buf_len = 4092 / p_i2s[i2s_num]->bytes_per_sample / p_i2s[i2s_num]->channel_num; +/** + * @brief Delete DMA buffer and descriptor + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * @return + * - ESP_OK DMA buffer delete success + * - ESP_ERR_INVALID_ARG dma_obj is NULL + */ +static esp_err_t i2s_delete_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) +{ + ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); + /* Loop to destroy every descriptor and buffer */ + for (int cnt = 0; cnt < p_i2s[i2s_num]->dma_buf_count; cnt++) { + if (dma_obj->desc && dma_obj->desc[cnt]) { + free(dma_obj->desc[cnt]); + dma_obj->desc[cnt] = NULL; } - - // Re-create TX DMA buffer - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - save_tx = p_i2s[i2s_num]->tx; - //destroy old tx dma if exist - if (save_tx) { - i2s_destroy_dma_queue(i2s_num, save_tx); - } - p_i2s[i2s_num]->tx = i2s_create_dma_queue(i2s_num, p_i2s[i2s_num]->dma_buf_count, p_i2s[i2s_num]->dma_buf_len); - if (p_i2s[i2s_num]->tx == NULL) { - ESP_LOGE(TAG, "Failed to create tx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - } - // Re-create RX DMA buffer - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { - save_rx = p_i2s[i2s_num]->rx; - //destroy old rx dma if exist - if (save_rx) { - i2s_destroy_dma_queue(i2s_num, save_rx); - } - p_i2s[i2s_num]->rx = i2s_create_dma_queue(i2s_num, p_i2s[i2s_num]->dma_buf_count, p_i2s[i2s_num]->dma_buf_len); - if (p_i2s[i2s_num]->rx == NULL) { - ESP_LOGE(TAG, "Failed to create rx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_rx_eof_num(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->dma_buf_len * p_i2s[i2s_num]->channel_num * p_i2s[i2s_num]->bytes_per_sample); + if (dma_obj->buf && dma_obj->buf[cnt]) { + free(dma_obj->buf[cnt]); + dma_obj->buf[cnt] = NULL; } } return ESP_OK; } -static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +/** + * @brief Allocate memory for DMA buffer and descriptor + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * @return + * - ESP_OK Allocate success + * - ESP_ERR_NO_MEM No memory for DMA buffer + */ +static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) { - int bux_idx; - if (p_i2s[i2s_num] == NULL) { - ESP_LOGE(TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, err, TAG, "I2S DMA object can't be NULL"); + + uint32_t buf_cnt = p_i2s[i2s_num]->dma_buf_count; + for (int cnt = 0; cnt < buf_cnt; cnt++) { + /* Allocate DMA buffer */ + dma_obj->buf[cnt] = (char *) heap_caps_calloc(dma_obj->buf_size, sizeof(char), MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(dma_obj->buf[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma buffer"); + /* Initialize DMA buffer to 0 */ + memset(dma_obj->buf[cnt], 0, dma_obj->buf_size); + ESP_LOGD(TAG, "Addr[%d] = %d", cnt, (int)dma_obj->buf[cnt]); + + /* Allocate DMA descpriptor */ + dma_obj->desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(dma_obj->desc[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma description entry"); } - if (dma == NULL) { - ESP_LOGE(TAG, "dma is NULL"); - return ESP_ERR_INVALID_ARG; + /* DMA descriptor must be initialize after all descriptor has been created, otherwise they can't be linked together as a chain */ + for (int cnt = 0; cnt < buf_cnt; cnt++) { + /* Initialize DMA descriptor */ + dma_obj->desc[cnt]->owner = 1; + dma_obj->desc[cnt]->eof = 1; + dma_obj->desc[cnt]->sosf = 0; + dma_obj->desc[cnt]->length = dma_obj->buf_size; + dma_obj->desc[cnt]->size = dma_obj->buf_size; + dma_obj->desc[cnt]->buf = (uint8_t *) dma_obj->buf[cnt]; + dma_obj->desc[cnt]->offset = 0; + /* Link to the next descriptor */ + dma_obj->desc[cnt]->empty = (uint32_t)((cnt < (buf_cnt - 1)) ? (dma_obj->desc[cnt + 1]) : dma_obj->desc[0]); } - for (bux_idx = 0; bux_idx < p_i2s[i2s_num]->dma_buf_count; bux_idx++) { - if (dma->desc && dma->desc[bux_idx]) { - free(dma->desc[bux_idx]); - } - if (dma->buf && dma->buf[bux_idx]) { - free(dma->buf[bux_idx]); - } + ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_obj->buf_size, buf_cnt); + return ESP_OK; +err: + /* Delete DMA buffer if failed to allocate memory */ + i2s_delete_dma_buffer(i2s_num, dma_obj); + return ret; +} + +/** + * @brief Realloc I2S dma buffer + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM No memory for I2S tx dma buffer + */ +static esp_err_t i2s_realloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) +{ + ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); + + /* Destroy old dma descriptor and buffer */ + i2s_delete_dma_buffer(i2s_num, dma_obj); + /* Alloc new dma descriptor and buffer */ + ESP_RETURN_ON_ERROR(i2s_alloc_dma_buffer(i2s_num, dma_obj), TAG, "Failed to allocate dma buffer"); + + return ESP_OK; +} + +/** + * @brief I2S destroy the whole DMA object + * + * @param i2s_num I2S device number + * @param dma Secondary pointer to the DMA object + * @return + * - ESP_OK I2S DMA buffer has been destroyed successfully + * - ESP_ERR_INVALID_ARG I2S driver has not installed yet + */ +static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) +{ + /* Check if DMA truely need destroy */ + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S not initialized yet"); + if (!(*dma)) { + return ESP_OK; } - if (dma->buf) { - free(dma->buf); + /* Destroy every descriptor and buffer */ + i2s_delete_dma_buffer(i2s_num, (*dma)); + /* Destroy descriptor pointer */ + if ((*dma)->desc) { + free((*dma)->desc); + (*dma)->desc = NULL; } - if (dma->desc) { - free(dma->desc); + /* Destroy buffer pointer */ + if ((*dma)->buf) { + free((*dma)->buf); + (*dma)->buf = NULL; } + /* Delete DMA mux */ + vSemaphoreDelete((*dma)->mux); + /* Delete DMA queue */ + vQueueDelete((*dma)->queue); + /* Free DMA structure */ + free(*dma); + *dma = NULL; ESP_LOGI(TAG, "DMA queue destroyed"); - vQueueDelete(dma->queue); - vSemaphoreDelete(dma->mux); - free(dma); return ESP_OK; } -static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +/** + * @brief Create I2S DMA object + * @note This function only create I2S DMA object but will not allocate memory + * for DMA descriptor and buffer, call 'i2s_alloc_dma_buffer' additionally to + * allocate DMA buffer + * + * @param i2s_num I2S device number + * @param dma The secondary pointer of DMA object + * @return + * - ESP_OK The pointer of DMA object + * - ESP_ERR_INVALID_ARG NULL pointer error or DMA object has been created + * - ESP_ERR_NO_MEM No memory for new DMA object + */ +static esp_err_t i2s_create_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) { - int bux_idx; - int sample_size = p_i2s[i2s_num]->bytes_per_sample * p_i2s[i2s_num]->channel_num; - i2s_dma_t *dma = (i2s_dma_t *) malloc(sizeof(i2s_dma_t)); - if (dma == NULL) { - ESP_LOGE(TAG, "Error malloc i2s_dma_t"); - return NULL; + ESP_RETURN_ON_FALSE(dma, ESP_ERR_INVALID_ARG, TAG, "DMA object secondary pointer is NULL"); + ESP_RETURN_ON_FALSE((*dma == NULL), ESP_ERR_INVALID_ARG, TAG, "DMA object has been created"); + uint32_t buf_cnt = p_i2s[i2s_num]->dma_buf_count; + /* Allocate new DMA structure */ + *dma = (i2s_dma_t *) malloc(sizeof(i2s_dma_t)); + ESP_RETURN_ON_FALSE(*dma, ESP_ERR_NO_MEM, TAG, "DMA object allocate failed"); + /* Allocate DMA buffer poiter */ + (*dma)->buf = (char **)heap_caps_calloc(buf_cnt, sizeof(char *), MALLOC_CAP_DMA); + if (!(*dma)->buf) { + goto err; } - memset(dma, 0, sizeof(i2s_dma_t)); - - dma->buf = (char **)malloc(sizeof(char *) * dma_buf_count); - if (dma->buf == NULL) { - ESP_LOGE(TAG, "Error malloc dma buffer pointer"); - free(dma); - return NULL; + /* Allocate secondary pointer of DMA descriptor chain */ + (*dma)->desc = (lldesc_t **)heap_caps_calloc(buf_cnt, sizeof(lldesc_t *), MALLOC_CAP_DMA); + if (!(*dma)->desc) { + goto err; } - memset(dma->buf, 0, sizeof(char *) * dma_buf_count); - - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->buf[bux_idx] = (char *) heap_caps_calloc(1, dma_buf_len * sample_size, MALLOC_CAP_DMA); - if (dma->buf[bux_idx] == NULL) { - ESP_LOGE(TAG, "Error malloc dma buffer"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - ESP_LOGD(TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + /* Create queue and mutex */ + (*dma)->queue = xQueueCreate(buf_cnt - 1, sizeof(char *)); + if (!(*dma)->queue) { + goto err; + } + (*dma)->mux = xSemaphoreCreateMutex(); + if (!(*dma)->mux) { + goto err; } - dma->desc = (lldesc_t **) malloc(sizeof(lldesc_t *) * dma_buf_count); - if (dma->desc == NULL) { - ESP_LOGE(TAG, "Error malloc dma description"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx] = (lldesc_t *) heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA); - if (dma->desc[bux_idx] == NULL) { - ESP_LOGE(TAG, "Error malloc dma description entry"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx]->owner = 1; - dma->desc[bux_idx]->eof = 1; - dma->desc[bux_idx]->sosf = 0; - dma->desc[bux_idx]->length = dma_buf_len * sample_size; - dma->desc[bux_idx]->size = dma_buf_len * sample_size; - dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; - dma->desc[bux_idx]->offset = 0; - dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); - } - dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char *)); - dma->mux = xSemaphoreCreateMutex(); - dma->buf_size = dma_buf_len * sample_size; - ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); - return dma; + return ESP_OK; +err: + ESP_LOGE(TAG, "I2S DMA object create failed, preparing to uninstall"); + /* Destroy DMA queue if failed to allocate memory */ + i2s_destroy_dma_object(i2s_num, dma); + return ESP_ERR_NO_MEM; } +/** + * @brief Zero the contents of the TX DMA buffer. + * @note Pushes zero-byte samples into the TX DMA buffer, until it is full. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + + /* Clear I2S RX DMA buffer */ if (p_i2s[i2s_num]->rx && p_i2s[i2s_num]->rx->buf != NULL && p_i2s[i2s_num]->rx->buf_size != 0) { for (int i = 0; i < p_i2s[i2s_num]->dma_buf_count; i++) { memset(p_i2s[i2s_num]->rx->buf[i], 0, p_i2s[i2s_num]->rx->buf_size); } } + + /* Clear I2S TX DMA buffer */ if (p_i2s[i2s_num]->tx && p_i2s[i2s_num]->tx->buf != NULL && p_i2s[i2s_num]->tx->buf_size != 0) { - int bytes_left = 0; - bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; + /* Finish to write all tx data */ + int bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; if (bytes_left) { size_t zero_bytes = 0, bytes_written; i2s_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); @@ -616,23 +885,44 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) return ESP_OK; } -/************************************************************** - * I2S clock operation * - * - i2s_get_clk * - * - i2s_apll_get_fi2s * - * - i2s_apll_calculate_fi2s * - * - i2s_fbclk_cal * - **************************************************************/ - -float i2s_get_clk(i2s_port_t i2s_num) -{ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - return (float)p_i2s[i2s_num]->sample_rate; -} - - +/*------------------------------------------------------------- + I2S clock operation + -------------------------------------------------------------*/ #if SOC_I2S_SUPPORTS_APLL -static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) +/** + * @brief APLL calculate function, was described by following: + * APLL Output frequency is given by the formula: + * + * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) + * apll_freq = fout / ((o_div + 2) * 2) + * + * The dividend in this expression should be in the range of 240 - 600 MHz. + * In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. + * * sdm0 frequency adjustment parameter, 0..255 + * * sdm1 frequency adjustment parameter, 0..255 + * * sdm2 frequency adjustment parameter, 0..63 + * * o_div frequency divider, 0..31 + * + * The most accurate way to find the sdm0..2 and odir parameters is to loop through them all, + * then apply the above formula, finding the closest frequency to the desired one. + * But 256*256*64*32 = 134,217,728 loops are too slow with ESP32 + * 1. We will choose the parameters with the highest level of change, + * With 350MHzmclk_multiple ? p_i2s[i2s_num]->mclk_multiple : I2S_MCLK_MULTIPLE_256; - uint32_t _bck_div = rate * multi / _fbck; - i2s_clock_src_t clk_src = I2S_CLK_D2CLK; - -//ADC mode only support on ESP32, -#if SOC_I2S_SUPPORTS_ADC_DAC - if ( p_i2s[i2s_num]->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) { - _fbck = rate * I2S_LL_AD_BCK_FACTOR * 2; - _bck_div = I2S_LL_AD_BCK_FACTOR; - } -#endif // SOC_I2S_SUPPORTS_ADC_DAC - - if ( p_i2s[i2s_num]->mode & I2S_MODE_PDM) { -#if SOC_I2S_SUPPORTS_PDM_TX - if ( p_i2s[i2s_num]->mode & I2S_MODE_TX) { - int fp = i2s_hal_get_tx_pdm_fp(&(p_i2s[i2s_num]->hal)); - int fs = i2s_hal_get_tx_pdm_fs(&(p_i2s[i2s_num]->hal)); - _fbck = rate * I2S_LL_PDM_BCK_FACTOR * fp / fs; - } -#endif //SOC_I2S_SUPPORTS_PDM_TX -#if SOC_I2S_SUPPORTS_PDM_RX - if ( p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_pdm_dsr_t dsr; - i2s_hal_get_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), &dsr); - _fbck = rate * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); - } -#endif // SOC_I2S_SUPPORTS_PDM_RX - _bck_div = 8; - } - #if SOC_I2S_SUPPORTS_APLL - int sdm0 = 0; - int sdm1 = 0; - int sdm2 = 0; - int odir = 0; - //If APLL is specified, try to calculate in APLL - if (p_i2s[i2s_num]->use_apll && i2s_apll_calculate_fi2s(p_i2s[i2s_num]->fixed_mclk, channel_bit, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { - _sclk = p_i2s[i2s_num]->fixed_mclk; - clk_src = I2S_CLK_APLL; - ESP_LOGD(TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, odir); + if (use_apll) { + int sdm0 = 0; + int sdm1 = 0; + int sdm2 = 0; + int odir = 0; + if (fixed_mclk == 0) { + ESP_LOGI(TAG, "fixed_mclk is not set, set to 128 MHz as default"); + fixed_mclk = 128000000; + }else if ((fixed_mclk / p_i2s[i2s_num]->hal_cfg.chan_bits / 16) < SOC_I2S_APLL_MIN_RATE) { + ESP_LOGW(TAG, "fixed_mclk is too small, use I2S_CLK_D2CLK as default clock source"); + goto err; + } + i2s_apll_calculate_fi2s(p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, + &sdm0, &sdm1, &sdm2, &odir); + ESP_LOGI(TAG, "APLL Enabled, coefficient: sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); + rtc_clk_apll_enable(true, sdm0, sdm1, sdm2, odir); + return fixed_mclk; } -#endif // SOC_I2S_SUPPORTS_APLL - if ((_fbck * _bck_div) > _sclk) { - ESP_LOGE(TAG, "sample rate is too large\r\n"); - return ESP_ERR_INVALID_ARG; +err: + /* Set I2S_D2CLK (160M) as default I2S module clock source */ + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); + return I2S_LL_BASE_CLK; +#else + if (use_apll) { + ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source"); } - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), clk_src); - *sclk = _sclk; - *fbck = _fbck; - *bck_div = _bck_div; + /* Set I2S_D2CLK (160M) as I2S module clock source */ + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); + return I2S_LL_BASE_CLK; +#endif +} + +#if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC +/** + * @brief I2S calculate clock for built-in ADC/DAC mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_ARG, TAG, "current mode is not built-in ADC/DAC"); + + /* Set I2S module clock */ + clk_cfg->sclk = i2s_get_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, p_i2s[i2s_num]->fixed_mclk); + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = I2S_LL_AD_BCK_FACTOR; + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_AD_BCK_FACTOR * 2; + /* Get I2S master clock, mclk = bclk * bclk_div */ + clk_cfg->mclk = clk_cfg->bclk * clk_cfg->bclk_div; + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "I2S module get clock failed"); + ESP_RETURN_ON_FALSE(clk_cfg->bclk * clk_cfg->bclk_div <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + +#if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief I2S calculate clock for PDM tx mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); + + /* Set I2S module clock */ + clk_cfg->sclk = i2s_get_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, p_i2s[i2s_num]->fixed_mclk); + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = 8; + + int fp = i2s_hal_get_tx_pdm_fp(&(p_i2s[i2s_num]->hal)); + int fs = i2s_hal_get_tx_pdm_fs(&(p_i2s[i2s_num]->hal)); + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * fp / fs; + /* Get I2S master clock, mclk = bclk * bclk_div */ + clk_cfg->mclk = clk_cfg->bclk * clk_cfg->bclk_div; + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "I2S module clock is 0"); + ESP_RETURN_ON_FALSE(clk_cfg->bclk * clk_cfg->bclk_div <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_PDM_TX + +#if SOC_I2S_SUPPORTS_PDM_RX +/** + * @brief I2S calculate clock for PDM rx mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); + + /* Set I2S module clock */ + clk_cfg->sclk = i2s_get_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, p_i2s[i2s_num]->fixed_mclk); + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = 8; + + i2s_pdm_dsr_t dsr; + i2s_hal_get_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), &dsr); + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); + /* Get I2S master clock, mclk = bclk * bclk_div */ + clk_cfg->mclk = clk_cfg->bclk * clk_cfg->bclk_div; + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "I2S module clock is 0"); + ESP_RETURN_ON_FALSE(clk_cfg->bclk * clk_cfg->bclk_div <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_PDM_RX + +/** + * @brief I2S calculate clock for common mode (philip, MSB, PCM) + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + + uint32_t rate = p_i2s[i2s_num]->hal_cfg.sample_rate; + uint32_t chan_num = p_i2s[i2s_num]->hal_cfg.total_chan < 2 ? 2 : p_i2s[i2s_num]->hal_cfg.total_chan; + uint32_t chan_bit = p_i2s[i2s_num]->hal_cfg.chan_bits; + uint32_t multi; + /* Set I2S module clock */ + clk_cfg->sclk = i2s_get_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, p_i2s[i2s_num]->fixed_mclk); + /* Calculate multiple */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_MASTER) { + multi = p_i2s[i2s_num]->mclk_multiple ? p_i2s[i2s_num]->mclk_multiple : I2S_MCLK_MULTIPLE_256; + } else { + /* Only need to set the multiple of mclk to sample rate for MASTER mode, + * because BCK and WS clock are provided by the external codec in SLAVE mode. + * The multiple should be big enough to get a high module clock which could detect the edges of externel clock more accurately, + * otherwise the data we receive or send would get a large latency and go wrong due to the slow module clock. + * But on ESP32 and ESP32S2, due to the different clock work mode in hardware, + * their multiple should be set to an appropriate range according to the sample bits, + * and this particular multiple finally aims at guaranteeing the bclk_div not smaller than 8, + * if not, the I2S may can't send data or send wrong data. + * Here use 'SOC_I2S_SUPPORTS_TDM' to differentialize other chips with ESP32 and ESP32S2. + */ +#if SOC_I2S_SUPPORTS_TDM + multi = clk_cfg->sclk / rate; +#else + multi = 64 * chan_bit; +#endif + } + /* Set I2S bit clock */ + clk_cfg->bclk = rate * chan_num * chan_bit; + /* Set I2S bit clock division according to the sample rate and multiple of mclk */ + clk_cfg->bclk_div = rate * multi / clk_cfg->bclk; + /* Get I2S master clock, mclk = bclk * bclk_div = rate * multiple */ + clk_cfg->mclk = clk_cfg->bclk * clk_cfg->bclk_div; + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "I2S module clock is 0"); + ESP_RETURN_ON_FALSE(clk_cfg->bclk * clk_cfg->bclk_div <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + return ESP_OK; } -/************************************************************** - * I2S configuration * - * - i2s_get_active_chan_num * - * - i2s_set_dac_mode * - * - _i2s_adc_mode_recover * - * - i2s_set_adc_mode * - * - i2s_adc_enable * - * - i2s_adc_disable * - * - i2s_set_sample_rates * - * - i2s_pcm_config * - * - i2s_set_pdm_rx_down_sample * - * - i2s_set_pdm_tx_up_sample * - * - i2s_check_cfg_static * - * - i2s_param_config * - * - i2s_set_clk * - * - i2s_set_mode * - **************************************************************/ +/** + * @brief I2S calculate clocks according to the selected I2S mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Claculate clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + /* Calculate clock for ADC mode */ +#if SOC_I2S_SUPPORTS_ADC + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { + ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "ADC clock calculate failed"); + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_ADC + /* Calculate clock for DAC mode */ +#if SOC_I2S_SUPPORTS_DAC + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_DAC_BUILT_IN) { + ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "DAC clock calculate failed"); + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_DAC -static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) + /* Calculate clock for PDM mode */ +#if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM) { +#if SOC_I2S_SUPPORTS_PDM_TX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_tx_clock(i2s_num, clk_cfg), TAG, "PDM TX clock calculate failed"); + } +#endif // SOC_I2S_SUPPORTS_PDM_TX +#if SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_rx_clock(i2s_num, clk_cfg), TAG, "PDM RX clock calculate failed"); + } +#endif // SOC_I2S_SUPPORTS_PDM_RX + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + + /* Calculate clock for common mode */ + ESP_RETURN_ON_ERROR(i2s_calculate_common_clock(i2s_num, clk_cfg), TAG, "Common clock calculate failed"); + return ESP_OK; +} + +/*------------------------------------------------------------- + I2S configuration + -------------------------------------------------------------*/ +#if SOC_I2S_SUPPORTS_TDM +/** + * @brief Get max actived channel number + * + * @param chan_mask I2S channel mask that indicates which channels are actived + * @return + * - Max actived channel number + */ +static uint32_t i2s_get_max_channel_num(i2s_channel_t chan_mask) +{ + uint32_t max_chan = 0; + uint32_t channel = chan_mask & 0xFFFF; + for (int i = 0; channel && i < 16; i++, channel >>= 1) { + if (channel & 0x01) { + max_chan = i + 1; + } + } + /* Can't be smaller than 2 */ + return max_chan < 2 ? 2 : max_chan; +} +#endif + +/** + * @brief Get active channel number according to channel format + * @note In 'I2S_CHANNEL_FMT_MULTIPLE' format, this function will check + * 'total_chan' and fix it if it is not correct. + * + * @param hal_cfg [input/output] I2S hal configuration structer + * @return + * - Active channel number + */ +static uint32_t i2s_get_active_channel_num(const i2s_hal_config_t *hal_cfg) { switch (hal_cfg->chan_fmt) { case I2S_CHANNEL_FMT_RIGHT_LEFT: //fall through @@ -843,18 +1310,12 @@ static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) #if SOC_I2S_SUPPORTS_TDM case I2S_CHANNEL_FMT_MULTIPLE: { uint32_t num = 0; - uint32_t max_chan = 0; - uint32_t chan_mask = hal_cfg->chan_mask; - + uint32_t chan_mask = hal_cfg->chan_mask & 0xFFFF; for (int i = 0; chan_mask && i < 16; i++, chan_mask >>= 1) { - if ((chan_mask & 0x01) == 1) { + if (chan_mask & 0x01) { num++; - max_chan = i + 1; } } - if (max_chan > hal_cfg->total_chan) { - hal_cfg->total_chan = max_chan; - } return num; } #endif @@ -863,7 +1324,15 @@ static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) } } -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC +/** + * @brief I2S set built-in DAC mode + * + * @param dac_mode DAC mode + * @return + * - ESP_OK Set DAC success + * - ESP_ERR_INVALID_ARG Wrong DAC mode + */ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) { ESP_RETURN_ON_FALSE((dac_mode < I2S_DAC_CHANNEL_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s dac mode error"); @@ -885,13 +1354,31 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) } return ESP_OK; } +#endif // SOC_I2S_SUPPORTS_DAC +#if SOC_I2S_SUPPORTS_ADC +/** + * @brief ADC mode recover + * + * @return + * - ESP_OK ADC Recover success + * - ESP_ERR_INVALID_ARG ADC not initialized yet + */ static esp_err_t _i2s_adc_mode_recover(void) { ESP_RETURN_ON_FALSE(((_i2s_adc_unit != -1) && (_i2s_adc_channel != -1)), ESP_ERR_INVALID_ARG, TAG, "i2s ADC recover error, not initialized..."); return adc_i2s_mode_init(_i2s_adc_unit, _i2s_adc_channel); } +/** + * @brief I2S set adc mode + * + * @param adc_unit ADC unit number + * @param adc_channel ADC channel + * @return + * - ESP_OK ADC Recover success + * - ESP_ERR_INVALID_ARG ADC not initialized yet + */ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) { ESP_RETURN_ON_FALSE((adc_unit < ADC_UNIT_2), ESP_ERR_INVALID_ARG, TAG, "i2s ADC unit error, only support ADC1 for now"); @@ -901,23 +1388,41 @@ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) return adc_i2s_mode_init(adc_unit, adc_channel); } +/** + * @brief I2S enable ADC mode + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Enable ADC success + * - ESP_ERR_INVALID_ARG Invalid argument + * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC + */ esp_err_t i2s_adc_enable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); adc1_dma_mode_acquire(); _i2s_adc_mode_recover(); i2s_rx_reset(i2s_num); - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } +/** + * @brief I2S disable ADC + * + * @param i2s_num I2S device number + * @return + * - ESP_OK I2S ADC mode successfully disabled + * - ESP_ERR_INVALID_ARG Invalid argument + * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC + */ esp_err_t i2s_adc_disable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); adc1_lock_release(); @@ -925,25 +1430,47 @@ esp_err_t i2s_adc_disable(i2s_port_t i2s_num) } #endif - +/** + * @brief Set sample rate used for I2S RX and TX. + * @note The bit clock rate is determined by the sample rate and i2s_config_t configuration parameters (number of channels, bits_per_sample). + * `bit_clock = rate * (number of channels) * bits_per_sample` + * + * @param i2s_num I2S device number + * @param rate I2S sample rate (ex: 8000, 44100...) + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->bytes_per_sample > 0), ESP_ERR_INVALID_ARG, TAG, "bits_per_sample not set"); - return i2s_set_clk(i2s_num, rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.sample_bits > 0), ESP_ERR_INVALID_ARG, TAG, "sample bits not set"); + return i2s_set_clk(i2s_num, rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #if SOC_I2S_SUPPORTS_PCM +/** + * @brief Configure I2S a/u-law decompress or compress + * @note This function should be called after i2s driver installed + * Only take effect when the i2s 'communication_format' is set to 'I2S_COMM_FORMAT_STAND_PCM_SHORT' or 'I2S_COMM_FORMAT_STAND_PCM_LONG' + * + * @param i2s_num I2S_NUM_0 + * @param pcm_cfg Including mode selection and a/u-law decompress or compress configuration paramater + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->communication_format & I2S_COMM_FORMAT_STAND_PCM_SHORT), + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.comm_fmt & I2S_COMM_FORMAT_STAND_PCM_SHORT), ESP_ERR_INVALID_ARG, TAG, "i2s communication mode is not PCM mode"); i2s_stop(i2s_num); I2S_ENTER_CRITICAL(i2s_num); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_hal_tx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); - } else if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + } else if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_hal_rx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); } I2S_EXIT_CRITICAL(i2s_num); @@ -953,198 +1480,275 @@ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) #endif #if SOC_I2S_SUPPORTS_PDM_RX +/** + * @brief Set PDM mode down-sample rate + * In PDM RX mode, there would be 2 rounds of downsample process in hardware. + * In the first downsample process, the sampling number can be 16 or 8. + * In the second downsample process, the sampling number is fixed as 8. + * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly. + * @note After calling this function, it would call i2s_set_clk inside to update the clock frequency. + * Please call this function after I2S driver has been initialized. + * + * @param i2s_num I2S device number + * @param downsample i2s RX down sample rate for PDM mode. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsample) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); i2s_stop(i2s_num); i2s_hal_set_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), downsample); // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #endif #if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief Set TX PDM mode up-sample rate + * @note If you have set PDM mode while calling 'i2s_driver_install', + * default PDM TX upsample parameters have already been set, + * no need to call this function again if you don't have to change the default configuration + * + * @param i2s_num I2S device number + * @param upsample_cfg Set I2S PDM up-sample rate configuration + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample_cfg_t *upsample_cfg) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); i2s_stop(i2s_num); i2s_hal_set_tx_pdm_fpfs(&(p_i2s[i2s_num]->hal), upsample_cfg->fp, upsample_cfg->fs); // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #endif -static esp_err_t i2s_check_cfg_static(i2s_port_t i2s_num) +/** + * @brief I2S check the validity of configuration + * + * @param i2s_num I2S device number + * @param cfg I2S HAL configuration + * @return + * - ESP_OK I2S configuration is valid + * - ESP_ERR_INVALID_ARG I2S configuration is invalid + */ +static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, i2s_hal_config_t *cfg) { - i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; -#if SOC_I2S_SUPPORTS_ADC_DAC - //We only check if the I2S number is invalid when set to build in ADC and DAC mode. - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_ADC_BUILT_IN) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S ADC built-in only support on I2S0"); - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_DAC_BUILT_IN) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S DAC built-in only support on I2S0"); - return ESP_OK; -#endif - //We only check if the I2S number is invalid when set to PDM mode. - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S DAC PDM only support on I2S0"); - return ESP_OK; +#if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + /* Check PDM mode */ + if (cfg->mode & I2S_MODE_PDM) { + ESP_RETURN_ON_FALSE(i2s_num == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "I2S PDM mode only support on I2S0"); +#if !SOC_I2S_SUPPORTS_PDM_TX + ESP_RETURN_ON_FALSE(cfg->mode & I2S_MODE_TX, ESP_ERR_INVALID_ARG, TAG, "PDM does not support TX on this chip"); +#endif // SOC_I2S_SUPPORTS_PDM_TX +#if !SOC_I2S_SUPPORTS_PDM_RX + ESP_RETURN_ON_FALSE(cfg->mode & I2S_MODE_RX, ESP_ERR_INVALID_ARG, TAG, "PDM does not support RX on this chip"); +#endif // SOC_I2S_SUPPORTS_PDM_RX + } +#else + ESP_RETURN_ON_FALSE(!(cfg->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "I2S PDM mode not supported on current chip"); +#endif // SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX +#if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + /* Check built-in ADC/DAC mode */ + if (cfg->mode & (I2S_MODE_ADC_BUILT_IN | I2S_MODE_DAC_BUILT_IN)) { + ESP_RETURN_ON_FALSE(i2s_num == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "I2S built-in ADC/DAC only support on I2S0"); + } +#else + /* Check the transmit/receive mode */ + ESP_RETURN_ON_FALSE((cfg->mode & I2S_MODE_TX) || (cfg->mode & I2S_MODE_RX), ESP_ERR_INVALID_ARG, TAG, "I2S no TX/RX mode selected"); + /* Check communication format */ ESP_RETURN_ON_FALSE(cfg->comm_fmt && (cfg->comm_fmt < I2S_COMM_FORMAT_STAND_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid communication formats"); - ESP_RETURN_ON_FALSE(!((cfg->comm_fmt & I2S_COMM_FORMAT_STAND_MSB) && (cfg->comm_fmt & I2S_COMM_FORMAT_STAND_PCM_LONG)), ESP_ERR_INVALID_ARG, TAG, "multiple communication formats specified"); +#endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + return ESP_OK; } -static esp_err_t i2s_param_config(i2s_port_t i2s_num) +static void i2s_tx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + i2s_hal_tx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); + i2s_hal_set_tx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); +} + +static void i2s_rx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + i2s_hal_rx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); + i2s_hal_set_rx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); +} + +/** + * @brief Get clock set on particular port number. + * + * @param i2s_num I2S device number + * @return + * - sample rate + */ +float i2s_get_clk(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((i2s_check_cfg_static(i2s_num) == ESP_OK), ESP_ERR_INVALID_ARG, TAG, "param check error"); - - i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; - p_i2s[i2s_num]->communication_format = cfg->comm_fmt; -#if SOC_I2S_SUPPORTS_ADC_DAC - if ((cfg->mode & I2S_MODE_DAC_BUILT_IN) || (cfg->mode & I2S_MODE_ADC_BUILT_IN)) { - if (cfg->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_hal_enable_builtin_dac(&(p_i2s[i2s_num]->hal)); - } - if (cfg->mode & I2S_MODE_ADC_BUILT_IN) { - //in ADC built-in mode, we need to call i2s_set_adc_mode to - //initialize the specific ADC channel. - //in the current stage, we only support ADC1 and single channel mode. - //In default data mode, the ADC data is in 12-bit resolution mode. - adc_power_acquire(); - i2s_hal_enable_builtin_adc(&(p_i2s[i2s_num]->hal)); - } - } else { - i2s_hal_disable_builtin_dac(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_builtin_adc(&(p_i2s[i2s_num]->hal)); -#endif - // configure I2S data port interface. - i2s_hal_config_param(&(p_i2s[i2s_num]->hal), cfg); -#if SOC_I2S_SUPPORTS_ADC_DAC - } -#endif - if ((p_i2s[i2s_num]->mode & I2S_MODE_RX) && (p_i2s[i2s_num]->mode & I2S_MODE_TX)) { - i2s_hal_enable_sig_loopback(&(p_i2s[i2s_num]->hal)); - if (p_i2s[i2s_num]->mode & I2S_MODE_MASTER) { - i2s_hal_enable_master_fd_mode(&(p_i2s[i2s_num]->hal)); - } else { - i2s_hal_enable_slave_fd_mode(&(p_i2s[i2s_num]->hal)); - } - } - return ESP_OK; + return (float)p_i2s[i2s_num]->hal_cfg.sample_rate; } +/** + * @brief Set clock & bit width used for I2S RX and TX. + * Similar to i2s_set_sample_rates(), but also sets bit width. + * + * 1. stop i2s + * 2. calculate mclk, bck, bck_factor + * 3. set clock configurations + * 4. realloc dma buffer if DMA buffer size changed + * 5. start i2s + * + * @param i2s_num I2S device number + * @param rate I2S sample rate (ex: 8000, 44100...) + * @param bits_cfg I2S bits configuration + * the low 16 bits is for data bits per sample in one channel (see 'i2s_bits_per_sample_t') + * the high 16 bits is for total bits in one channel (see 'i2s_bits_per_chan_t') + * high 16bits =0 means same as the bits per sample. + * @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO or specific channel in TDM mode) + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch) { + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_ARG, TAG, "Not initialized yet"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S%d has not installed yet", i2s_num); i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; - int data_bits = 0; - int chan_bits = 0; - int active_chan_num = 0; - int chan_num = 0; - - cfg->ch = ch; - cfg->sample_rate = rate; - cfg->bits_cfg.val = bits_cfg; - - cfg->bits_cfg.chan_bits = cfg->bits_cfg.chan_bits < cfg->bits_cfg.sample_bits ? - cfg->bits_cfg.sample_bits : cfg->bits_cfg.chan_bits; - chan_bits = cfg->bits_cfg.chan_bits; - data_bits = cfg->bits_cfg.sample_bits; - + /* If not the first time, update configuration */ + if (p_i2s[i2s_num]->last_buf_size) { + cfg->sample_rate = rate; + cfg->sample_bits = bits_cfg & 0xFFFF; + cfg->chan_bits = (bits_cfg >> 16) > cfg->sample_bits ? (bits_cfg >> 16) : cfg->sample_bits; #if SOC_I2S_SUPPORTS_TDM - cfg->chan_mask = ch & 0xFFFF; - active_chan_num = i2s_get_active_chan_num(cfg); - chan_num = cfg->total_chan; + cfg->chan_mask = ch; + cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; + cfg->active_chan = i2s_get_active_channel_num(cfg); + uint32_t max_channel = i2s_get_max_channel_num(cfg->chan_mask); + /* If total channel is smaller than max actived channel number then set it to the max active channel number */ + cfg->total_chan = p_i2s[i2s_num]->hal_cfg.total_chan < max_channel ? max_channel : p_i2s[i2s_num]->hal_cfg.total_chan; #else - active_chan_num = i2s_get_active_chan_num(cfg); - chan_num = ch == I2S_CHANNEL_MONO ? 2 : active_chan_num; + /* Default */ + cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; + cfg->active_chan = i2s_get_active_channel_num(cfg); + cfg->total_chan = 2; #endif - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - - if ((data_bits % 8 != 0) || (data_bits > I2S_BITS_PER_SAMPLE_32BIT)) { - ESP_LOGE(TAG, "Invalid bits per sample"); - return ESP_ERR_INVALID_ARG; - } - //Stop I2S - i2s_stop(i2s_num); - // wait all on-going writing finish - if ((p_i2s[i2s_num]->mode & I2S_MODE_TX) && p_i2s[i2s_num]->tx) { - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - } - if ((p_i2s[i2s_num]->mode & I2S_MODE_RX) && p_i2s[i2s_num]->rx) { - xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); - } - //malloc DMA buffer - if (i2s_alloc_dma_buffer(i2s_num, data_bits, active_chan_num) != ESP_OK ) { - return ESP_ERR_NO_MEM; - } - - uint32_t i2s_clk = 0; // I2S source clock - uint32_t i2s_bck = 0; // I2S back clock - uint32_t bck_div = 0; // I2S bck div - //calculate bck_div, f_bck and select source clock - if (i2s_fbclk_cal(i2s_num, rate, chan_num, chan_bits, &i2s_clk, &i2s_bck, &bck_div) != ESP_OK) { - return ESP_FAIL; - } - //configure i2s clock - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - i2s_hal_tx_clock_config(&(p_i2s[i2s_num]->hal), i2s_clk, i2s_bck, bck_div); - i2s_hal_set_tx_sample_bit(&(p_i2s[i2s_num]->hal), chan_bits, data_bits); - // wait all writing on-going finish - if (p_i2s[i2s_num]->tx) { + if (cfg->mode & I2S_MODE_TX) { + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); xSemaphoreGive(p_i2s[i2s_num]->tx->mux); } - i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_hal_rx_clock_config(&(p_i2s[i2s_num]->hal), i2s_clk, i2s_bck, bck_div); - i2s_hal_set_rx_sample_bit(&(p_i2s[i2s_num]->hal), chan_bits, data_bits); - // wait all writing on-going finish - if (p_i2s[i2s_num]->rx) { + if (cfg->mode & I2S_MODE_RX) { + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); xSemaphoreGive(p_i2s[i2s_num]->rx->mux); } - i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - // Reset message queue to avoid receiving unavailable values because the old dma queque has been destroyed - if (p_i2s[i2s_num]->tx) { - xQueueReset(p_i2s[i2s_num]->tx->queue); - } - if (p_i2s[i2s_num]->rx) { - xQueueReset(p_i2s[i2s_num]->rx->queue); } + uint32_t data_bits = cfg->sample_bits; - //I2S start + /* Check the validity of sample bits */ + ESP_RETURN_ON_FALSE((data_bits % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + ESP_RETURN_ON_FALSE((data_bits <= I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + + /* Stop I2S */ + i2s_stop(i2s_num); + + i2s_hal_clock_cfg_t clk_cfg; + /* To get sclk, mclk, mclk_div bclk and bclk_div */ + i2s_calculate_clock(i2s_num, &clk_cfg); + + uint32_t buf_size = i2s_get_buf_size(i2s_num); + bool need_realloc = p_i2s[i2s_num]->last_buf_size != buf_size; + + /* TX mode clock reset */ + if (cfg->mode & I2S_MODE_TX) { + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->tx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); + /* Waiting for transmit finish */ + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + i2s_tx_set_clk_and_channel(i2s_num, &clk_cfg); + /* If buffer size changed, the DMA buffer need realloc */ + if (need_realloc) { + p_i2s[i2s_num]->tx->buf_size = buf_size; + ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->tx); + } + /* If not the first time, update I2S tx channel style */ + if (p_i2s[i2s_num]->last_buf_size) { + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + } + /* Reset the queue to avoid receive invalid data */ + xQueueReset(p_i2s[i2s_num]->tx->queue); + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d tx DMA buffer malloc failed", i2s_num); + } + /* RX mode clock reset */ + if (cfg->mode & I2S_MODE_RX) { + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->rx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); + /* Waiting for receive finish */ + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + i2s_rx_set_clk_and_channel(i2s_num, &clk_cfg); + /* If buffer size changed, the DMA buffer need realloc */ + if (need_realloc) { + p_i2s[i2s_num]->rx->buf_size = buf_size; + ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->rx); + /* Reset the end-of-frame number */ + i2s_hal_set_rx_eof_num(&(p_i2s[i2s_num]->hal), buf_size); + } + /* If not the first time, update I2S rx channel style */ + if (p_i2s[i2s_num]->last_buf_size) { + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + } + /* Reset the queue to avoid receiving invalid data */ + xQueueReset(p_i2s[i2s_num]->rx->queue); + xSemaphoreGive(p_i2s[i2s_num]->rx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d rx DMA buffer malloc failed", i2s_num); + } + /* Update last buffer size */ + p_i2s[i2s_num]->last_buf_size = buf_size; + + /* I2S start */ i2s_start(i2s_num); - p_i2s[i2s_num]->sample_rate = rate; + return ESP_OK; } - - -/************************************************************** - * I2S driver operation * - * - i2s_start * - * - i2s_stop * - * - i2s_driver_install * - * - i2s_write * - * - i2s_write_expand * - * - i2s_read * - **************************************************************/ - +/*------------------------------------------------------------- + I2S driver operation + -------------------------------------------------------------*/ +/** + * @brief Start I2S driver + * @note It is not necessary to call this function after i2s_driver_install() (it is started automatically), however it is necessary to call it after i2s_stop(). + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_start(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); //start DMA link I2S_ENTER_CRITICAL(i2s_num); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_tx_reset(i2s_num); i2s_tx_start(i2s_num); } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_rx_reset(i2s_num); i2s_rx_start(i2s_num); } @@ -1155,6 +1759,17 @@ esp_err_t i2s_start(i2s_port_t i2s_num) return ESP_OK; } +/** + * @brief Stop I2S driver + * @note There is no need to call i2s_stop() before calling i2s_driver_uninstall(). + * Disables I2S TX/RX, until i2s_start() is called. + * + * @param i2s_num I2S device number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_stop(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); @@ -1162,10 +1777,10 @@ esp_err_t i2s_stop(i2s_port_t i2s_num) #if !SOC_GDMA_SUPPORTED esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle); #endif - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_tx_stop(i2s_num); } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_rx_stop(i2s_num); } #if !SOC_GDMA_SUPPORTED @@ -1175,17 +1790,135 @@ esp_err_t i2s_stop(i2s_port_t i2s_num) return ESP_OK; } +/** + * @brief Initialize I2S driver configurations + * + * @param i2s_num I2S device number + * @param i2s_config I2S configurations - see i2s_config_t struct + * @return + * - ESP_OK I2S initialize success + * - ESP_ERR_INVALID_ARG No channel enabled in multiple channel format + */ +static esp_err_t i2s_driver_init(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "The pointer of I2S configuration structure is NULL"); + + /* I2S driver configuration assignment */ + p_i2s[i2s_num]->i2s_num = i2s_num; + p_i2s[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s[i2s_num]->last_buf_size = 0; + p_i2s[i2s_num]->use_apll = i2s_config->use_apll; + p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; + p_i2s[i2s_num]->mclk_multiple = i2s_config->mclk_multiple; + p_i2s[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; + + /* I2S HAL configuration assignment */ + p_i2s[i2s_num]->hal_cfg.mode = i2s_config->mode; + p_i2s[i2s_num]->hal_cfg.sample_rate = i2s_config->sample_rate; + p_i2s[i2s_num]->hal_cfg.comm_fmt = i2s_config->communication_format; + p_i2s[i2s_num]->hal_cfg.chan_fmt = i2s_config->channel_format; + p_i2s[i2s_num]->hal_cfg.sample_bits = i2s_config->bits_per_sample; + p_i2s[i2s_num]->hal_cfg.chan_bits = (uint32_t)i2s_config->bits_per_chan < (uint32_t)i2s_config->bits_per_sample ? + (uint32_t)i2s_config->bits_per_sample : (uint32_t)i2s_config->bits_per_chan; + +#if SOC_I2S_SUPPORTS_TDM + /* I2S HAL TDM configuration assignment */ + p_i2s[i2s_num]->hal_cfg.left_align = i2s_config->left_align; + p_i2s[i2s_num]->hal_cfg.big_edin = i2s_config->big_edin; + p_i2s[i2s_num]->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; + p_i2s[i2s_num]->hal_cfg.skip_msk = i2s_config->skip_msk; + + /* Set chan_mask according to channel format */ + switch (i2s_config->channel_format) { + case I2S_CHANNEL_FMT_RIGHT_LEFT: // fall through + case I2S_CHANNEL_FMT_ALL_RIGHT: // fall through + case I2S_CHANNEL_FMT_ALL_LEFT: + p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; + p_i2s[i2s_num]->hal_cfg.total_chan = 2; + break; + case I2S_CHANNEL_FMT_ONLY_RIGHT: // fall through + case I2S_CHANNEL_FMT_ONLY_LEFT: + p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0; + p_i2s[i2s_num]->hal_cfg.total_chan = 2; + break; + case I2S_CHANNEL_FMT_MULTIPLE: + ESP_RETURN_ON_FALSE(i2s_config->chan_mask, ESP_ERR_INVALID_ARG, TAG, "i2s all channel are disabled"); + p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->chan_mask; + /* Get the max actived channel number */ + uint32_t max_channel = i2s_get_max_channel_num(p_i2s[i2s_num]->hal_cfg.chan_mask); + /* If total channel is smaller than max actived channel number then set it to the max active channel number */ + p_i2s[i2s_num]->hal_cfg.total_chan = p_i2s[i2s_num]->hal_cfg.total_chan < max_channel ? max_channel : + p_i2s[i2s_num]->hal_cfg.total_chan; + break; + default: + ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "wrong i2s channel format, going to uninstall i2s"); + } + + /* Calculate actived channel number in channel mask */ + p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); + +#else + /* Calculate actived channel number in channel mask */ + p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); + /* Total channel number is equal to the actived channel number in non-TDM mode */ + p_i2s[i2s_num]->hal_cfg.total_chan = 2; +#endif + return ESP_OK; +} + +/** + * @brief Initialize I2S DMA object + * + * @param i2s_num I2S device number + * @return + * - ESP_OK DMA object initialize success + * - ESP_ERR_NO_MEM No memory for DMA object + */ +static esp_err_t i2s_dma_object_init(i2s_port_t i2s_num) +{ + uint32_t buf_size = i2s_get_buf_size(i2s_num); + /* Create DMA object */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->tx), TAG, "I2S TX DMA object create failed"); + p_i2s[i2s_num]->tx->buf_size = buf_size; + } + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->rx), TAG, "I2S RX DMA object create failed"); + p_i2s[i2s_num]->rx->buf_size = buf_size; + } + return ESP_OK; +} + +/** + * @brief Install and start I2S driver. + * @note This function must be called before any I2S driver read/write operations. + * + * + * @param i2s_num I2S device number + * @param i2s_config I2S configurations - see i2s_config_t struct + * @param queue_size I2S event queue size/depth. + * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue) { - esp_err_t ret = ESP_FAIL; - i2s_obj_t *pre_alloc_i2s_obj = NULL; + esp_err_t ret = ESP_OK; + + /* Step 1: Check the validity of input parameters */ + /* Check the validity of i2s device number */ ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((i2s_config != NULL), ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not NULL"); + ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL"); + /* Check the size of DMA buffer */ ESP_RETURN_ON_FALSE((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), ESP_ERR_INVALID_ARG, TAG, "I2S buffer count less than 128 and more than 2"); ESP_RETURN_ON_FALSE((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), ESP_ERR_INVALID_ARG, TAG, "I2S buffer length at most 1024 and more than 8"); - // alloc driver object and register to platform - pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); + /* Step 2: Allocate driver object and register to platform */ + i2s_obj_t *pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); ESP_RETURN_ON_FALSE(pre_alloc_i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver"); ret = i2s_priv_register_object(pre_alloc_i2s_obj, i2s_num); if (ret != ESP_OK) { @@ -1193,139 +1926,78 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, ESP_LOGE(TAG, "register I2S object to platform failed"); return ret; } - // initialize HAL layer + + /* Step 3: Initialize I2S object, assign configarations */ + ESP_GOTO_ON_ERROR(i2s_driver_init(i2s_num, i2s_config), err, TAG, "I2S init failed"); + /* Check the validity of I2S configuration */ + ESP_GOTO_ON_ERROR(i2s_check_cfg_validity(i2s_num, &(pre_alloc_i2s_obj->hal_cfg)), err, TAG, "I2S configuration is invalid"); + + /* Get device instance */ i2s_hal_init(&(pre_alloc_i2s_obj->hal), i2s_num); - // Set I2S HAL configurations - pre_alloc_i2s_obj->hal_cfg.mode = i2s_config->mode; - pre_alloc_i2s_obj->hal_cfg.sample_rate = i2s_config->sample_rate; - pre_alloc_i2s_obj->hal_cfg.comm_fmt = i2s_config->communication_format; - pre_alloc_i2s_obj->hal_cfg.chan_fmt = i2s_config->channel_format; - pre_alloc_i2s_obj->hal_cfg.bits_cfg.sample_bits = i2s_config->bits_per_sample; - pre_alloc_i2s_obj->hal_cfg.bits_cfg.chan_bits = i2s_config->bits_per_chan; -#if SOC_I2S_SUPPORTS_TDM - int active_chan = 0; - switch (i2s_config->channel_format) { - case I2S_CHANNEL_FMT_RIGHT_LEFT: - case I2S_CHANNEL_FMT_ALL_RIGHT: - case I2S_CHANNEL_FMT_ALL_LEFT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; - pre_alloc_i2s_obj->hal_cfg.total_chan = 2; - active_chan = 2; - break; - case I2S_CHANNEL_FMT_ONLY_RIGHT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; - pre_alloc_i2s_obj->hal_cfg.total_chan = 1; - active_chan = 1; - break; - case I2S_CHANNEL_FMT_ONLY_LEFT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; - pre_alloc_i2s_obj->hal_cfg.total_chan = 1; - active_chan = 1; - break; - case I2S_CHANNEL_FMT_MULTIPLE: - ESP_GOTO_ON_FALSE(i2s_config->chan_mask != 0, ESP_ERR_INVALID_ARG, err, TAG, "i2s all channel are disabled"); - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->chan_mask; - i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid I2S channel format:%d", i2s_config->channel_format); - } - pre_alloc_i2s_obj->hal_cfg.left_align = i2s_config->left_align; - pre_alloc_i2s_obj->hal_cfg.big_edin = i2s_config->big_edin; - pre_alloc_i2s_obj->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; - pre_alloc_i2s_obj->hal_cfg.skip_msk = i2s_config->skip_msk; -#endif - - // Set I2S driver configurations - pre_alloc_i2s_obj->i2s_num = i2s_num; - pre_alloc_i2s_obj->mode = i2s_config->mode; - pre_alloc_i2s_obj->channel_num = i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); - pre_alloc_i2s_obj->i2s_queue = i2s_queue; - pre_alloc_i2s_obj->bits_per_sample = 0; - pre_alloc_i2s_obj->bytes_per_sample = 0; // Not initialized yet - pre_alloc_i2s_obj->dma_buf_count = i2s_config->dma_buf_count; - pre_alloc_i2s_obj->dma_buf_len = i2s_config->dma_buf_len; - pre_alloc_i2s_obj->mclk_multiple = i2s_config->mclk_multiple; - #ifdef CONFIG_PM_ENABLE + esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX; #if SOC_I2S_SUPPORTS_APLL if (i2s_config->use_apll) { - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); - } else + pm_lock = ESP_PM_NO_LIGHT_SLEEP; + } #endif // SOC_I2S_SUPPORTS_APLL - { - ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); - } - ESP_GOTO_ON_ERROR(ret, err, TAG, "create PM lock failed"); + ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock), err, TAG, "I2S pm lock error"); #endif //CONFIG_PM_ENABLE -#if SOC_GDMA_SUPPORTED - ret = ESP_OK; - gdma_trigger_t trig = {.periph = GDMA_TRIG_PERIPH_I2S}; -#if SOC_I2S_NUM > 1 - trig.instance_id = (i2s_num == I2S_NUM_0) ? SOC_GDMA_TRIG_PERIPH_I2S0 : SOC_GDMA_TRIG_PERIPH_I2S1; -#else - trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0; + + /* Step 4: Initialize I2S DMA interrupt and DMA object */ + ESP_GOTO_ON_ERROR(i2s_dma_intr_init(i2s_num, i2s_config->intr_alloc_flags), err, TAG, "I2S interrupt initailze failed"); + /* Initialize I2S DMA object */ + ESP_GOTO_ON_ERROR(i2s_dma_object_init(i2s_num), err, TAG, "I2S dma object create failed"); + +#if SOC_I2S_SUPPORTS_ADC + /* If using built-in ADC, we need to enable ADC power manerge*/ + if (pre_alloc_i2s_obj->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { + adc_power_acquire(); + } #endif - gdma_channel_alloc_config_t dma_cfg = {.flags.reserve_sibling = 1}; - if (pre_alloc_i2s_obj->mode & I2S_MODE_RX) { - dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->rx_dma_chan), err, TAG, "Register rx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->rx_dma_chan, trig), err, TAG, "Connect rx dma channel error"); - gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback}; - gdma_register_rx_event_callbacks(pre_alloc_i2s_obj->rx_dma_chan, &cb, pre_alloc_i2s_obj); - } - if (pre_alloc_i2s_obj->mode & I2S_MODE_TX) { - dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->tx_dma_chan), err, TAG, "Register tx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->tx_dma_chan, trig), err, TAG, "Connect tx dma channel error"); - gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback}; - gdma_register_tx_event_callbacks(pre_alloc_i2s_obj->tx_dma_chan, &cb, pre_alloc_i2s_obj); - } -#else - //initial interrupt - ret = esp_intr_alloc(i2s_periph_signal[i2s_num].irq, i2s_config->intr_alloc_flags, i2s_intr_handler_default, pre_alloc_i2s_obj, &pre_alloc_i2s_obj->i2s_isr_handle); - ESP_GOTO_ON_ERROR(ret, err, TAG, "Register I2S Interrupt error"); -#endif // SOC_GDMA_SUPPORTED - i2s_stop(i2s_num); - pre_alloc_i2s_obj->use_apll = i2s_config->use_apll; - pre_alloc_i2s_obj->fixed_mclk = i2s_config->fixed_mclk; - pre_alloc_i2s_obj->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - ret = i2s_param_config(i2s_num); - ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S param configure error"); + /* Enable module clock */ + i2s_hal_enable_module_clock(&p_i2s[i2s_num]->hal); + + /* Step 5: Initialize I2S configuration and set the configurations to register */ + i2s_hal_config_param(&(pre_alloc_i2s_obj->hal), &pre_alloc_i2s_obj->hal_cfg); + + /* Step 6: Initialise i2s event queue if user needs */ if (i2s_queue) { pre_alloc_i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - ESP_GOTO_ON_ERROR((pre_alloc_i2s_obj->i2s_queue != NULL), err, TAG, "I2S queue create failed"); + printf("queue handle %p\n", pre_alloc_i2s_obj->i2s_queue); + ESP_GOTO_ON_FALSE(pre_alloc_i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed"); *((QueueHandle_t *) i2s_queue) = pre_alloc_i2s_obj->i2s_queue; ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(pre_alloc_i2s_obj->i2s_queue)); } else { pre_alloc_i2s_obj->i2s_queue = NULL; } - //set clock and start -#if SOC_I2S_SUPPORTS_TDM - ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, - (i2s_channel_t)active_chan); -#else - ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, - I2S_CHANNEL_STEREO); -#endif - ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S set clock failed"); - return ret; + /* Step 7: Set I2S clocks and start. No need to give parameters since configurations has been set in 'i2s_driver_init' */ + ESP_GOTO_ON_ERROR(i2s_set_clk(i2s_num, 0, 0, 0), err, TAG, "I2S set clock failed"); + return ESP_OK; + err: + /* I2S install failed, prepare to uninstall */ i2s_driver_uninstall(i2s_num); return ret; } +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num); i2s_obj_t *obj = p_i2s[i2s_num]; i2s_stop(i2s_num); -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); #endif #if SOC_GDMA_SUPPORTED @@ -1342,14 +2014,9 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) esp_intr_free(p_i2s[i2s_num]->i2s_isr_handle); } #endif - if (p_i2s[i2s_num]->tx != NULL && p_i2s[i2s_num]->mode & I2S_MODE_TX) { - i2s_destroy_dma_queue(i2s_num, p_i2s[i2s_num]->tx); - p_i2s[i2s_num]->tx = NULL; - } - if (p_i2s[i2s_num]->rx != NULL && p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_destroy_dma_queue(i2s_num, p_i2s[i2s_num]->rx); - p_i2s[i2s_num]->rx = NULL; - } + /* Destroy dma object if exist */ + i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->tx); + i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->rx); if (p_i2s[i2s_num]->i2s_queue) { vQueueDelete(p_i2s[i2s_num]->i2s_queue); @@ -1367,20 +2034,41 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) #ifdef CONFIG_PM_ENABLE if (p_i2s[i2s_num]->pm_lock) { esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); + p_i2s[i2s_num]->pm_lock = NULL; } #endif + /* Disable module clock */ + i2s_hal_disable_module_clock(&p_i2s[i2s_num]->hal); i2s_priv_deregister_object(i2s_num); free(obj); return ESP_OK; } +/** + * @brief Write data to I2S DMA transmit buffer. + * @note Many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * + * @param i2s_num I2S device number + * @param src Source address to write from + * @param size Size of data in bytes + * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait) { + esp_err_t ret = ESP_OK; char *data_ptr, *src_byte; size_t bytes_can_write; *bytes_written = 0; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "tx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled"); xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); @@ -1389,6 +2077,7 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by while (size > 0) { if (p_i2s[i2s_num]->tx->rw_pos == p_i2s[i2s_num]->tx->buf_size || p_i2s[i2s_num]->tx->curr_ptr == NULL) { if (xQueueReceive(p_i2s[i2s_num]->tx->queue, &p_i2s[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + ret = ESP_ERR_TIMEOUT; break; } p_i2s[i2s_num]->tx->rw_pos = 0; @@ -1409,13 +2098,33 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by #ifdef CONFIG_PM_ENABLE esp_pm_lock_release(p_i2s[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - return ESP_OK; + return ret; } +/** + * @brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. + * @note Many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * Format of the data in source buffer is determined by the I2S configuration (see i2s_config_t). + * + * @param i2s_num I2S device number + * @param src Source address to write from + * @param size Size of data in bytes + * @param src_bits Source audio bit + * @param aim_bits Bit wanted, no more than 32, and must be greater than src_bits + * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait) { + esp_err_t ret = ESP_OK; char *data_ptr; int bytes_can_write, tail; int src_bytes, aim_bytes, zero_bytes; @@ -1423,7 +2132,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((size > 0), ESP_ERR_INVALID_ARG, TAG, "size must greater than zero"); ESP_RETURN_ON_FALSE((aim_bits >= src_bits), ESP_ERR_INVALID_ARG, TAG, "aim_bits mustn't be less than src_bits"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "tx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled"); if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) { ESP_LOGE(TAG, "bits mustn't be less than 8, src_bits %d aim_bits %d", src_bits, aim_bits); return ESP_ERR_INVALID_ARG; @@ -1450,6 +2159,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz while (size > 0) { if (p_i2s[i2s_num]->tx->rw_pos == p_i2s[i2s_num]->tx->buf_size || p_i2s[i2s_num]->tx->curr_ptr == NULL) { if (xQueueReceive(p_i2s[i2s_num]->tx->queue, &p_i2s[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + ret = ESP_ERR_TIMEOUT; break; } p_i2s[i2s_num]->tx->rw_pos = 0; @@ -1473,9 +2183,23 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz p_i2s[i2s_num]->tx->rw_pos += bytes_can_write; } xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - return ESP_OK; + return ret; } +/** + * @brief Read data from I2S DMA receive buffer + * @note If the built-in ADC mode is enabled, we should call i2s_adc_enable and i2s_adc_disable around the whole reading process, + * to prevent the data getting corrupted. + * + * @param i2s_num I2S device number + * @param dest Destination address to read into + * @param size Size of data in bytes + * @param[out] bytes_read Number of bytes read, if timeout, bytes read will be less than the size passed in. + * @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many ticks pass without bytes becoming available in the DMA receive buffer, then the function will return (note that if data is read from the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait) { esp_err_t ret = ESP_OK; @@ -1484,7 +2208,7 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re *bytes_read = 0; dest_byte = (char *)dest; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "rx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "RX mode is not enabled"); xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index 0c8a43db0d..6fffd9311e 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -17,7 +17,7 @@ #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC #include "driver/adc.h" #endif @@ -355,7 +355,7 @@ esp_err_t i2s_stop(i2s_port_t i2s_num); * * @param i2s_num I2S port number * -* @return + * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ @@ -430,7 +430,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ */ float i2s_get_clk(i2s_port_t i2s_num); -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC /** * @brief Set built-in ADC mode for I2S DMA, this function will initialize ADC pad, * and set ADC parameters. @@ -466,7 +466,9 @@ esp_err_t i2s_adc_enable(i2s_port_t i2s_num); * - ESP_ERR_INVALID_STATE Driver state error */ esp_err_t i2s_adc_disable(i2s_port_t i2s_num); +#endif // SOC_I2S_SUPPORTS_ADC +#if SOC_I2S_SUPPORTS_DAC /** * @brief Set I2S dac mode, I2S built-in DAC is disabled by default * @@ -481,7 +483,7 @@ esp_err_t i2s_adc_disable(i2s_port_t i2s_num); * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode); -#endif //SOC_I2S_SUPPORTS_ADC_DAC +#endif //SOC_I2S_SUPPORTS_DAC #ifdef __cplusplus diff --git a/components/driver/test/test_i2s.c b/components/driver/test/test_i2s.c index 2e303b6e3d..5a0e79755d 100644 --- a/components/driver/test/test_i2s.c +++ b/components/driver/test/test_i2s.c @@ -380,11 +380,12 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]") i2s_write(I2S_NUM_1, data_wr, sizeof(uint8_t) * 400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); printf("write data size: %d\n", i2s_bytes_write); int flag = 0; // break loop flag - int end_position = 0; + volatile int end_position = 0; // write data to slave while (!flag) { TEST_ESP_OK(i2s_read(I2S_NUM_0, i2s_read_buff + length, 10000 - length, &bytes_read, 1000 / portTICK_PERIOD_MS)); if (bytes_read > 0) { + printf("read data size: %d\n", bytes_read); for (int i = length; i < length + bytes_read; i++) { if (i2s_read_buff[i] == 100) { flag = 1; @@ -494,7 +495,7 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]") TEST_ASSERT(initial_size == esp_get_free_heap_size()); } -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC /* Only ESP32 need I2S adc/dac test */ TEST_CASE("I2S adc test", "[i2s]") { @@ -560,7 +561,9 @@ TEST_CASE("I2S adc test", "[i2s]") free(i2sReadBuffer); i2s_driver_uninstall(I2S_NUM_0); } +#endif +#if SOC_I2S_SUPPORTS_DAC TEST_CASE("I2S dac test", "[i2s]") { // dac, adc i2s diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c index f132b3bd29..653a50a016 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c @@ -536,7 +536,7 @@ static unsigned long i2s_lcd_select_periph_clock(i2s_hal_context_t *hal) #error "invalid LCD peripheral clock source" #endif i2s_ll_tx_clk_set_src(hal->dev, clock_source); - i2s_ll_clk_cal_t clk_cal_config = { + i2s_ll_mclk_div_t clk_cal_config = { .mclk_div = LCD_PERIPH_CLOCK_PRE_SCALE, .a = 1, .b = 0, diff --git a/components/hal/esp32/include/hal/i2s_ll.h b/components/hal/esp32/include/hal/i2s_ll.h index 159446b41d..fd20c406cb 100644 --- a/components/hal/esp32/include/hal/i2s_ll.h +++ b/components/hal/esp32/include/hal/i2s_ll.h @@ -50,7 +50,7 @@ typedef struct { uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; uint16_t b; // The decimal part of module clock devider, the decimal is: b/a -} i2s_ll_clk_cal_t; +} i2s_ll_mclk_div_t; /** * @brief Enable DMA descriptor owner check @@ -98,6 +98,18 @@ static inline void i2s_ll_enable_clock(i2s_dev_t *hw) } } +/** + * @brief I2S module disable clock. + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_disable_clock(i2s_dev_t *hw) +{ + if (hw->clkm_conf.clk_en == 1) { + hw->clkm_conf.clk_en = 0; + } +} + /** * @brief I2S tx msb right enable * @@ -272,7 +284,7 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); hw->clkm_conf.clkm_div_b = set->b; @@ -296,7 +308,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); hw->clkm_conf.clkm_div_b = set->b; @@ -713,9 +725,9 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) * @brief Enable I2S loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param loopback_en Set true to enable loopback mode. + * @param loopback_en Set true to share BCK and WS signal for tx module and rx module. */ -static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool loopback_en) +static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool loopback_en) { hw->conf.sig_loopback = loopback_en; } diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index 30fbfb7e51..670db91616 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -44,7 +44,7 @@ typedef struct { uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; uint16_t b; // The decimal part of module clock devider, the decimal is: b/a -} i2s_ll_clk_cal_t; +} i2s_ll_mclk_div_t; /** * @brief I2S module general init, enable I2S clock. @@ -56,6 +56,16 @@ static inline void i2s_ll_enable_clock(i2s_dev_t *hw) hw->tx_clkm_conf.clk_en = 1; } +/** + * @brief I2S module disable I2S clock. + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_disable_clock(i2s_dev_t *hw) +{ + hw->tx_clkm_conf.clk_en = 0; +} + /** * @brief Enable I2S tx module clock * @@ -201,7 +211,7 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; @@ -240,7 +250,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; @@ -781,13 +791,41 @@ static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) hw->conf_single_data = data; } +/** + * @brief Enable TX mono mode + * @note MONO in hardware means only one channel got data, but another doesn't + * MONO in software means two channel share same data + * This function aims to use MONO in software meaning + * so 'tx_mono' and 'tx_chan_equal' should be enabled at the same time + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->tx_conf.tx_mono = mono_ena; + hw->tx_conf.tx_chan_equal = mono_ena; +} + +/** + * @brief Enable RX mono mode + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->rx_conf.rx_mono = mono_ena; + hw->rx_conf.rx_mono_fst_vld = mono_ena; +} + /** * @brief Enable loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param ena Set true to enable loopback mode. + * @param ena Set true to share BCK and WS signal for tx module and rx module. */ -static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) +static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool ena) { hw->tx_conf.sig_loopback = ena; } diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index 9819a8ed4b..e241ac2185 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -45,7 +45,7 @@ typedef struct { uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; uint16_t b; // The decimal part of module clock devider, the decimal is: b/a -} i2s_ll_clk_cal_t; +} i2s_ll_mclk_div_t; /** * @brief I2S module general init, enable I2S clock. @@ -57,6 +57,16 @@ static inline void i2s_ll_enable_clock(i2s_dev_t *hw) hw->tx_clkm_conf.clk_en = 1; } +/** + * @brief I2S module disable I2S clock. + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_disable_clock(i2s_dev_t *hw) +{ + hw->tx_clkm_conf.clk_en = 0; +} + /** * @brief Enable I2S tx module clock * @@ -202,7 +212,7 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; @@ -241,7 +251,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; @@ -785,9 +795,9 @@ static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) * @brief Enable loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param ena Set true to enable loopback mode. + * @param ena Set true to share BCK and WS signal for tx module and rx module. */ -static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) +static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool ena) { hw->tx_conf.sig_loopback = ena; } @@ -804,6 +814,34 @@ static inline void i2s_ll_set_pdm2pcm_conv_en(i2s_dev_t *hw, bool val) } +/** + * @brief Enable TX mono mode + * @note MONO in hardware means only one channel got data, but another doesn't + * MONO in software means two channel share same data + * This function aims to use MONO in software meaning + * so 'tx_mono' and 'tx_chan_equal' should be enabled at the same time + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->tx_conf.tx_mono = mono_ena; + hw->tx_conf.tx_chan_equal = mono_ena; +} + +/** + * @brief Enable RX mono mode + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->rx_conf.rx_mono = mono_ena; + hw->rx_conf.rx_mono_fst_vld = mono_ena; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 725d4343b2..6f453221ee 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -45,7 +45,7 @@ typedef struct { uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; uint16_t b; // The decimal part of module clock devider, the decimal is: b/a -} i2s_ll_clk_cal_t; +} i2s_ll_mclk_div_t; #define I2S_LL_EVENT_TX_EOF (1 << 12) #define I2S_LL_BCK_MAX_PRESCALE (64) @@ -97,6 +97,18 @@ static inline void i2s_ll_enable_clock(i2s_dev_t *hw) } } +/** + * @brief I2S module disable clock. + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_disable_clock(i2s_dev_t *hw) +{ + if (hw->clkm_conf.clk_en == 1) { + hw->clkm_conf.clk_en = 0; + } +} + /** * @brief I2S tx msb right enable * @@ -268,7 +280,7 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); hw->clkm_conf.clkm_div_b = set->b; @@ -292,7 +304,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); hw->clkm_conf.clkm_div_b = set->b; @@ -813,9 +825,9 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) * @brief Enable I2S loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param loopback_en Set true to enable loopback mode. + * @param loopback_en Set true to share BCK and WS signal for tx module and rx module. */ -static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool loopback_en) +static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool loopback_en) { hw->conf.sig_loopback = loopback_en; } diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index 791918fae0..5e27b9352e 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/components/hal/esp32s3/include/hal/i2s_ll.h @@ -45,7 +45,7 @@ typedef struct { uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; uint16_t b; // The decimal part of module clock devider, the decimal is: b/a -} i2s_ll_clk_cal_t; +} i2s_ll_mclk_div_t; /** * @brief I2S module general init, enable I2S clock. @@ -57,6 +57,16 @@ static inline void i2s_ll_enable_clock(i2s_dev_t *hw) hw->tx_clkm_conf.clk_en = 1; } +/** + * @brief I2S module disable I2S clock. + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_disable_clock(i2s_dev_t *hw) +{ + hw->tx_clkm_conf.clk_en = 0; +} + /** * @brief Enable I2S tx module clock * @@ -204,7 +214,7 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; @@ -243,7 +253,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) * @param hw Peripheral I2S hardware instance address. * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) { if (set->a == 0 || set->b == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; @@ -806,13 +816,41 @@ static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) hw->conf_single_data = data; } +/** + * @brief Enable TX mono mode + * @note MONO in hardware means only one channel got data, but another doesn't + * MONO in software means two channel share same data + * This function aims to use MONO in software meaning + * so 'tx_mono' and 'tx_chan_equal' should be enabled at the same time + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->tx_conf.tx_mono = mono_ena; + hw->tx_conf.tx_chan_equal = mono_ena; +} + +/** + * @brief Enable RX mono mode + * + * @param hw Peripheral I2S hardware instance address. + * @param mono_ena Set true to enable mono mde. + */ +static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) +{ + hw->rx_conf.rx_mono = mono_ena; + hw->rx_conf.rx_mono_fst_vld = mono_ena; +} + /** * @brief Enable loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param ena Set true to enable loopback mode. + * @param ena Set true to share BCK and WS signal for tx module and rx module. */ -static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) +static inline void i2s_ll_share_bck_ws(i2s_dev_t *hw, bool ena) { hw->tx_conf.sig_loopback = ena; } diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index c0ff7a6f2b..1f49ac1c65 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -19,35 +19,30 @@ #include "soc/soc_caps.h" #include "hal/i2s_hal.h" -#define I2S_MODE_I2S (I2S_MODE_MASTER|I2S_MODE_SLAVE|I2S_MODE_TX|I2S_MODE_RX) /*!< I2S normal mode*/ - /** * @brief Calculate the closest sample rate clock configuration. * clock relationship: * Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) * - * @param fsclk I2S source clock freq. - * @param fbck BCK freuency. - * @param bck_div The BCK devider of bck. Generally, set bck_div to 8. - * @param cal Point to `i2s_ll_clk_cal_t` structure. + * @param clk_cfg I2S clock configuration(input) + * @param cal Point to `i2s_ll_mclk_div_t` structure(output). */ -static void i2s_hal_clk_cal(uint32_t fsclk, uint32_t fbck, int bck_div, i2s_ll_clk_cal_t *cal) +static void i2s_hal_mclk_div_decimal_cal(i2s_hal_clock_cfg_t *clk_cfg, i2s_ll_mclk_div_t *cal) { int ma = 0; int mb = 0; - uint32_t mclk = fbck * bck_div; - cal->mclk_div = fsclk / mclk; + cal->mclk_div = clk_cfg->mclk_div; cal->a = 1; cal->b = 0; - uint32_t freq_diff = fsclk - mclk * cal->mclk_div; - uint32_t min = ~0; - if (freq_diff == 0) { + if (!clk_cfg->sclk) { return; } + uint32_t freq_diff = clk_cfg->sclk - clk_cfg->mclk * cal->mclk_div; + uint32_t min = ~0; for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { for (int b = 1; b < a; b++) { ma = freq_diff * a; - mb = mclk * b; + mb = clk_cfg->mclk * b; if (ma == mb) { cal->a = a; cal->b = b; @@ -68,44 +63,43 @@ void i2s_hal_set_clock_src(i2s_hal_context_t *hal, i2s_clock_src_t sel) i2s_ll_rx_clk_set_src(hal->dev, sel); } -void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor) +void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg) { - i2s_ll_clk_cal_t clk_set = {0}; - i2s_hal_clk_cal(sclk, fbck, factor, &clk_set); - i2s_ll_tx_set_clk(hal->dev, &clk_set); - i2s_ll_tx_set_bck_div_num(hal->dev, factor); + i2s_ll_mclk_div_t mclk_set; + i2s_hal_mclk_div_decimal_cal(clk_cfg, &mclk_set); + i2s_ll_tx_set_clk(hal->dev, &mclk_set); + i2s_ll_tx_set_bck_div_num(hal->dev, clk_cfg->bclk_div); } -void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor) +void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg) { - i2s_ll_clk_cal_t clk_set = {0}; - i2s_hal_clk_cal(sclk, fbck, factor, &clk_set); - i2s_ll_rx_set_clk(hal->dev, &clk_set); - i2s_ll_rx_set_bck_div_num(hal->dev, factor); + i2s_ll_mclk_div_t mclk_set; + i2s_hal_mclk_div_decimal_cal(clk_cfg, &mclk_set); + i2s_ll_rx_set_clk(hal->dev, &mclk_set); + i2s_ll_rx_set_bck_div_num(hal->dev, clk_cfg->bclk_div); } void i2s_hal_enable_master_fd_mode(i2s_hal_context_t *hal) { - i2s_ll_tx_set_slave_mod(hal->dev, 0); //TX master - i2s_ll_rx_set_slave_mod(hal->dev, 1); //RX Slave + i2s_ll_tx_set_slave_mod(hal->dev, false); //TX master + i2s_ll_rx_set_slave_mod(hal->dev, true); //RX Slave } void i2s_hal_enable_slave_fd_mode(i2s_hal_context_t *hal) { - i2s_ll_tx_set_slave_mod(hal->dev, 1); //TX Slave - i2s_ll_rx_set_slave_mod(hal->dev, 1); //RX Slave + i2s_ll_tx_set_slave_mod(hal->dev, true); //TX Slave + i2s_ll_rx_set_slave_mod(hal->dev, true); //RX Slave } void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num) { - //Get hardware instance. + /* Get hardware instance */ hal->dev = I2S_LL_GET_HW(i2s_num); - i2s_ll_enable_clock(hal->dev); } +#if SOC_I2S_SUPPORTS_PDM_TX void i2s_hal_tx_set_pdm_mode_default(i2s_hal_context_t *hal, uint32_t sample_rate) { -#if SOC_I2S_SUPPORTS_PDM_TX /* enable pdm tx mode */ i2s_ll_tx_enable_pdm(hal->dev, true); /* set pdm tx default presacle */ @@ -134,23 +128,23 @@ void i2s_hal_tx_set_pdm_mode_default(i2s_hal_context_t *hal, uint32_t sample_rat i2s_ll_tx_set_pdm_sd_dither2(hal->dev, 0); #endif // SOC_I2S_SUPPORTS_PDM_CODEC -#endif // SOC_I2S_SUPPORTS_PDM_TX } +#endif // SOC_I2S_SUPPORTS_PDM_TX +#if SOC_I2S_SUPPORTS_PDM_RX void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal) { -#if SOC_I2S_SUPPORTS_PDM_RX /* enable pdm rx mode */ i2s_ll_rx_enable_pdm(hal->dev, true); /* set pdm rx downsample number */ i2s_ll_rx_set_pdm_dsr(hal->dev, I2S_PDM_DSR_8S); -#endif // SOC_I2S_SUPPORTS_PDM_RX } +#endif // SOC_I2S_SUPPORTS_PDM_RX void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { - /* disable pdm tx mode */ + /* Disable PDM tx mode and enable TDM mode (if support) */ i2s_ll_tx_enable_pdm(hal->dev, false); #if SOC_I2S_SUPPORTS_TDM @@ -172,7 +166,7 @@ void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t * void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { - /* disable pdm rx mode */ + /* Disable PDM rx mode and enable TDM rx mode (if support)*/ i2s_ll_rx_enable_pdm(hal->dev, false); #if SOC_I2S_SUPPORTS_TDM @@ -208,8 +202,10 @@ static uint32_t i2s_hal_get_ws_bit(i2s_comm_format_t fmt, uint32_t chan_num, uin void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { uint32_t chan_num = 2; - uint32_t chan_bits = hal_cfg->bits_cfg.chan_bits; - uint32_t data_bits = hal_cfg->bits_cfg.sample_bits; + uint32_t chan_bits = hal_cfg->chan_bits; + uint32_t data_bits = hal_cfg->sample_bits; + bool is_mono = (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_RIGHT) || + (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT); /* Set channel number and valid data bits */ #if SOC_I2S_SUPPORTS_TDM @@ -217,6 +213,7 @@ void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t i2s_ll_tx_set_chan_num(hal->dev, chan_num); #endif i2s_ll_tx_set_sample_bit(hal->dev, chan_bits, data_bits); + i2s_ll_tx_enable_mono_mode(hal->dev, is_mono); /* Set communication format */ bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; @@ -231,14 +228,17 @@ void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { uint32_t chan_num = 2; - uint32_t chan_bits = hal_cfg->bits_cfg.chan_bits; - uint32_t data_bits = hal_cfg->bits_cfg.sample_bits; + uint32_t chan_bits = hal_cfg->chan_bits; + uint32_t data_bits = hal_cfg->sample_bits; + bool is_mono = (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_RIGHT) || + (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT); #if SOC_I2S_SUPPORTS_TDM chan_num = hal_cfg->total_chan; i2s_ll_rx_set_chan_num(hal->dev, chan_num); #endif i2s_ll_rx_set_sample_bit(hal->dev, chan_bits, data_bits); + i2s_ll_rx_enable_mono_mode(hal->dev, is_mono); /* Set communication format */ bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; @@ -252,30 +252,67 @@ void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { +#if SOC_I2S_SUPPORTS_ADC + if (hal_cfg->mode & I2S_MODE_ADC_BUILT_IN) { + /* In ADC built-in mode, we need to call i2s_set_adc_mode to initialize the specific ADC channel. + * In the current stage, we only support ADC1 and single channel mode. + * In default data mode, the ADC data is in 12-bit resolution mode. + */ + i2s_ll_enable_builtin_adc(hal->dev, true); + return; + } + i2s_ll_enable_builtin_adc(hal->dev, false); +#endif +#if SOC_I2S_SUPPORTS_DAC + if (hal_cfg->mode & I2S_MODE_DAC_BUILT_IN) { + i2s_ll_enable_builtin_dac(hal->dev, true); + return; + } + i2s_ll_enable_builtin_dac(hal->dev, false); +#endif + /* Set configurations for TX mode */ if (hal_cfg->mode & I2S_MODE_TX) { i2s_ll_tx_stop(hal->dev); i2s_ll_tx_reset(hal->dev); i2s_ll_tx_set_slave_mod(hal->dev, (hal_cfg->mode & I2S_MODE_SLAVE) != 0); //TX Slave +#if SOC_I2S_SUPPORTS_PDM_TX if (hal_cfg->mode & I2S_MODE_PDM) { /* Set tx pdm mode */ i2s_hal_tx_set_pdm_mode_default(hal, hal_cfg->sample_rate); - } else { + } else +#endif + { /* Set tx common mode */ i2s_hal_tx_set_common_mode(hal, hal_cfg); i2s_hal_tx_set_channel_style(hal, hal_cfg); } } + + /* Set configurations for RX mode */ if (hal_cfg->mode & I2S_MODE_RX) { i2s_ll_rx_stop(hal->dev); i2s_ll_rx_reset(hal->dev); i2s_ll_rx_set_slave_mod(hal->dev, (hal_cfg->mode & I2S_MODE_SLAVE) != 0); //RX Slave +#if SOC_I2S_SUPPORTS_PDM_RX if (hal_cfg->mode & I2S_MODE_PDM) { /* Set rx pdm mode */ i2s_hal_rx_set_pdm_mode_default(hal); - } else { + } else +#endif + { /* Set rx common mode */ i2s_hal_rx_set_common_mode(hal, hal_cfg); i2s_hal_rx_set_channel_style(hal, hal_cfg); } } + + /* Set configurations for full-duplex mode */ + if ((hal_cfg->mode & I2S_MODE_RX) && (hal_cfg->mode & I2S_MODE_TX)) { + i2s_ll_share_bck_ws(hal->dev, true); + if (hal_cfg->mode & I2S_MODE_MASTER) { + i2s_hal_enable_master_fd_mode(hal); + } else { + i2s_hal_enable_slave_fd_mode(hal); + } + } } diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index d0446f7a47..a08813db80 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/components/hal/include/hal/i2s_hal.h @@ -33,16 +33,16 @@ extern "C" { #endif /** - * @brief I2S channel bits configurations - * + * @brief I2S clock configuration */ -typedef union { - struct { - uint32_t sample_bits : 16; /*!< I2S sample bits in one channel */ - uint32_t chan_bits : 16; /*!< I2S total bits in one channel. Should not be smaller than 'sample_bits', default '0' means equal to 'sample_bits' */ - }; - uint32_t val; /*!< I2S cannel bits configiration value */ -} i2s_hal_bits_cfg_t; +typedef struct { + uint32_t sclk; /*!< I2S module clock */ + uint32_t mclk; /*!< I2S master clock */ + uint32_t bclk; /*!< I2S bit clock */ + uint16_t mclk_div; /*!< I2S master clock division */ + uint16_t bclk_div; /*!< I2S bit clock division*/ +} i2s_hal_clock_cfg_t; + /** * @brief I2S HAL configurations @@ -50,17 +50,19 @@ typedef union { typedef struct { i2s_mode_t mode; /*!< I2S work mode, using ored mask of `i2s_mode_t`*/ uint32_t sample_rate; /*!< I2S sample rate*/ - i2s_channel_t ch; /*!< I2S channels*/ i2s_comm_format_t comm_fmt; /*!< I2S communication format */ i2s_channel_fmt_t chan_fmt; /*!< I2S channel format, there are total 16 channels in TDM mode.*/ - i2s_hal_bits_cfg_t bits_cfg; /*!< Channel bits configuration*/ -#if SOC_I2S_SUPPORTS_TDM + uint32_t sample_bits; /*!< I2S sample bits in one channel */ + uint32_t chan_bits; /*!< I2S total bits in one channel. Should not be smaller than 'sample_bits', default '0' means equal to 'sample_bits' */ + uint32_t active_chan; /*!< I2S active channel number */ uint32_t total_chan; /*!< Total number of I2S channels */ + +#if SOC_I2S_SUPPORTS_TDM uint32_t chan_mask; /*!< Active channel bit mask, set value in `i2s_channel_t` to enable specific channel, the bit map of active channel can not exceed (0x1<dev); + +/** + * @brief Disable I2S module clock + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_module_clock(hal) i2s_ll_disable_clock((hal)->dev); + /** * @brief Reset I2S TX channel * @@ -101,7 +117,8 @@ typedef struct { #define i2s_hal_reset_rx_fifo(hal) i2s_ll_rx_reset_fifo((hal)->dev) /** - * @brief Init the I2S hal. This function should be called first before other hal layer function is called + * @brief Get I2S hardware instance and enable I2S module clock + * @note This function should be called first before other hal layer function is called * * @param hal Context of the HAL layer * @param i2s_num The uart port number, the max port number is (I2S_NUM_MAX -1) @@ -133,7 +150,7 @@ void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); /** - * @brief Config I2S param + * @brief Initialize I2S hardware * * @param hal Context of the HAL layer * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` @@ -212,46 +229,56 @@ void i2s_hal_enable_slave_fd_mode(i2s_hal_context_t *hal); * @brief Configure I2S TX module clock devider * * @param hal Context of the HAL layer - * @param sclk I2S source clock freq - * @param fbck I2S bck freq - * @param factor bck factor, factor=sclk/fbck + * @param clk_cfg I2S clock configuration */ -void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor); +void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg); /** * @brief Configure I2S RX module clock devider * * @param hal Context of the HAL layer - * @param sclk I2S source clock freq - * @param fbck I2S bck freq - * @param factor bck factor, factor=sclk/fbck + * @param clk_cfg I2S clock configuration */ -void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor); - -#if SOC_I2S_SUPPORTS_PCM -/** - * @brief Configure I2S TX PCM encoder or decoder. - * - * @param hal Context of the HAL layer - * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` - */ -#define i2s_hal_tx_pcm_cfg(hal, cfg) i2s_ll_tx_set_pcm_type((hal)->dev, cfg) +void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg); /** - * @brief Configure I2S RX PCM encoder or decoder. + * @brief Set I2S tx clock source * * @param hal Context of the HAL layer - * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` + * @param clk_src i2s tx clock source (see 'i2s_clock_src_t') */ -#define i2s_hal_rx_pcm_cfg(hal, cfg) i2s_ll_rx_set_pcm_type((hal)->dev, cfg) -#endif +#define i2s_hal_tx_set_clock_source(hal, clk_src) i2s_ll_tx_clk_set_src((hal)->dev, clk_src) + +/** + * @brief Set I2S rx clock source + * + * @param hal Context of the HAL layer + * @param clk_src i2s rx clock source (see 'i2s_clock_src_t') + */ +#define i2s_hal_rx_set_clock_source(hal, clk_src) i2s_ll_rx_clk_set_src((hal)->dev, clk_src) + +/** + * @brief Enable I2S tx slave mode + * + * @param hal Context of the HAL layer + * @param enable set 'true' to enable tx slave mode + */ +#define i2s_hal_tx_enable_slave_mode(hal, enable) i2s_ll_tx_set_slave_mod((hal)->dev, enable) + +/** + * @brief Enable I2S rx slave mode + * + * @param hal Context of the HAL layer + * @param enable set 'true' to enable rx slave mode + */ +#define i2s_hal_rx_enable_slave_mode(hal, enable) i2s_ll_rx_set_slave_mod((hal)->dev, enable) /** * @brief Enable loopback mode * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_sig_loopback(hal) i2s_ll_enable_loop_back((hal)->dev, true) +#define i2s_hal_enable_sig_loopback(hal) i2s_ll_share_bck_ws((hal)->dev, true) /** * @brief Set I2S configuration for common TX mode @@ -271,6 +298,24 @@ void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t * */ void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); +#if SOC_I2S_SUPPORTS_PCM +/** + * @brief Configure I2S TX PCM encoder or decoder. + * + * @param hal Context of the HAL layer + * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` + */ +#define i2s_hal_tx_pcm_cfg(hal, cfg) i2s_ll_tx_set_pcm_type((hal)->dev, cfg) + +/** + * @brief Configure I2S RX PCM encoder or decoder. + * + * @param hal Context of the HAL layer + * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` + */ +#define i2s_hal_rx_pcm_cfg(hal, cfg) i2s_ll_rx_set_pcm_type((hal)->dev, cfg) +#endif + #if SOC_I2S_SUPPORTS_PDM_TX /** * @brief Configure I2S TX PDM sample rate @@ -470,7 +515,7 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); #define i2s_hal_get_in_eof_des_addr(hal, addr) i2s_ll_rx_get_eof_des_addr((hal)->dev, addr) #endif -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC /** * @brief Enable Builtin DAC * @@ -485,13 +530,6 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); */ #define i2s_hal_enable_builtin_adc(hal) i2s_ll_enable_builtin_adc((hal)->dev, true); -/** - * @brief Disable Builtin DAC - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_disable_builtin_dac(hal) i2s_ll_enable_builtin_dac((hal)->dev, false); - /** * @brief Disable Builtin ADC * @@ -500,6 +538,15 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); #define i2s_hal_disable_builtin_adc(hal) i2s_ll_enable_builtin_adc((hal)->dev, false); #endif +#if SOC_I2S_SUPPORTS_DAC +/** + * @brief Disable Builtin DAC + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_builtin_dac(hal) i2s_ll_enable_builtin_dac((hal)->dev, false); +#endif + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/i2s_types.h b/components/hal/include/hal/i2s_types.h index 630aedb149..48d745eb2f 100644 --- a/components/hal/include/hal/i2s_types.h +++ b/components/hal/include/hal/i2s_types.h @@ -107,8 +107,8 @@ typedef enum { I2S_CHANNEL_FMT_RIGHT_LEFT, /*!< Separated left and right channel */ I2S_CHANNEL_FMT_ALL_RIGHT, /*!< Load right channel data in both two channels */ I2S_CHANNEL_FMT_ALL_LEFT, /*!< Load left channel data in both two channels */ - I2S_CHANNEL_FMT_ONLY_RIGHT, /*!< Only load data in right channel */ - I2S_CHANNEL_FMT_ONLY_LEFT, /*!< Only load data in left channel */ + I2S_CHANNEL_FMT_ONLY_RIGHT, /*!< Only load data in right channel (mono mode) */ + I2S_CHANNEL_FMT_ONLY_LEFT, /*!< Only load data in left channel (mono mode) */ #if SOC_I2S_SUPPORTS_TDM // Multiple channels are available with TDM feature I2S_CHANNEL_FMT_MULTIPLE, /*!< More than two channels are used */ @@ -123,11 +123,13 @@ typedef enum { I2S_MODE_SLAVE = (0x1 << 1), /*!< Slave mode*/ I2S_MODE_TX = (0x1 << 2), /*!< TX mode*/ I2S_MODE_RX = (0x1 << 3), /*!< RX mode*/ -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC //built-in DAC functions are only supported on I2S0 for ESP32 chip. I2S_MODE_DAC_BUILT_IN = (0x1 << 4), /*!< Output I2S data to built-in DAC, no matter the data format is 16bit or 32 bit, the DAC module will only take the 8bits from MSB*/ +#endif // SOC_I2S_SUPPORTS_DAC +#if SOC_I2S_SUPPORTS_ADC I2S_MODE_ADC_BUILT_IN = (0x1 << 5), /*!< Input I2S data from built-in ADC, each data can be 12-bit width at most*/ -#endif +#endif // SOC_I2S_SUPPORTS_ADC // PDM functions are only supported on I2S0 (all chips). I2S_MODE_PDM = (0x1 << 6), /*!< I2S PDM mode*/ } i2s_mode_t; @@ -152,7 +154,7 @@ typedef enum { I2S_MCLK_MULTIPLE_384 = 384, /*!< mclk = sample_rate * 384 */ } i2s_mclk_multiple_t; -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC /** * @brief I2S DAC mode for i2s_set_dac_mode. * @@ -165,7 +167,7 @@ typedef enum { I2S_DAC_CHANNEL_BOTH_EN = 0x3, /*!< Enable both of the I2S built-in DAC channels.*/ I2S_DAC_CHANNEL_MAX = 0x4, /*!< I2S built-in DAC mode max index*/ } i2s_dac_mode_t; -#endif //SOC_I2S_SUPPORTS_ADC_DAC +#endif //SOC_I2S_SUPPORTS_DAC #if SOC_I2S_SUPPORTS_PCM /** diff --git a/components/soc/esp32/i2s_periph.c b/components/soc/esp32/i2s_periph.c index cf6d4ba992..b65dc9602d 100644 --- a/components/soc/esp32/i2s_periph.c +++ b/components/soc/esp32/i2s_periph.c @@ -21,23 +21,39 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = -1, // Unavailable - .rx_bck_sig = I2S0I_BCK_IN_IDX, - .tx_bck_sig = I2S0O_BCK_OUT_IDX, - .tx_ws_sig = I2S0O_WS_OUT_IDX, - .rx_ws_sig = I2S0I_WS_IN_IDX, + + .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, + .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, + .m_tx_ws_sig = I2S0O_WS_OUT_IDX, + .m_rx_ws_sig = I2S0I_WS_OUT_IDX, + + .s_tx_bck_sig = I2S0O_BCK_IN_IDX, + .s_rx_bck_sig = I2S0I_BCK_IN_IDX, + .s_tx_ws_sig = I2S0O_WS_IN_IDX, + .s_rx_ws_sig = I2S0I_WS_IN_IDX, + .data_out_sig = I2S0O_DATA_OUT23_IDX, .data_in_sig = I2S0I_DATA_IN15_IDX, + .irq = ETS_I2S0_INTR_SOURCE, .module = PERIPH_I2S0_MODULE, }, { .mck_out_sig = -1, // Unavailable - .rx_bck_sig = I2S1I_BCK_IN_IDX, - .tx_bck_sig = I2S1O_BCK_OUT_IDX, - .tx_ws_sig = I2S1O_WS_OUT_IDX, - .rx_ws_sig = I2S1I_WS_IN_IDX, + + .m_tx_bck_sig = I2S1O_BCK_OUT_IDX, + .m_rx_bck_sig = I2S1I_BCK_OUT_IDX, + .m_tx_ws_sig = I2S1O_WS_OUT_IDX, + .m_rx_ws_sig = I2S1I_WS_OUT_IDX, + + .s_tx_bck_sig = I2S1O_BCK_IN_IDX, + .s_rx_bck_sig = I2S1I_BCK_IN_IDX, + .s_tx_ws_sig = I2S1O_WS_IN_IDX, + .s_rx_ws_sig = I2S1I_WS_IN_IDX, + .data_out_sig = I2S1O_DATA_OUT23_IDX, .data_in_sig = I2S1I_DATA_IN15_IDX, + .irq = ETS_I2S1_INTR_SOURCE, .module = PERIPH_I2S1_MODULE, } diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index e2bd1842b4..54dd6b674a 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -137,7 +137,8 @@ #define SOC_I2S_NUM (2) #define SOC_I2S_SUPPORTS_PDM_TX (1) #define SOC_I2S_SUPPORTS_PDM_RX (1) -#define SOC_I2S_SUPPORTS_ADC_DAC (1) // ESP32 support ADC and DAC +#define SOC_I2S_SUPPORTS_ADC (1) // ESP32 support ADC and DAC +#define SOC_I2S_SUPPORTS_DAC (1) #define SOC_I2S_SUPPORTS_APLL (1)// ESP32 support APLL #define SOC_I2S_APLL_MIN_FREQ (250000000) diff --git a/components/soc/esp32c3/i2s_periph.c b/components/soc/esp32c3/i2s_periph.c index e654a0c62e..e852a8e56d 100644 --- a/components/soc/esp32c3/i2s_periph.c +++ b/components/soc/esp32c3/i2s_periph.c @@ -21,12 +21,20 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S_MCLK_OUT_IDX, - .rx_bck_sig = I2SI_BCK_IN_IDX, - .tx_bck_sig = I2SO_BCK_OUT_IDX, - .tx_ws_sig = I2SO_WS_OUT_IDX, - .rx_ws_sig = I2SI_WS_IN_IDX, + + .m_tx_bck_sig = I2SO_BCK_OUT_IDX, + .m_rx_bck_sig = I2SI_BCK_OUT_IDX, + .m_tx_ws_sig = I2SO_WS_OUT_IDX, + .m_rx_ws_sig = I2SI_WS_OUT_IDX, + + .s_tx_bck_sig = I2SO_BCK_IN_IDX, + .s_rx_bck_sig = I2SI_BCK_IN_IDX, + .s_tx_ws_sig = I2SO_WS_IN_IDX, + .s_rx_ws_sig = I2SI_WS_IN_IDX, + .data_out_sig = I2SO_SD_OUT_IDX, .data_in_sig = I2SI_SD_IN_IDX, + .irq = -1, .module = PERIPH_I2S1_MODULE, } diff --git a/components/soc/esp32s2/i2s_periph.c b/components/soc/esp32s2/i2s_periph.c index 83700ed886..48c6161d20 100644 --- a/components/soc/esp32s2/i2s_periph.c +++ b/components/soc/esp32s2/i2s_periph.c @@ -21,12 +21,20 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = CLK_I2S_MUX_IDX, - .rx_bck_sig = I2S0I_BCK_IN_IDX, - .tx_bck_sig = I2S0O_BCK_OUT_IDX, - .tx_ws_sig = I2S0O_WS_OUT_IDX, - .rx_ws_sig = I2S0I_WS_IN_IDX, + + .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, + .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, + .m_tx_ws_sig = I2S0O_WS_OUT_IDX, + .m_rx_ws_sig = I2S0I_WS_OUT_IDX, + + .s_tx_bck_sig = I2S0O_BCK_IN_IDX, + .s_rx_bck_sig = I2S0I_BCK_IN_IDX, + .s_tx_ws_sig = I2S0O_WS_IN_IDX, + .s_rx_ws_sig = I2S0I_WS_IN_IDX, + .data_out_sig = I2S0O_DATA_OUT23_IDX, .data_in_sig = I2S0I_DATA_IN15_IDX, + .irq = ETS_I2S0_INTR_SOURCE, .module = PERIPH_I2S0_MODULE, } diff --git a/components/soc/esp32s3/i2s_periph.c b/components/soc/esp32s3/i2s_periph.c index 39ddfff5a5..79a8308e21 100644 --- a/components/soc/esp32s3/i2s_periph.c +++ b/components/soc/esp32s3/i2s_periph.c @@ -21,23 +21,39 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S0_MCLK_OUT_IDX, - .rx_bck_sig = I2S0I_BCK_IN_IDX, - .tx_bck_sig = I2S0O_BCK_OUT_IDX, - .tx_ws_sig = I2S0O_WS_OUT_IDX, - .rx_ws_sig = I2S0I_WS_IN_IDX, + + .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, + .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, + .m_tx_ws_sig = I2S0O_WS_OUT_IDX, + .m_rx_ws_sig = I2S0I_WS_OUT_IDX, + + .s_tx_bck_sig = I2S0O_BCK_IN_IDX, + .s_rx_bck_sig = I2S0I_BCK_IN_IDX, + .s_tx_ws_sig = I2S0O_WS_IN_IDX, + .s_rx_ws_sig = I2S0I_WS_IN_IDX, + .data_out_sig = I2S0O_SD_OUT_IDX, .data_in_sig = I2S0I_SD_IN_IDX, + .irq = -1, .module = PERIPH_I2S0_MODULE, }, { .mck_out_sig = I2S1_MCLK_OUT_IDX, - .rx_bck_sig = I2S1I_BCK_IN_IDX, - .tx_bck_sig = I2S1O_BCK_OUT_IDX, - .tx_ws_sig = I2S1O_WS_OUT_IDX, - .rx_ws_sig = I2S1I_WS_IN_IDX, + + .m_tx_bck_sig = I2S1O_BCK_OUT_IDX, + .m_rx_bck_sig = I2S1I_BCK_OUT_IDX, + .m_tx_ws_sig = I2S1O_WS_OUT_IDX, + .m_rx_ws_sig = I2S1I_WS_OUT_IDX, + + .s_tx_bck_sig = I2S1O_BCK_IN_IDX, + .s_rx_bck_sig = I2S1I_BCK_IN_IDX, + .s_tx_ws_sig = I2S1O_WS_IN_IDX, + .s_rx_ws_sig = I2S1I_WS_IN_IDX, + .data_out_sig = I2S1O_SD_OUT_IDX, .data_in_sig = I2S1I_SD_IN_IDX, + .irq = -1, .module = PERIPH_I2S1_MODULE, } diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index 59e31674d5..167c84700f 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -28,12 +28,20 @@ extern "C" { */ typedef struct { const uint8_t mck_out_sig; - const uint8_t tx_bck_sig; - const uint8_t rx_bck_sig; - const uint8_t tx_ws_sig; - const uint8_t rx_ws_sig; + + const uint8_t m_tx_bck_sig; + const uint8_t m_rx_bck_sig; + const uint8_t m_tx_ws_sig; + const uint8_t m_rx_ws_sig; + + const uint8_t s_tx_bck_sig; + const uint8_t s_rx_bck_sig; + const uint8_t s_tx_ws_sig; + const uint8_t s_rx_ws_sig; + const uint8_t data_out_sig; const uint8_t data_in_sig; + const uint8_t irq; const periph_module_t module; } i2s_signal_conn_t; diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index 6fd3bd2415..99e4924105 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -166,11 +166,11 @@ Application Example A code example for the I2S driver can be found in the directory :example:`peripherals/i2s`. -.. only:: SOC_I2S_SUPPORTS_ADC_DAC +.. only:: SOC_I2S_SUPPORTS_ADC or SOC_I2S_SUPPORTS_DAC In addition, there are two short configuration examples for the I2S driver. -.. only:: not SOC_I2S_SUPPORTS_ADC_DAC +.. only:: not SOC_I2S_SUPPORTS_ADC or SOC_I2S_SUPPORTS_DAC In addition, there is a short configuration examples for the I2S driver. @@ -310,7 +310,7 @@ Example for general usage. i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver -.. only:: SOC_I2S_SUPPORTS_ADC_DAC +.. only:: SOC_I2S_SUPPORTS_ADC or SOC_I2S_SUPPORTS_DAC Configuring I2S to use internal DAC for analog output ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^