diff --git a/components/driver/i2s.c b/components/driver/i2s.c index be3e90b3b7..f0dc634ac3 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -17,16 +17,21 @@ #include "driver/gpio.h" #include "driver/i2s.h" #include "hal/gpio_hal.h" +#include "hal/i2s_hal.h" #if SOC_I2S_SUPPORTS_ADC_DAC #include "driver/dac.h" -#include "hal/i2s_hal.h" #include "adc1_private.h" #endif +#if SOC_GDMA_SUPPORTED +#include "esp_private/gdma.h" +#endif + #include "soc/rtc.h" #include "esp_intr_alloc.h" #include "esp_err.h" +#include "esp_check.h" #include "esp_attr.h" #include "esp_log.h" #include "esp_pm.h" @@ -35,24 +40,22 @@ #include "sdkconfig.h" -static const char* I2S_TAG = "I2S"; +static const char *TAG = "I2S"; -#define I2S_CHECK(a, str, ret) if (!(a)) { \ - ESP_LOGE(I2S_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret); \ - } - -#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) -#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL_ISR(i2s_num) portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#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) -//TODO: Refactor to put this logic into LL -#define I2S_AD_BCK_FACTOR (2) -#define I2S_PDM_BCK_FACTOR (64) -#define I2S_BASE_CLK (2*APB_CLK_FREQ) +#if !SOC_GDMA_SUPPORTED +#define I2S_INTR_IN_SUC_EOF BIT(9) +#define I2S_INTR_OUT_EOF BIT(12) +#define I2S_INTR_IN_DSCR_ERR BIT(13) +#define I2S_INTR_OUT_DSCR_ERR BIT(14) +#define I2S_INTR_MAX (~0) +#endif /** * @brief DMA buffer object @@ -78,25 +81,33 @@ 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*/ - i2s_dma_t *rx; /*!< DMA Tx buffer*/ - i2s_dma_t *tx; /*!< DMA Rx buffer*/ + i2s_dma_t *tx; /*!< DMA Tx buffer*/ + i2s_dma_t *rx; /*!< DMA Rx buffer*/ +#if SOC_GDMA_SUPPORTED + gdma_channel_handle_t rx_dma_chan; /*!< I2S rx gDMA channel handle*/ + gdma_channel_handle_t tx_dma_chan; /*!< I2S tx gDMA channel handle*/ +#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 bytes_per_sample; /*!< Bytes per sample*/ int bits_per_sample; /*!< Bits per sample*/ + i2s_comm_format_t communication_format; /*!hal), clr_mask); + if (gpio_num == -1) { + return ESP_OK; + } +#if CONFIG_IDF_TARGET_ESP32 + ESP_RETURN_ON_FALSE((gpio_num == GPIO_NUM_0 || gpio_num == GPIO_NUM_1 || gpio_num == GPIO_NUM_3), + ESP_ERR_INVALID_ARG, TAG, + "ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num); + bool is_i2s0 = i2s_num == I2S_NUM_0; + if (gpio_num == GPIO_NUM_0) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFFF0 : 0xFFFF); + } else if (gpio_num == GPIO_NUM_1) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xF0F0 : 0xF0FF); + } else { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFF00 : 0xFF0F); + } +#else + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid"); + gpio_matrix_out_check_and_set(gpio_num, i2s_periph_signal[i2s_num].mck_out_sig, 0, 0); +#endif + ESP_LOGI(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num); return ESP_OK; } -esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num) +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) { + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + if (pin == NULL) { +#if SOC_I2S_SUPPORTS_ADC_DAC + return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); +#else + return ESP_ERR_INVALID_ARG; +#endif + } - I2S_ENTER_CRITICAL(); - i2s_hal_enable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); + ESP_RETURN_ON_FALSE((pin->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)), + ESP_ERR_INVALID_ARG, TAG, "ws_io_num invalid"); + ESP_RETURN_ON_FALSE((pin->data_out_num == -1 || GPIO_IS_VALID_GPIO(pin->data_out_num)), + ESP_ERR_INVALID_ARG, TAG, "data_out_num invalid"); + 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); + } 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); + } + } else { + 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); + } 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->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; } -esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num) + +/************************************************************** + * 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 * + **************************************************************/ + +#if SOC_GDMA_SUPPORTED +static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { - I2S_ENTER_CRITICAL(); - i2s_hal_disable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); + i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; + portBASE_TYPE high_priority_task_awoken = 0; + BaseType_t ret = 0; + int dummy; + i2s_event_t i2s_event; + uint32_t finish_desc; + + if (p_i2s->rx) { + finish_desc = event_data->rx_eof_desc_addr; + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + } + ret = xQueueSendFromISR(p_i2s->rx->queue, &(((lldesc_t *)finish_desc)->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = (ret == pdPASS) ? I2S_EVENT_RX_DONE : I2S_EVENT_RX_Q_OVF; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + return high_priority_task_awoken; +} + +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; + portBASE_TYPE high_priority_task_awoken = 0; + BaseType_t ret; + int dummy; + i2s_event_t i2s_event; + uint32_t finish_desc; + if (p_i2s->tx) { + finish_desc = event_data->tx_eof_desc_addr; + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); + if (p_i2s->tx_desc_auto_clear) { + memset((void *) dummy, 0, p_i2s->tx->buf_size); + } + } + ret = xQueueSendFromISR(p_i2s->tx->queue, &(((lldesc_t *)finish_desc)->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = (ret == pdPASS) ? I2S_EVENT_TX_DONE : I2S_EVENT_TX_Q_OVF; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + return high_priority_task_awoken; +} + +#else +static void IRAM_ATTR i2s_intr_handler_default(void *arg) +{ + i2s_obj_t *p_i2s = (i2s_obj_t *) arg; + uint32_t status = i2s_hal_get_intr_status(&(p_i2s->hal)); + if (status == 0) { + //Avoid spurious interrupt + return; + } + + i2s_event_t i2s_event; + int dummy; + portBASE_TYPE high_priority_task_awoken = 0; + uint32_t finish_desc = 0; + if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) { + ESP_EARLY_LOGE(TAG, "dma error, interrupt status: 0x%08x", status); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + + if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) { + i2s_hal_get_out_eof_des_addr(&(p_i2s->hal), &finish_desc); + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); + // See if tx descriptor needs to be auto cleared: + // This will avoid any kind of noise that may get introduced due to transmission + // of previous data from tx descriptor on I2S line. + if (p_i2s->tx_desc_auto_clear == true) { + memset((void *) dummy, 0, p_i2s->tx->buf_size); + } + } + xQueueSendFromISR(p_i2s->tx->queue, &(((lldesc_t *)finish_desc)->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + + if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) { + // All buffers are full. This means we have an overflow. + i2s_hal_get_in_eof_des_addr(&(p_i2s->hal), &finish_desc); + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->rx->queue, &(((lldesc_t *)finish_desc)->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + i2s_hal_clear_intr_status(&(p_i2s->hal), status); + + if (high_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} +#endif + +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; +#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 +} + +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; +#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 +} + +static void i2s_tx_start(i2s_port_t i2s_num) +{ +#if SOC_GDMA_SUPPORTED + gdma_start(p_i2s[i2s_num]->tx_dma_chan, (uint32_t) p_i2s[i2s_num]->tx->desc[0]); +#else + i2s_hal_enable_tx_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_enable_tx_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_start_tx_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->tx->desc[0]); +#endif + i2s_hal_start_tx(&(p_i2s[i2s_num]->hal)); +} + +static void i2s_rx_start(i2s_port_t i2s_num) +{ +#if SOC_GDMA_SUPPORTED + gdma_start(p_i2s[i2s_num]->rx_dma_chan, (uint32_t) p_i2s[i2s_num]->rx->desc[0]); +#else + i2s_hal_enable_rx_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_enable_rx_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_start_rx_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->rx->desc[0]); +#endif + i2s_hal_start_rx(&(p_i2s[i2s_num]->hal)); +} + +static void i2s_tx_stop(i2s_port_t i2s_num) +{ +#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 +} + +static void i2s_rx_stop(i2s_port_t i2s_num) +{ +#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) +{ + if (p_i2s[i2s_num]->channel_num != ch) { + p_i2s[i2s_num]->channel_num = (ch == 2) ? 2 : 1; + } + + 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; + } + + // 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); + } + } return ESP_OK; } -esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num) +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) { - I2S_ENTER_CRITICAL(); - i2s_hal_disable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); + int bux_idx; + if (p_i2s[i2s_num] == NULL) { + ESP_LOGE(TAG, "Not initialized yet"); + return ESP_ERR_INVALID_ARG; + } + if (dma == NULL) { + ESP_LOGE(TAG, "dma is NULL"); + return ESP_ERR_INVALID_ARG; + } + 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]); + } + } + if (dma->buf) { + free(dma->buf); + } + if (dma->desc) { + free(dma->desc); + } + ESP_LOGI(TAG, "DMA queue destroyed"); + vQueueDelete(dma->queue); + vSemaphoreDelete(dma->mux); + free(dma); return ESP_OK; } -esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num) +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) { - I2S_ENTER_CRITICAL(); - i2s_hal_enable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); + 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; + } + 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; + } + 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]); + } + + 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; +} + +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"); + 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); + } + } + 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; + if (bytes_left) { + size_t zero_bytes = 0, bytes_written; + i2s_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); + } + for (int i = 0; i < p_i2s[i2s_num]->dma_buf_count; i++) { + memset(p_i2s[i2s_num]->tx->buf[i], 0, p_i2s[i2s_num]->tx->buf_size); + } + } 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) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - return p_i2s_obj[i2s_num]->real_rate; + 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; } -static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) -{ - return esp_intr_alloc(i2s_periph_signal[i2s_num].irq, intr_alloc_flags, fn, arg, handle); -} +#if SOC_I2S_SUPPORTS_APLL static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) { int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; @@ -193,8 +643,8 @@ static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2 if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) { return SOC_I2S_APLL_MAX_FREQ; } - float fpll = fout / (2 * (odir+2)); //== fi2s (N=1, b=0, a=1) - return fpll/2; + float fpll = fout / (2 * (odir + 2)); //== fi2s (N=1, b=0, a=1) + return fpll / 2; } /** @@ -230,13 +680,12 @@ static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2 * * @return ESP_ERR_INVALID_ARG or ESP_OK */ - static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir) { int _odir, _sdm0, _sdm1, _sdm2; float avg; float min_rate, max_rate, min_diff; - if (rate/bits_per_sample/2/8 < SOC_I2S_APLL_MIN_RATE) { + if (rate / bits_per_sample / 2 / 8 < SOC_I2S_APLL_MIN_RATE) { return ESP_ERR_INVALID_ARG; } @@ -249,7 +698,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) { max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31); - avg = (max_rate + min_rate)/2; + avg = (max_rate + min_rate) / 2; if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *sdm2 = _sdm2; @@ -259,7 +708,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm for (_odir = 0; _odir < 32; _odir ++) { max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, *sdm2, _odir); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, *sdm2, _odir); - avg = (max_rate + min_rate)/2; + avg = (max_rate + min_rate) / 2; if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *odir = _odir; @@ -269,7 +718,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) { max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, *odir); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, *odir); - avg = (max_rate + min_rate)/2; + avg = (max_rate + min_rate) / 2; if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *sdm2 = _sdm2; @@ -280,7 +729,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) { max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, _sdm1, *sdm2, *odir); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, _sdm1, *sdm2, *odir); - avg = (max_rate + min_rate)/2; + avg = (max_rate + min_rate) / 2; if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *sdm1 = _sdm1; @@ -298,410 +747,122 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm return ESP_OK; } +#endif -esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch) +static esp_err_t i2s_fbclk_cal(int i2s_num, uint32_t rate, int channel, int channel_bit, uint32_t *sclk, uint32_t *fbck, uint32_t *bck_div) { - int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs) - int clkmInteger, clkmDecimals, bck = 0; - double denom = (double)1 / 64; - int channel = 2; - i2s_dma_t *save_tx = NULL, *save_rx = NULL; + //Default select I2S_D2CLK (160M) + uint32_t _sclk = I2S_LL_BASE_CLK; + uint32_t _fbck = rate * channel * channel_bit; + i2s_mclk_multiple_t multi = p_i2s[i2s_num]->mclk_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; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - - if (bits % 8 != 0 || bits > I2S_BITS_PER_SAMPLE_32BIT || bits < I2S_BITS_PER_SAMPLE_16BIT) { - ESP_LOGE(I2S_TAG, "Invalid bits per sample"); - return ESP_ERR_INVALID_ARG; - } - - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; - } - p_i2s_obj[i2s_num]->sample_rate = rate; - double clkmdiv = (double)I2S_BASE_CLK / (rate * factor); - - if (clkmdiv > 256) { - ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); - return ESP_ERR_INVALID_ARG; - } - - // wait all on-going writing finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { - xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); - } - - i2s_stop(i2s_num); +//ADC mode only support on ESP32, #if SOC_I2S_SUPPORTS_ADC_DAC - /* I2S-ADC only support single channel format. */ - if (!(p_i2s_obj[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN)) { - i2s_hal_set_rx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); + 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; } -#else - i2s_hal_set_rx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); -#endif - i2s_hal_set_tx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); +#endif // SOC_I2S_SUPPORTS_ADC_DAC - if (p_i2s_obj[i2s_num]->channel_num != (int)ch) { - p_i2s_obj[i2s_num]->channel_num = (ch == 2) ? 2 : 1; - } - - if ((int)bits != p_i2s_obj[i2s_num]->bits_per_sample) { - p_i2s_obj[i2s_num]->bits_per_sample = bits; - - // Round bytes_per_sample up to next multiple of 16 bits - int halfwords_per_sample = (bits + 15) / 16; - p_i2s_obj[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; - - // Because limited of DMA buffer is 4092 bytes - if (p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num > 4092) { - p_i2s_obj[i2s_num]->dma_buf_len = 4092 / p_i2s_obj[i2s_num]->bytes_per_sample / p_i2s_obj[i2s_num]->channel_num; + 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; } - // Re-create TX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - - save_tx = p_i2s_obj[i2s_num]->tx; - - p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->tx == NULL) { - ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_out_link_addr(&(p_i2s_obj[i2s_num]->hal), (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]); - - //destroy old tx dma if exist - if (save_tx) { - i2s_destroy_dma_queue(i2s_num, save_tx); - } +#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); } - // Re-create RX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - - save_rx = p_i2s_obj[i2s_num]->rx; - - p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->rx == NULL){ - ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_in_link(&(p_i2s_obj[i2s_num]->hal), p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->channel_num * p_i2s_obj[i2s_num]->bytes_per_sample, (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0]); - //destroy old rx dma if exist - if (save_rx) { - i2s_destroy_dma_queue(i2s_num, save_rx); - } - } - +#endif // SOC_I2S_SUPPORTS_PDM_RX + _bck_div = 8; } - double mclk; - int sdm0, sdm1, sdm2, odir, m_scale = 8; - int fi2s_clk = rate*channel*bits*m_scale; -#if SOC_I2S_SUPPORTS_ADC_DAC - if (p_i2s_obj[i2s_num]->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) { - - //DAC uses bclk as sample clock, not WS. WS can be something arbitrary. - //Rate as given to this function is the intended sample rate; - //According to the TRM, WS clk equals to the sample rate, and bclk is double the speed of WS - uint32_t b_clk = rate * I2S_AD_BCK_FACTOR; - fi2s_clk /= I2S_AD_BCK_FACTOR; - int factor2 = 60; - mclk = b_clk * factor2; - clkmdiv = ((double) I2S_BASE_CLK) / mclk; - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - bck = mclk / b_clk; -#endif -#if SOC_I2S_SUPPORTS_PDM - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_PDM) { - uint32_t b_clk = 0; - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - uint32_t fp, fs; - i2s_hal_get_tx_pdm(&(p_i2s_obj[i2s_num]->hal), &fp, &fs); - // Recommended set `fp = 960, fs = sample_rate / 100` - fs = rate / 100; - i2s_hal_tx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), fp, fs); - b_clk = rate * I2S_PDM_BCK_FACTOR * fp / fs; - - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - uint32_t dsr; - i2s_hal_get_rx_pdm(&(p_i2s_obj[i2s_num]->hal), &dsr); - b_clk = rate * I2S_PDM_BCK_FACTOR * (dsr ? 2 : 1); - } - fi2s_clk = b_clk * m_scale; - int factor2 = 5 ; - mclk = b_clk * factor2; - clkmdiv = ((double) I2S_BASE_CLK) / mclk; - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - bck = mclk / b_clk; - } else -#endif - { - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - mclk = clkmInteger + denom * clkmDecimals; - bck = factor/(bits * channel); - } - - if(p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) { - fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; - m_scale = fi2s_clk/bits/rate/channel; - } - if(p_i2s_obj[i2s_num]->use_apll && i2s_apll_calculate_fi2s(fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { - ESP_LOGD(I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); +#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); - i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), 1, 1, 0, m_scale, m_scale); - i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_APLL); - double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); - p_i2s_obj[i2s_num]->real_rate = fi2s_rate/bits/channel/m_scale; - ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0); - } else { - i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); - i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), clkmInteger, 63, clkmDecimals, bck, bck); - double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); - p_i2s_obj[i2s_num]->real_rate = real_rate; - ESP_LOGI(I2S_TAG, "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals); } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->tx->rw_pos = 0; - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - p_i2s_obj[i2s_num]->rx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->rx->rw_pos = 0; - } - - i2s_hal_set_tx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); - i2s_hal_set_rx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); - - // wait all writing on-going finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); - } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { - xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); - } - i2s_start(i2s_num); - return ESP_OK; -} - -static void IRAM_ATTR i2s_intr_handler_default(void *arg) -{ - i2s_obj_t *p_i2s = (i2s_obj_t*) arg; - uint32_t status; - i2s_hal_get_intr_status(&(p_i2s->hal), &status); - if(status == 0) { - //Avoid spurious interrupt - return; - } - - i2s_event_t i2s_event; - int dummy; - - portBASE_TYPE high_priority_task_awoken = 0; - - lldesc_t *finish_desc = NULL; - - if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) { - ESP_EARLY_LOGE(I2S_TAG, "dma error, interrupt status: 0x%08x", status); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_DMA_ERROR; - if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); - } - } - - if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) { - i2s_hal_get_out_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); - // All buffers are empty. This means we have an underflow on our hands. - if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { - xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); - // See if tx descriptor needs to be auto cleared: - // This will avoid any kind of noise that may get introduced due to transmission - // of previous data from tx descriptor on I2S line. - if (p_i2s->tx_desc_auto_clear == true) { - memset((void *) dummy, 0, p_i2s->tx->buf_size); - } - } - xQueueSendFromISR(p_i2s->tx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_TX_DONE; - if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); - } - } - - if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) { - // All buffers are full. This means we have an overflow. - i2s_hal_get_in_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); - if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { - xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); - } - xQueueSendFromISR(p_i2s->rx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_RX_DONE; - if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); - } - } - i2s_hal_clear_intr_status(&(p_i2s->hal), status); - - if (high_priority_task_awoken == pdTRUE) { - portYIELD_FROM_ISR(); - } -} - -static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) -{ - int bux_idx; - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Not initialized yet"); +#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; } - if (dma == NULL) { - ESP_LOGE(I2S_TAG, "dma is NULL"); - return ESP_ERR_INVALID_ARG; - } - for (bux_idx = 0; bux_idx < p_i2s_obj[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]); - } - } - if (dma->buf) { - free(dma->buf); - } - if (dma->desc) { - free(dma->desc); - } - vQueueDelete(dma->queue); - vSemaphoreDelete(dma->mux); - free(dma); + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), clk_src); + *sclk = _sclk; + *fbck = _fbck; + *bck_div = _bck_div; return ESP_OK; } -static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +/************************************************************** + * 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 * + **************************************************************/ + +static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) { - int bux_idx; - int sample_size = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; - i2s_dma_t *dma = (i2s_dma_t*) malloc(sizeof(i2s_dma_t)); - if (dma == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); - return NULL; - } - memset(dma, 0, sizeof(i2s_dma_t)); + switch (hal_cfg->chan_fmt) { + case I2S_CHANNEL_FMT_RIGHT_LEFT: //fall through + case I2S_CHANNEL_FMT_ALL_RIGHT: //fall through + case I2S_CHANNEL_FMT_ALL_LEFT: + return 2; + case I2S_CHANNEL_FMT_ONLY_RIGHT: //fall through + case I2S_CHANNEL_FMT_ONLY_LEFT: + return 1; +#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; - dma->buf = (char **)malloc(sizeof(char*) * dma_buf_count); - if (dma->buf == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); - free(dma); - return NULL; - } - 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(I2S_TAG, "Error malloc dma buffer"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; + for (int i = 0; chan_mask && i < 16; i++, chan_mask >>= 1) { + if ((chan_mask & 0x01) == 1) { + num++; + max_chan = i + 1; + } } - ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); - } - - dma->desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * dma_buf_count); - if (dma->desc == NULL) { - ESP_LOGE(I2S_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(I2S_TAG, "Error malloc dma description entry"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; + if (max_chan > hal_cfg->total_chan) { + hal_cfg->total_chan = max_chan; } + return num; } - - 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]); +#endif + default: + return 0; } - dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char*)); - dma->mux = xSemaphoreCreateMutex(); - dma->buf_size = dma_buf_len * sample_size; - ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); - return dma; -} - -esp_err_t i2s_start(i2s_port_t i2s_num) -{ - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - //start DMA link - I2S_ENTER_CRITICAL(); - i2s_hal_reset(&(p_i2s_obj[i2s_num]->hal)); - - esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); - i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), I2S_INTR_MAX); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_enable_tx_intr(i2s_num); - i2s_hal_start_tx(&(p_i2s_obj[i2s_num]->hal)); - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_enable_rx_intr(i2s_num); - i2s_hal_start_rx(&(p_i2s_obj[i2s_num]->hal)); - } - esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); - I2S_EXIT_CRITICAL(); - return ESP_OK; -} - -esp_err_t i2s_stop(i2s_port_t i2s_num) -{ - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_ENTER_CRITICAL(); - esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_hal_stop_tx(&(p_i2s_obj[i2s_num]->hal)); - i2s_disable_tx_intr(i2s_num); - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_hal_stop_rx(&(p_i2s_obj[i2s_num]->hal)); - i2s_disable_rx_intr(i2s_num); - } - uint32_t mask; - i2s_hal_get_intr_status(&(p_i2s_obj[i2s_num]->hal), &mask); - i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), mask); - I2S_EXIT_CRITICAL(); - return ESP_OK; } #if SOC_I2S_SUPPORTS_ADC_DAC esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) { - I2S_CHECK((dac_mode < I2S_DAC_CHANNEL_MAX), "i2s dac mode error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE((dac_mode < I2S_DAC_CHANNEL_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s dac mode error"); if (dac_mode == I2S_DAC_CHANNEL_DISABLE) { dac_output_disable(DAC_CHANNEL_1); dac_output_disable(DAC_CHANNEL_2); @@ -723,310 +884,510 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) static esp_err_t _i2s_adc_mode_recover(void) { - I2S_CHECK(((_i2s_adc_unit != -1) && (_i2s_adc_channel != -1)), "i2s ADC recover error, not initialized...", ESP_ERR_INVALID_ARG); + 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); } esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) { - I2S_CHECK((adc_unit < ADC_UNIT_2), "i2s ADC unit error, only support ADC1 for now", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE((adc_unit < ADC_UNIT_2), ESP_ERR_INVALID_ARG, TAG, "i2s ADC unit error, only support ADC1 for now"); // For now, we only support SAR ADC1. _i2s_adc_unit = adc_unit; _i2s_adc_channel = adc_channel; return adc_i2s_mode_init(adc_unit, adc_channel); } -#endif -esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +esp_err_t i2s_adc_enable(i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (pin == NULL) { -#if SOC_I2S_SUPPORTS_ADC_DAC - return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); -#else - return ESP_ERR_INVALID_ARG; -#endif - } + 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"); - if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { - ESP_LOGE(I2S_TAG, "bck_io_num error"); - return ESP_FAIL; - } - if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { - ESP_LOGE(I2S_TAG, "ws_io_num error"); - return ESP_FAIL; - } - if (pin->data_out_num != -1 && !GPIO_IS_VALID_OUTPUT_GPIO(pin->data_out_num)) { - ESP_LOGE(I2S_TAG, "data_out_num error"); - return ESP_FAIL; - } - if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { - ESP_LOGE(I2S_TAG, "data_in_num error"); - return ESP_FAIL; - } + 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); +} - int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; - //Each IIS hw module has a RX and TX unit. - //For TX unit, the output signal index should be I2SnO_xxx_OUT_IDX - //For TX unit, the input signal index should be I2SnO_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_in_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; - } - } - //For RX unit, the output signal index should be I2SnI_xxx_OUT_IDX - //For RX unit, the input signal index shuld be I2SnI_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_out_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; - } - } - //For "full-duplex + slave" mode, we should select RX signal index for ws and bck. - //For "full-duplex + master" mode, we should select TX signal index for ws and bck. - if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_SLAVE_MODE_MASK) == I2S_FULL_DUPLEX_SLAVE_MODE_MASK) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - } else if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_MASTER_MODE_MASK) == I2S_FULL_DUPLEX_MASTER_MODE_MASK) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; - } - gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); - gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); - gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); - gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); - } - ESP_LOGD(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, data_in_sig, ws_sig, bck_sig); +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"); + i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); + adc1_lock_release(); return ESP_OK; } +#endif + esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_ERR_INVALID_ARG); - return i2s_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_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]->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); } -#if SOC_I2S_SUPPORTS_PDM -esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr) +#if SOC_I2S_SUPPORTS_PCM +esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - i2s_hal_rx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), dsr); - return i2s_set_clk(i2s_num, p_i2s_obj[i2s_num]->sample_rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); + 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_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) { + i2s_hal_tx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); + } else if(p_i2s[i2s_num]->mode & I2S_MODE_RX) { + i2s_hal_rx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); + } + I2S_EXIT_CRITICAL(i2s_num); + i2s_start(i2s_num); + return ESP_OK; } #endif -static esp_err_t i2s_check_cfg_static(i2s_port_t i2s_num, const i2s_config_t *cfg) +#if SOC_I2S_SUPPORTS_PDM_RX +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"); + 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); +} +#endif + +#if SOC_I2S_SUPPORTS_PDM_TX +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"); + 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); +} +#endif + +static esp_err_t i2s_check_cfg_static(i2s_port_t i2s_num) +{ + 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. - I2S_CHECK(!((cfg->mode & I2S_MODE_ADC_BUILT_IN) && (i2s_num != I2S_NUM_0)), "I2S ADC built-in only support on I2S0", ESP_ERR_INVALID_ARG); - I2S_CHECK(!((cfg->mode & I2S_MODE_DAC_BUILT_IN) && (i2s_num != I2S_NUM_0)), "I2S DAC built-in only support on I2S0", ESP_ERR_INVALID_ARG); + 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 -#if SOC_I2S_SUPPORTS_PDM //We only check if the I2S number is invalid when set to PDM mode. - I2S_CHECK(!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), "I2S DAC PDM only support on I2S0", ESP_ERR_INVALID_ARG); + 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; -#endif - I2S_CHECK(cfg->communication_format && (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), "invalid communication formats", ESP_ERR_INVALID_ARG); - I2S_CHECK(!((cfg->communication_format & I2S_COMM_FORMAT_STAND_MSB) && (cfg->communication_format & I2S_COMM_FORMAT_STAND_PCM_LONG)), "multiple communication formats specified", ESP_ERR_INVALID_ARG); + 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"); return ESP_OK; } -static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +static esp_err_t i2s_param_config(i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config), "param null", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_check_cfg_static(i2s_num, i2s_config) == ESP_OK), "param check error", ESP_ERR_INVALID_ARG); + 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 - if(i2s_config->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(); } #endif - // configure I2S data port interface. - i2s_hal_config_param(&(p_i2s_obj[i2s_num]->hal), i2s_config); - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { - i2s_hal_enable_sig_loopback(&(p_i2s_obj[i2s_num]->hal)); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - i2s_hal_enable_master_mode(&(p_i2s_obj[i2s_num]->hal)); + 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_mode(&(p_i2s_obj[i2s_num]->hal)); - } - } - - p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll; - p_i2s_obj[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - p_i2s_obj[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; - return ESP_OK; -} - -esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) -{ - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num]->rx && p_i2s_obj[i2s_num]->rx->buf != NULL && p_i2s_obj[i2s_num]->rx->buf_size != 0) { - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { - memset(p_i2s_obj[i2s_num]->rx->buf[i], 0, p_i2s_obj[i2s_num]->rx->buf_size); - } - } - if (p_i2s_obj[i2s_num]->tx && p_i2s_obj[i2s_num]->tx->buf != NULL && p_i2s_obj[i2s_num]->tx->buf_size != 0) { - int bytes_left = 0; - bytes_left = (p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[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); - } - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { - memset(p_i2s_obj[i2s_num]->tx->buf[i], 0, p_i2s_obj[i2s_num]->tx->buf_size); + i2s_hal_enable_slave_fd_mode(&(p_i2s[i2s_num]->hal)); } } return ESP_OK; } -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 i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch) { - esp_err_t err; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), "I2S buffer length at most 1024 and more than 8", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) { - p_i2s_obj[i2s_num] = (i2s_obj_t*) malloc(sizeof(i2s_obj_t)); - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); - return ESP_ERR_NO_MEM; + 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"); + + 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 SOC_I2S_SUPPORTS_TDM + cfg->chan_mask = ch & 0xFFFF; + active_chan_num = i2s_get_active_chan_num(cfg); + chan_num = cfg->total_chan; +#else + active_chan_num = i2s_get_active_chan_num(cfg); + chan_num = ch == I2S_CHANNEL_MONO ? 2 : active_chan_num; +#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) { + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); } - memset(p_i2s_obj[i2s_num], 0, sizeof(i2s_obj_t)); - - portMUX_TYPE i2s_spinlock_unlocked[1] = {portMUX_INITIALIZER_UNLOCKED}; - for (int x = 0; x < I2S_NUM_MAX; x++) { - i2s_spinlock[x] = i2s_spinlock_unlocked[0]; + 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) { + xSemaphoreGive(p_i2s[i2s_num]->rx->mux); } - //To make sure hardware is enabled before any hardware register operations. - periph_module_enable(i2s_periph_signal[i2s_num].module); - i2s_hal_init(&(p_i2s_obj[i2s_num]->hal), i2s_num); + 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); + } - p_i2s_obj[i2s_num]->i2s_num = i2s_num; - p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; - p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; - p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; - p_i2s_obj[i2s_num]->mode = i2s_config->mode; + //I2S start + i2s_start(i2s_num); + p_i2s[i2s_num]->sample_rate = rate; + return ESP_OK; +} - p_i2s_obj[i2s_num]->bits_per_sample = 0; - p_i2s_obj[i2s_num]->bytes_per_sample = 0; // Not initialized yet - p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + + +/************************************************************** + * I2S driver operation * + * - i2s_start * + * - i2s_stop * + * - i2s_driver_install * + * - i2s_write * + * - i2s_write_expand * + * - i2s_read * + **************************************************************/ + +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) { + i2s_tx_reset(i2s_num); + i2s_tx_start(i2s_num); + } + if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + i2s_rx_reset(i2s_num); + i2s_rx_start(i2s_num); + } +#if !SOC_GDMA_SUPPORTED + esp_intr_enable(p_i2s[i2s_num]->i2s_isr_handle); +#endif + I2S_EXIT_CRITICAL(i2s_num); + return ESP_OK; +} + +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"); + I2S_ENTER_CRITICAL(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) { + i2s_tx_stop(i2s_num); + } + if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + i2s_rx_stop(i2s_num); + } +#if !SOC_GDMA_SUPPORTED + i2s_hal_clear_intr_status(&(p_i2s[i2s_num]->hal), I2S_INTR_MAX); +#endif + I2S_EXIT_CRITICAL(i2s_num); + return ESP_OK; +} + +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; + 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->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"); + if (p_i2s[i2s_num] != NULL) { + ESP_LOGW(TAG, "I2S driver already installed"); + return ESP_OK; + } + + p_i2s[i2s_num] = (i2s_obj_t *) calloc(1, sizeof(i2s_obj_t)); + if (p_i2s[i2s_num] == NULL) { + ESP_LOGE(TAG, "Malloc I2S driver error"); + return ESP_ERR_NO_MEM; + } + + portMUX_TYPE i2s_spinlock_unlocked[1] = {portMUX_INITIALIZER_UNLOCKED}; + for (int x = 0; x < I2S_NUM_MAX; x++) { + i2s_spinlock[x] = i2s_spinlock_unlocked[0]; + } + //To make sure hardware is enabled before any hardware register operations. + periph_module_enable(i2s_periph_signal[i2s_num].module); + i2s_hal_init(&(p_i2s[i2s_num]->hal), i2s_num); + + // Set I2S HAL configurations + 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.bits_cfg.sample_bits = i2s_config->bits_per_sample; + p_i2s[i2s_num]->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: + 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; + active_chan = 2; + break; + case I2S_CHANNEL_FMT_ONLY_RIGHT: + p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; + p_i2s[i2s_num]->hal_cfg.total_chan = 1; + active_chan = 1; + break; + case I2S_CHANNEL_FMT_ONLY_LEFT: + p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; + p_i2s[i2s_num]->hal_cfg.total_chan = 1; + active_chan = 1; + break; + case I2S_CHANNEL_FMT_MULTIPLE: + ESP_RETURN_ON_FALSE((i2s_config->chan_mask != 0), ESP_ERR_INVALID_ARG, TAG, "i2s all channel are disabled"); + p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->chan_mask; + i2s_get_active_chan_num(&p_i2s[i2s_num]->hal_cfg); + break; + default: + ESP_LOGE(TAG, "wrong i2s channel format, uninstalled i2s."); + goto err; + } + 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; +#endif + + // Set I2S driver configurations + p_i2s[i2s_num]->i2s_num = i2s_num; + p_i2s[i2s_num]->mode = i2s_config->mode; + p_i2s[i2s_num]->channel_num = i2s_get_active_chan_num(&p_i2s[i2s_num]->hal_cfg); + p_i2s[i2s_num]->i2s_queue = i2s_queue; + p_i2s[i2s_num]->bits_per_sample = 0; + p_i2s[i2s_num]->bytes_per_sample = 0; // Not initialized yet + 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]->mclk_multiple = i2s_config->mclk_multiple; #ifdef CONFIG_PM_ENABLE +#if SOC_I2S_SUPPORTS_APLL if (i2s_config->use_apll) { - err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &p_i2s_obj[i2s_num]->pm_lock); - } else { - err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &p_i2s_obj[i2s_num]->pm_lock); + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock); + } else +#endif // SOC_I2S_SUPPORTS_APLL + { + ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock); } - if (err != ESP_OK) { - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE(I2S_TAG, "I2S pm lock error"); - return err; + if (ret != ESP_OK) { + free(p_i2s[i2s_num]); + p_i2s[i2s_num] = NULL; + ESP_LOGE(TAG, "I2S pm lock error"); + return ret; } #endif //CONFIG_PM_ENABLE - - //initial interrupt - err = i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s_obj[i2s_num], &p_i2s_obj[i2s_num]->i2s_isr_handle); - if (err != ESP_OK) { -#ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); - } +#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; #endif - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); - return err; - } - i2s_stop(i2s_num); - err = i2s_param_config(i2s_num, i2s_config); - if (err != ESP_OK) { - i2s_driver_uninstall(i2s_num); - ESP_LOGE(I2S_TAG, "I2S param configure error"); - return err; - } - - if (i2s_queue) { - p_i2s_obj[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - *((QueueHandle_t*) i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; - ESP_LOGI(I2S_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); - } else { - p_i2s_obj[i2s_num]->i2s_queue = NULL; - } - //set clock and start - return i2s_set_clk(i2s_num, i2s_config->sample_rate, i2s_config->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); + gdma_channel_alloc_config_t dma_cfg = {.flags.reserve_sibling = 1}; + if ( p_i2s[i2s_num]->mode & I2S_MODE_RX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; + ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->rx_dma_chan), err, TAG, "Register rx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_connect(p_i2s[i2s_num]->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(p_i2s[i2s_num]->rx_dma_chan, &cb, p_i2s[i2s_num]); + } + if ( p_i2s[i2s_num]->mode & I2S_MODE_TX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; + ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->tx_dma_chan), err, TAG, "Register tx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_connect(p_i2s[i2s_num]->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(p_i2s[i2s_num]->tx_dma_chan, &cb, p_i2s[i2s_num]); + } +#else + //initial interrupt + ret = esp_intr_alloc(i2s_periph_signal[i2s_num].irq, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s[i2s_num], &p_i2s[i2s_num]->i2s_isr_handle); + ESP_GOTO_ON_ERROR(ret, err, TAG, "Register I2S Interrupt error"); +#endif // SOC_GDMA_SUPPORTED + i2s_stop(i2s_num); + p_i2s[i2s_num]->use_apll = i2s_config->use_apll; + p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; + p_i2s[i2s_num]->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"); + if (i2s_queue) { + p_i2s[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + ESP_GOTO_ON_ERROR((p_i2s[i2s_num]->i2s_queue != NULL), err, TAG, "I2S queue create failed"); + *((QueueHandle_t *) i2s_queue) = p_i2s[i2s_num]->i2s_queue; + ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s[i2s_num]->i2s_queue)); + } else { + p_i2s[i2s_num]->i2s_queue = NULL; } - ESP_LOGW(I2S_TAG, "I2S driver already installed"); - return ESP_OK; + //set clock and start +#if SOC_I2S_SUPPORTS_TDM + ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, + p_i2s[i2s_num]->hal_cfg.bits_cfg.val, + (i2s_channel_t)active_chan); +#else + ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, + p_i2s[i2s_num]->hal_cfg.bits_cfg.val, + I2S_CHANNEL_STEREO); +#endif + ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S set clock failed"); + return ret; + +err: +#ifdef CONFIG_PM_ENABLE + if (p_i2s[i2s_num]->pm_lock) { + esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); + } +#endif + i2s_driver_uninstall(i2s_num); + return ret; } esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGI(I2S_TAG, "already uninstalled"); + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + if (p_i2s[i2s_num] == NULL) { + ESP_LOGI(TAG, "already uninstalled"); return ESP_OK; } i2s_stop(i2s_num); - esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); - - if (p_i2s_obj[i2s_num]->tx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); - p_i2s_obj[i2s_num]->tx = NULL; +#if SOC_I2S_SUPPORTS_ADC_DAC + i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); +#endif +#if SOC_GDMA_SUPPORTED + if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + gdma_disconnect(p_i2s[i2s_num]->tx_dma_chan); + gdma_del_channel(p_i2s[i2s_num]->tx_dma_chan); } - if (p_i2s_obj[i2s_num]->rx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); - p_i2s_obj[i2s_num]->rx = NULL; + if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + gdma_disconnect(p_i2s[i2s_num]->rx_dma_chan); + gdma_del_channel(p_i2s[i2s_num]->rx_dma_chan); + } +#else + 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; } - if (p_i2s_obj[i2s_num]->i2s_queue) { - vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); - p_i2s_obj[i2s_num]->i2s_queue = NULL; + if (p_i2s[i2s_num]->i2s_queue) { + vQueueDelete(p_i2s[i2s_num]->i2s_queue); + p_i2s[i2s_num]->i2s_queue = NULL; } - if(p_i2s_obj[i2s_num]->use_apll) { +#if SOC_I2S_SUPPORTS_APLL + if (p_i2s[i2s_num]->use_apll) { // switch back to PLL clock source - i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); rtc_clk_apll_enable(0, 0, 0, 0, 0); } -#ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); - } #endif - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - periph_module_disable(i2s_periph_signal[i2s_num].module); +#ifdef CONFIG_PM_ENABLE + if (p_i2s[i2s_num]->pm_lock) { + esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); + } +#endif + free(p_i2s[i2s_num]); + p_i2s[i2s_num] = NULL; +#if !SOC_GDMA_SUPPORTED + periph_module_disable(i2s_periph_signal[i2s_num].module); +#endif return ESP_OK; } @@ -1035,112 +1396,84 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by char *data_ptr, *src_byte; size_t bytes_can_write; *bytes_written = 0; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + 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"); + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); #endif src_byte = (char *)src; while (size > 0) { - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + 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) { break; } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + p_i2s[i2s_num]->tx->rw_pos = 0; } - ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); - data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + ESP_LOGD(TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s[i2s_num]->tx->rw_pos, p_i2s[i2s_num]->tx->buf_size, (int)p_i2s[i2s_num]->tx->curr_ptr); + data_ptr = (char *)p_i2s[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s[i2s_num]->tx->rw_pos; + bytes_can_write = p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos; if (bytes_can_write > size) { bytes_can_write = size; } memcpy(data_ptr, src_byte, bytes_can_write); size -= bytes_can_write; src_byte += bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + p_i2s[i2s_num]->tx->rw_pos += bytes_can_write; (*bytes_written) += bytes_can_write; } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release(p_i2s[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); return ESP_OK; } -#if SOC_I2S_SUPPORTS_ADC_DAC -esp_err_t i2s_adc_enable(i2s_port_t i2s_num) -{ - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num] != NULL), "Not initialized yet", ESP_ERR_INVALID_STATE); - I2S_CHECK((p_i2s_obj[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), "i2s built-in adc not enabled", ESP_ERR_INVALID_STATE); - - adc1_dma_mode_acquire(); - _i2s_adc_mode_recover(); - i2s_hal_start_rx(&(p_i2s_obj[i2s_num]->hal)); - i2s_hal_reset(&(p_i2s_obj[i2s_num]->hal)); - return i2s_set_clk(i2s_num, p_i2s_obj[i2s_num]->sample_rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); -} - -esp_err_t i2s_adc_disable(i2s_port_t i2s_num) -{ - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num] != NULL), "Not initialized yet", ESP_ERR_INVALID_STATE); - I2S_CHECK((p_i2s_obj[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), "i2s built-in adc not enabled", ESP_ERR_INVALID_STATE); - - i2s_hal_stop_rx(&(p_i2s_obj[i2s_num]->hal)); - adc1_lock_release(); - return ESP_OK; -} -#endif - 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) { char *data_ptr; int bytes_can_write, tail; int src_bytes, aim_bytes, zero_bytes; *bytes_written = 0; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size > 0), "size must greater than zero", ESP_ERR_INVALID_ARG); - I2S_CHECK((aim_bits * size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((aim_bits >= src_bits), "aim_bits mustn't be less than src_bits", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + 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"); if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) { - ESP_LOGE(I2S_TAG,"bits mustn't be less than 8, src_bits %d aim_bits %d", src_bits, aim_bits); + 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; } if (src_bits > I2S_BITS_PER_SAMPLE_32BIT || aim_bits > I2S_BITS_PER_SAMPLE_32BIT) { - ESP_LOGE(I2S_TAG,"bits mustn't be greater than 32, src_bits %d aim_bits %d", src_bits, aim_bits); + ESP_LOGE(TAG, "bits mustn't be greater than 32, src_bits %d aim_bits %d", src_bits, aim_bits); return ESP_ERR_INVALID_ARG; } if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT || src_bits == I2S_BITS_PER_SAMPLE_32BIT) && (size % 2 != 0)) { - ESP_LOGE(I2S_TAG,"size must be a even number while src_bits is even, src_bits %d size %d", src_bits, size); + ESP_LOGE(TAG, "size must be a even number while src_bits is even, src_bits %d size %d", src_bits, size); return ESP_ERR_INVALID_ARG; } if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) { - ESP_LOGE(I2S_TAG,"size must be a multiple of 3 while src_bits is 24, size %d", size); + ESP_LOGE(TAG, "size must be a multiple of 3 while src_bits is 24, size %d", size); return ESP_ERR_INVALID_ARG; } src_bytes = src_bits / 8; aim_bytes = aim_bits / 8; zero_bytes = aim_bytes - src_bytes; - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); size = size * aim_bytes / src_bytes; - ESP_LOGD(I2S_TAG,"aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, size); + ESP_LOGD(TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, size); while (size > 0) { - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + 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) { break; } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + p_i2s[i2s_num]->tx->rw_pos = 0; } - data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + data_ptr = (char *)p_i2s[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s[i2s_num]->tx->rw_pos; + bytes_can_write = p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos; if (bytes_can_write > (int)size) { bytes_can_write = size; } @@ -1154,47 +1487,48 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz (*bytes_written) += (aim_bytes - zero_bytes); } size -= bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + p_i2s[i2s_num]->tx->rw_pos += bytes_can_write; } - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); return ESP_OK; } 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; char *data_ptr, *dest_byte; int bytes_can_read; *bytes_read = 0; dest_byte = (char *)dest; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->rx), "rx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + 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"); + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); #endif while (size > 0) { - if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + if (p_i2s[i2s_num]->rx->rw_pos == p_i2s[i2s_num]->rx->buf_size || p_i2s[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s[i2s_num]->rx->queue, &p_i2s[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + ret = ESP_ERR_TIMEOUT; break; } - p_i2s_obj[i2s_num]->rx->rw_pos = 0; + p_i2s[i2s_num]->rx->rw_pos = 0; } - data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; - bytes_can_read = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + data_ptr = (char *)p_i2s[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s[i2s_num]->rx->rw_pos; + bytes_can_read = p_i2s[i2s_num]->rx->buf_size - p_i2s[i2s_num]->rx->rw_pos; if (bytes_can_read > (int)size) { bytes_can_read = size; } memcpy(dest_byte, data_ptr, bytes_can_read); size -= bytes_can_read; dest_byte += bytes_can_read; - p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + p_i2s[i2s_num]->rx->rw_pos += bytes_can_read; (*bytes_read) += bytes_can_read; } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release(p_i2s[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); - return ESP_OK; + xSemaphoreGive(p_i2s[i2s_num]->rx->mux); + return ret; } diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index 4ee73b594d..3f6031ed26 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -13,7 +13,6 @@ #include "soc/i2s_periph.h" #include "soc/rtc_periph.h" #include "soc/soc_caps.h" -#include "hal/i2s_hal.h" #include "hal/i2s_types.h" #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" @@ -28,7 +27,119 @@ extern "C" { #define I2S_PIN_NO_CHANGE (-1) /*!< Use in i2s_pin_config_t for pins which should not be changed */ -typedef intr_handle_t i2s_isr_handle_t; +/** + * @brief I2S port number, the max port number is (I2S_NUM_MAX -1). + */ +typedef enum { + I2S_NUM_0 = 0, /*!< I2S port 0 */ +#if SOC_I2S_NUM > 1 + I2S_NUM_1 = 1, /*!< I2S port 1 */ +#endif + I2S_NUM_MAX, /*!< I2S port max */ +} i2s_port_t; + +#if SOC_I2S_SUPPORTS_PCM +/** + * @brief I2S PCM configuration + * + */ +typedef struct { + i2s_pcm_compress_t pcm_type; /*!< I2S PCM a/u-law decompress or compress type */ +} i2s_pcm_cfg_t; +#endif + +#if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief Default I2S PDM Up-Sampling Rate configuration + */ +#define I2S_PDM_DEFAULT_UPSAMPLE_CONFIG(rate) { \ + .sample_rate = rate, \ + .fp = 960, \ + .fs = (rate) / 100, \ + } + +/** + * @brief I2S PDM up-sample rate configuration + * @note TX PDM can only be set to the following two upsampling rate configurations: + * 1: fp = 960, fs = sample_rate / 100, in this case, Fpdm = 128*48000 + * 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate + * If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000. + * Otherwise, the second configuration should be applied. + */ +typedef struct { + int sample_rate; /*!< I2S PDM sample rate */ + int fp; /*!< I2S PDM TX upsampling paramater. Normally it should be set to 960 */ + int fs; /*!< I2S PDM TX upsampling paramater. When it is set to 480, the pdm clock frequency Fpdm = 128 * sample_rate, when it is set to sample_rate / 100, Fpdm will be fixed to 128*48000 */ +} i2s_pdm_tx_upsample_cfg_t; +#endif + +/** + * @brief I2S pin number for i2s_set_pin + * + */ +typedef struct { + int mck_io_num; /*!< MCK in out pin*/ + int bck_io_num; /*!< BCK in out pin*/ + int ws_io_num; /*!< WS in out pin*/ + int data_out_num; /*!< DATA out pin*/ + int data_in_num; /*!< DATA in pin*/ +} i2s_pin_config_t; + +/** + * @brief I2S driver configuration parameters + * + */ +typedef struct { + + i2s_mode_t mode; /*!< I2S work mode */ + uint32_t sample_rate; /*!< I2S sample rate */ + i2s_bits_per_sample_t bits_per_sample; /*!< I2S sample bits in one channel */ + i2s_channel_fmt_t channel_format; /*!< I2S channel format.*/ + i2s_comm_format_t communication_format; /*!< I2S communication format */ + int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */ + int dma_buf_count; /*!< I2S DMA Buffer Count */ + int dma_buf_len; /*!< I2S DMA Buffer Length */ + bool use_apll; /*!< I2S using APLL as main I2S clock, enable it to get accurate clock */ + bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor if there is underflow condition (helps in avoiding noise in case of data unavailability) */ + int fixed_mclk; /*!< I2S using fixed MCLK output. If use_apll = true and fixed_mclk > 0, then the clock output for i2s is fixed and equal to the fixed_mclk value. If fixed_mclk set, mclk_multiple won't take effect */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of I2S master clock(MCLK) to sample rate */ + i2s_bits_per_chan_t bits_per_chan; /*!< I2S total bits in one channel, only take effect when larger than 'bits_per_sample', default '0' means equal to 'bits_per_sample' */ + +#if SOC_I2S_SUPPORTS_TDM + i2s_channel_t chan_mask; /*!< I2S active channel bit mask, set value in `i2s_channel_t` to enable specific channel, the bit map of active channel can not exceed (0x1< 1 - case I2S_TEST_MODE_SLAVE_TO_MAXTER: { - esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0I_BCK_OUT_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1O_BCK_IN_IDX, 0); + case I2S_TEST_MODE_SLAVE_TO_MAXTER: { + esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0I_BCK_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1O_BCK_IN_IDX, 0); - esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0I_WS_OUT_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1O_WS_IN_IDX, 0); + esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0I_WS_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1O_WS_IN_IDX, 0); - esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S1O_DATA_OUT23_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0I_DATA_IN15_IDX, 0); - } - break; + esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S1_DATA_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0_DATA_IN_IDX, 0); + } + break; - case I2S_TEST_MODE_MASTER_TO_SLAVE: { - esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0O_BCK_OUT_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1I_BCK_IN_IDX, 0); + case I2S_TEST_MODE_MASTER_TO_SLAVE: { + esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0O_BCK_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1I_BCK_IN_IDX, 0); - esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0O_WS_OUT_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1I_WS_IN_IDX, 0); + esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0O_WS_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1I_WS_IN_IDX, 0); - esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0O_DATA_OUT23_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S1I_DATA_IN15_IDX, 0); - } - break; + esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0_DATA_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S1_DATA_IN_IDX, 0); + } + break; #endif - case I2S_TEST_MODE_LOOPBACK: { - esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0O_DATA_OUT23_IDX, 0, 0); - esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0I_DATA_IN15_IDX, 0); - } - break; + case I2S_TEST_MODE_LOOPBACK: { + esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0_DATA_OUT_IDX, 0, 0); + esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0_DATA_IN_IDX, 0); + } + break; - default: { - TEST_FAIL_MESSAGE("error: mode not supported"); - } - break; + default: { + TEST_FAIL_MESSAGE("error: mode not supported"); + } + break; } } @@ -115,18 +144,9 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]") .dma_buf_count = 6, .dma_buf_len = 60, .use_apll = 0, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 , + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; -#if CONFIG_IDF_TARGET_ESP32 - //install and start i2s driver - TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)); - //for internal DAC, this will enable both of the internal channels - TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, NULL)); - //stop & destroy i2s driver - TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); -#endif - // normal i2s i2s_pin_config_t pin_config = { .bck_io_num = MASTER_BCK_IO, @@ -160,7 +180,7 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]") .dma_buf_count = 6, .dma_buf_len = 100, .use_apll = 0, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 , + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; i2s_pin_config_t master_pin_config = { .bck_io_num = MASTER_BCK_IO, @@ -173,29 +193,28 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]") i2s_test_io_config(I2S_TEST_MODE_LOOPBACK); printf("\r\nheap size: %d\n", esp_get_free_heap_size()); - uint8_t* data_wr = (uint8_t*)malloc(sizeof(uint8_t)*400); + uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400); size_t i2s_bytes_write = 0; size_t bytes_read = 0; int length = 0; - uint8_t *i2s_read_buff = (uint8_t*)malloc(sizeof(uint8_t)*10000); + uint8_t *i2s_read_buff = (uint8_t *)malloc(sizeof(uint8_t) * 10000); - for(int i=0; i<100; i++) { - data_wr[i] = i+1; + for (int i = 0; i < 100; i++) { + data_wr[i] = i + 1; } - int flag=0; // break loop flag + int flag = 0; // break loop flag int end_position = 0; // write data to slave - i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t)*400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); - while(!flag){ + i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t) * 400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); + while (!flag) { if (length >= 10000 - 500) { break; } - i2s_read(I2S_NUM_0, i2s_read_buff + length, sizeof(uint8_t)*500, &bytes_read, 1000/portMAX_DELAY); - if(bytes_read>0) { - printf("read data size: %d\n", bytes_read); - for(int i=length; i 0) { + for (int i = length; i < length + bytes_read; i++) { + if (i2s_read_buff[i] == 100) { + flag = 1; end_position = i; break; } @@ -204,70 +223,16 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]") length = length + bytes_read; } // test the read data right or not - for(int i=end_position-99; i<=end_position; i++) { - TEST_ASSERT_EQUAL_UINT8((i-end_position+100), *(i2s_read_buff + i)); + for (int i = end_position - 99; i <= end_position; i++) { + TEST_ASSERT_EQUAL_UINT8((i - end_position + 100), *(i2s_read_buff + i)); } free(data_wr); free(i2s_read_buff); i2s_driver_uninstall(I2S_NUM_0); } -#if !DISABLED_FOR_TARGETS(ESP32S2) -/* ESP32S2 has only single I2S port and hence following test cases are not applicable */ -TEST_CASE("I2S adc test", "[i2s]") -{ - // init I2S ADC - i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN, - .sample_rate = SAMPLE_RATE, - .bits_per_sample = SAMPLE_BITS, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .intr_alloc_flags = 0, - .dma_buf_count = 2, - .dma_buf_len = 1024, - .use_apll = 0, - }; - // install and start I2S driver - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - // init ADC pad - i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_4); - // enable adc sampling, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11 hard-coded in adc_i2s_mode_init - i2s_adc_enable(I2S_NUM_0); - // init read buffer - uint16_t* i2sReadBuffer = (uint16_t*)calloc(1024, sizeof(uint16_t)); - size_t bytesRead; - - for (int loop = 0; loop < 10; loop++) { - for (int level = 0; level <= 1; level++) { - if (level == 0) { - gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLDOWN_ONLY); - } else { - gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLUP_ONLY); - } - vTaskDelay(200 / portTICK_RATE_MS); - // read data from adc, will block until buffer is full - i2s_read(I2S_NUM_0, (void*)i2sReadBuffer, 1024 * sizeof(uint16_t), &bytesRead, portMAX_DELAY); - - // calc average - int64_t adcSumValue = 0; - for (size_t i = 0; i < 1024; i++) { - adcSumValue += i2sReadBuffer[i] & 0xfff; - } - int adcAvgValue = adcSumValue / 1024; - printf("adc average val: %d\n", adcAvgValue); - - if (level == 0) { - TEST_ASSERT_LESS_THAN(100, adcAvgValue); - } else { - TEST_ASSERT_GREATER_THAN(4000, adcAvgValue); - } - } - } - i2s_adc_disable(I2S_NUM_0); - free(i2sReadBuffer); - i2s_driver_uninstall(I2S_NUM_0); -} - +#if SOC_I2S_NUM > 1 +/* ESP32S2 and ESP32C3 has only single I2S port and hence following test cases are not applicable */ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s]") { // master driver installed and send data @@ -280,7 +245,7 @@ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s]") .dma_buf_count = 6, .dma_buf_len = 100, .use_apll = 0, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 , + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; i2s_pin_config_t master_pin_config = { .bck_io_num = MASTER_BCK_IO, @@ -302,7 +267,7 @@ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s]") .dma_buf_count = 6, .dma_buf_len = 100, .use_apll = 0, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 , + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; i2s_pin_config_t slave_pin_config = { .bck_io_num = SLAVE_BCK_IO, @@ -316,26 +281,27 @@ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s]") i2s_test_io_config(I2S_TEST_MODE_MASTER_TO_SLAVE); printf("\r\nheap size: %d\n", esp_get_free_heap_size()); - uint8_t* data_wr = (uint8_t*)malloc(sizeof(uint8_t)*400); + uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400); size_t i2s_bytes_write = 0; size_t bytes_read = 0; int length = 0; - uint8_t *i2s_read_buff = (uint8_t*)malloc(sizeof(uint8_t)*10000); + uint8_t *i2s_read_buff = (uint8_t *)malloc(sizeof(uint8_t) * 10000); - for(int i=0; i<100; i++) { - data_wr[i] = i+1; + for (int i = 0; i < 100; i++) { + data_wr[i] = i + 1; } - int flag=0; // break loop flag + int flag = 0; // break loop flag int end_position = 0; // write data to slave - i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t)*400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); - while(!flag){ - i2s_read(I2S_NUM_1, i2s_read_buff + length, sizeof(uint8_t)*500, &bytes_read, 1000/portMAX_DELAY); - if(bytes_read>0) { + i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t) * 400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); + printf("write data size: %d\n", i2s_bytes_write); + while (!flag) { + i2s_read(I2S_NUM_1, i2s_read_buff + length, sizeof(uint8_t) * 500, &bytes_read, 1000 / portTICK_PERIOD_MS); + if (bytes_read > 0) { printf("read data size: %d\n", bytes_read); - for(int i=length; i 0) { - for(int i=length; i 0) { + for (int i = length; i < length + bytes_read; i++) { + if (i2s_read_buff[i] == 100) { + flag = 1; end_position = i; break; } @@ -429,8 +396,8 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]") length = length + bytes_read; } // test the readed data right or not - for(int i=end_position-99; i<=end_position; i++) { - TEST_ASSERT_EQUAL_UINT8((i-end_position+100), *(i2s_read_buff + i)); + for (int i = end_position - 99; i <= end_position; i++) { + TEST_ASSERT_EQUAL_UINT8((i - end_position + 100), *(i2s_read_buff + i)); } free(data_wr); free(i2s_read_buff); @@ -450,7 +417,7 @@ TEST_CASE("I2S memory leaking test", "[i2s]") .dma_buf_count = 6, .dma_buf_len = 100, .use_apll = 0, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 , + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; i2s_pin_config_t master_pin_config = { .bck_io_num = MASTER_BCK_IO, @@ -464,7 +431,7 @@ TEST_CASE("I2S memory leaking test", "[i2s]") i2s_driver_uninstall(I2S_NUM_0); int initial_size = esp_get_free_heap_size(); - for(int i=0; i<100; i++) { + for (int i = 0; i < 100; i++) { TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL)); TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config)); i2s_driver_uninstall(I2S_NUM_0); @@ -496,7 +463,9 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]") .communication_format = I2S_COMM_FORMAT_STAND_I2S, .dma_buf_count = 6, .dma_buf_len = 60, +#if SOC_I2S_SUPPORTS_APLL .use_apll = true, +#endif .intr_alloc_flags = 0, }; @@ -508,14 +477,14 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]") uint32_t sample_rate_arr[8] = { 10675, 11025, 16000, 22050, 32000, 44100, 48000, 96000 }; int bits_per_sample_arr[3] = { 16, 24, 32 }; - for (int i = 0; i < (sizeof(sample_rate_arr)/sizeof(sample_rate_arr[0])); i++) { - for (int j = 0; j < (sizeof(bits_per_sample_arr)/sizeof(bits_per_sample_arr[0])); j++) { + for (int i = 0; i < (sizeof(sample_rate_arr) / sizeof(sample_rate_arr[0])); i++) { + for (int j = 0; j < (sizeof(bits_per_sample_arr) / sizeof(bits_per_sample_arr[0])); j++) { i2s_config.sample_rate = sample_rate_arr[i]; i2s_config.bits_per_sample = bits_per_sample_arr[j]; TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)); TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &pin_config)); - TEST_ASSERT((fabs((i2s_get_clk(I2S_NUM_0) - sample_rate_arr[i]))/(sample_rate_arr[i]))*100 < PERCENT_DIFF); + TEST_ASSERT((fabs((i2s_get_clk(I2S_NUM_0) - sample_rate_arr[i])) / (sample_rate_arr[i])) * 100 < PERCENT_DIFF); TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); TEST_ASSERT(initial_size == esp_get_free_heap_size()); } @@ -525,4 +494,93 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]") TEST_ASSERT(initial_size == esp_get_free_heap_size()); } +#if SOC_I2S_SUPPORTS_ADC_DAC +/* Only ESP32 need I2S adc/dac test */ +TEST_CASE("I2S adc test", "[i2s]") +{ + // init I2S ADC + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN, + .sample_rate = SAMPLE_RATE, + .bits_per_sample = SAMPLE_BITS, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .intr_alloc_flags = 0, + .dma_buf_count = 2, + .dma_buf_len = 1024, + .use_apll = 0, + }; + // install and start I2S driver + i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); + // init ADC pad + i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_4); + // enable adc sampling, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11 hard-coded in adc_i2s_mode_init + i2s_adc_enable(I2S_NUM_0); + // init read buffer + uint16_t *i2sReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t)); + size_t bytesRead; + + for (int loop = 0; loop < 10; loop++) { + for (int level = 0; level <= 1; level++) { + if (level == 0) { + gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLDOWN_ONLY); + } else { + gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLUP_ONLY); + } + vTaskDelay(200 / portTICK_RATE_MS); + // read data from adc, will block until buffer is full + i2s_read(I2S_NUM_0, (void *)i2sReadBuffer, 1024 * sizeof(uint16_t), &bytesRead, portMAX_DELAY); + + // calc average + int64_t adcSumValue = 0; + for (size_t i = 0; i < 1024; i++) { + adcSumValue += i2sReadBuffer[i] & 0xfff; + } + int adcAvgValue = adcSumValue / 1024; + printf("adc average val: %d\n", adcAvgValue); + + if (level == 0) { + if (adcAvgValue > 100) { + i2s_adc_disable(I2S_NUM_0); + free(i2sReadBuffer); + i2s_driver_uninstall(I2S_NUM_0); + TEST_ASSERT_LESS_THAN(100, adcAvgValue); + } + } else { + if (adcAvgValue < 4000) { + i2s_adc_disable(I2S_NUM_0); + free(i2sReadBuffer); + i2s_driver_uninstall(I2S_NUM_0); + TEST_ASSERT_GREATER_THAN(4000, adcAvgValue); + } + } + } + } + + i2s_adc_disable(I2S_NUM_0); + free(i2sReadBuffer); + i2s_driver_uninstall(I2S_NUM_0); +} + +TEST_CASE("I2S dac test", "[i2s]") +{ + // dac, adc i2s + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = SAMPLE_RATE, + .bits_per_sample = SAMPLE_BITS, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .dma_buf_count = 6, + .dma_buf_len = 60, + .use_apll = 0, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + }; + + //install and start i2s driver + TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)); + //for internal DAC, this will enable both of the internal channels + TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, NULL)); + //stop & destroy i2s driver + TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); +} #endif diff --git a/components/hal/esp32/include/hal/i2s_ll.h b/components/hal/esp32/include/hal/i2s_ll.h index 96d4a3f8ec..95968eb27c 100644 --- a/components/hal/esp32/include/hal/i2s_ll.h +++ b/components/hal/esp32/include/hal/i2s_ll.h @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,9 +23,6 @@ #pragma once #include -#include "soc/rtc_periph.h" -#include "soc/rtc.h" -#include "soc/efuse_periph.h" #include "soc/i2s_periph.h" #include "hal/i2s_types.h" @@ -36,87 +33,284 @@ extern "C" { // Get I2S hardware instance with giving i2s num #define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : (((num) == 1) ? (&I2S1) : NULL)) -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (0xFFFFFFFF) +#define I2S_LL_AD_BCK_FACTOR (2) +#define I2S_LL_PDM_BCK_FACTOR (64) +#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ) + +#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6) +#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) + +/* I2S clock configuration structure */ +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 + uint16_t bck_div; // The BCK devider, Fbck = Fmclk / bck_div +} i2s_ll_clk_cal_t; /** - * @brief Reset rx fifo + * @brief I2S module general init, enable I2S clock. * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_enable_clock(i2s_dev_t *hw) { - hw->conf.rx_fifo_reset = 1; - hw->conf.rx_fifo_reset = 0; + if (hw->clkm_conf.clk_en == 0) { + hw->clkm_conf.clk_en = 1; + hw->conf2.val = 0; + } } /** - * @brief Reset tx fifo + * @brief I2S tx msb right enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable tx msb right + */ +static inline void i2s_ll_tx_enable_msb_right(i2s_dev_t *hw, bool enable) +{ + hw->conf.tx_msb_right = enable; +} + +/** + * @brief I2S rx msb right enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable rx msb right + */ +static inline void i2s_ll_rx_enable_msb_right(i2s_dev_t *hw, bool enable) +{ + hw->conf.rx_msb_right = enable; +} + +/** + * @brief I2S tx right channel first + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable send right channel first + */ +static inline void i2s_ll_tx_enable_right_first(i2s_dev_t *hw, bool enable) +{ + hw->conf.tx_right_first = enable; +} + +/** + * @brief I2S rx right channel first + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable receive right channel first + */ +static inline void i2s_ll_rx_enable_right_first(i2s_dev_t *hw, bool enable) +{ + hw->conf.rx_right_first = enable; +} + +/** + * @brief I2S tx fifo module force enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable tx fifo module + */ +static inline void i2s_ll_tx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable) +{ + hw->fifo_conf.tx_fifo_mod_force_en = enable; +} + +/** + * @brief I2S rx fifo module force enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable rx fifo module + */ +static inline void i2s_ll_rx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable) +{ + hw->fifo_conf.rx_fifo_mod_force_en = enable; +} +/** + * @brief Enable I2S TX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->conf.tx_slave_mod = slave_en; +} + +/** + * @brief Enable I2S RX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->conf.rx_slave_mod = slave_en; +} + +/** + * @brief Reset I2S TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset(i2s_dev_t *hw) +{ + hw->conf.tx_reset = 1; + hw->conf.tx_reset = 0; +} + +/** + * @brief Reset I2S RX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_reset(i2s_dev_t *hw) +{ + hw->conf.rx_reset = 1; + hw->conf.rx_reset = 0; +} + +/** + * @brief Reset I2S TX FIFO + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw) { hw->conf.tx_fifo_reset = 1; hw->conf.tx_fifo_reset = 0; } /** - * @brief Enable rx interrupt + * @brief Reset I2S RX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) { - hw->int_ena.in_suc_eof = 1; - hw->int_ena.in_dscr_err = 1; + hw->conf.rx_fifo_reset = 1; + hw->conf.rx_fifo_reset = 0; } /** - * @brief Disable rx interrupt + * @brief Set TX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock */ -static inline void i2s_ll_disable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->int_ena.in_suc_eof = 0; - hw->int_ena.in_dscr_err = 0; + //0: disable APLL clock, I2S module will using PLL_D2_CLK(160M) as source clock + //1: Enable APLL clock, I2S module will using APLL as source clock + hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0; } /** - * @brief Disable tx interrupt + * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock */ -static inline void i2s_ll_disable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->int_ena.out_eof = 0; - hw->int_ena.out_dscr_err = 0; + //0: disable APLL clock, I2S module will using PLL_D2_CLK(160M) as source clock + //1: Enable APLL clock, I2S module will using APLL as source clock + hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0; } /** - * @brief Enable tx interrupt + * @brief Configure I2S TX clock devider + * + * @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) +{ + hw->clkm_conf.clkm_div_num = set->mclk_div; + hw->clkm_conf.clkm_div_b = set->b; + hw->clkm_conf.clkm_div_a = set->a; + hw->sample_rate_conf.tx_bck_div_num = set->bck_div; +} + +/** + * @brief Configure I2S RX clock devider + * + * @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) +{ + hw->clkm_conf.clkm_div_num = set->mclk_div; + hw->clkm_conf.clkm_div_b = set->b; + hw->clkm_conf.clkm_div_a = set->a; + hw->sample_rate_conf.rx_bck_div_num = set->bck_div; +} + +/** + * @brief Enable TX interrupt * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 1; hw->int_ena.out_dscr_err = 1; } /** - * @brief Reset dma in + * @brief Disable TX interrupt * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) +static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) { - hw->lc_conf.in_rst = 1; - hw->lc_conf.in_rst = 0; + hw->int_ena.out_eof = 0; + hw->int_ena.out_dscr_err = 0; +} + +/** + * @brief Enable RX interrupt + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) +{ + hw->int_ena.in_suc_eof = 1; + hw->int_ena.in_dscr_err = 1; +} + +/** + * @brief Disable RX interrupt + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_disable_intr(i2s_dev_t *hw) +{ + hw->int_ena.in_suc_eof = 0; + hw->int_ena.in_dscr_err = 0; +} + +/** + * @brief Get I2S interrupt status + * + * @param hw Peripheral I2S hardware instance address. + * @return + * - module interrupt status + */ +static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw) +{ + return hw->int_st.val; +} + +/** + * @brief Clear I2S interrupt status + * + * @param hw Peripheral I2S hardware instance address. + * @param clr_mask Interrupt mask to clear interrupt status + */ +static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t clr_mask) +{ + hw->int_clr.val = clr_mask; } /** @@ -124,72 +318,85 @@ static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_out(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset_dma(i2s_dev_t *hw) { hw->lc_conf.out_rst = 1; hw->lc_conf.out_rst = 0; } /** - * @brief Reset tx + * @brief Reset dma in * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_dma(i2s_dev_t *hw) { - hw->conf.tx_reset = 1; - hw->conf.tx_reset = 0; + hw->lc_conf.in_rst = 1; + hw->lc_conf.in_rst = 0; } /** - * @brief Reset rx + * @brief Start TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx(i2s_dev_t *hw) -{ - hw->conf.rx_reset = 1; - hw->conf.rx_reset = 0; -} - -/** - * @brief Start out link - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_start_out_link(i2s_dev_t *hw) -{ - hw->out_link.start = 1; -} - -/** - * @brief Start tx - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_start_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_start(i2s_dev_t *hw) { hw->conf.tx_start = 1; } /** - * @brief Start in link + * @brief Start RX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_start(i2s_dev_t *hw) { + hw->conf.rx_start = 1; +} + +/** + * @brief Configure TX DMA descriptor address and start TX DMA + * + * @param hw Peripheral I2S hardware instance address. + * @param link_addr DMA descriptor link address. + */ +static inline void i2s_ll_tx_start_link(i2s_dev_t *hw, uint32_t link_addr) +{ + hw->out_link.addr = link_addr; + hw->out_link.start = 1; +} + +/** + * @brief Configure RX DMA descriptor address and start RX DMA + * + * @param hw Peripheral I2S hardware instance address. + * @param link_addr DMA descriptor link address. + */ +static inline void i2s_ll_rx_start_link(i2s_dev_t *hw, uint32_t link_addr) +{ + hw->in_link.addr = link_addr; hw->in_link.start = 1; } /** - * @brief Start rx + * @brief Stop TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop(i2s_dev_t *hw) { - hw->conf.rx_start = 1; + hw->conf.tx_start = 0; +} + +/** + * @brief Stop RX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_stop(i2s_dev_t *hw) +{ + hw->conf.rx_start = 0; } /** @@ -197,654 +404,376 @@ static inline void i2s_ll_start_rx(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_out_link(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop_link(i2s_dev_t *hw) { hw->out_link.stop = 1; } -/** - * @brief Stop tx - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_stop_tx(i2s_dev_t *hw) -{ - hw->conf.tx_start = 0; -} - /** * @brief Stop in link * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_stop_link(i2s_dev_t *hw) { hw->in_link.stop = 1; } /** - * @brief Stop rx + * @brief Get I2S out eof descriptor address * * @param hw Peripheral I2S hardware instance address. + * @param eof_addr Pointer to accept out eof des address */ -static inline void i2s_ll_stop_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr) { - hw->conf.rx_start = 0; + *eof_addr = hw->out_eof_des_addr; } /** - * @brief Enable dma + * @brief Get I2S in eof descriptor address * * @param hw Peripheral I2S hardware instance address. + * @param eof_addr Pointer to accept in eof des address */ -static inline void i2s_ll_enable_dma(i2s_dev_t *hw) +static inline void i2s_ll_rx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr) { - //Enable and configure DMA - typeof(hw->lc_conf) lc_conf; - lc_conf.val = 0; - lc_conf.out_eof_mode = 1; - hw->lc_conf.val = lc_conf.val; + *eof_addr = hw->in_eof_des_addr; } /** - * @brief Get I2S interrupt status + * @brief Configure the received length to trigger in_suc_eof interrupt * * @param hw Peripheral I2S hardware instance address. - * @param val value to get interrupt status + * @param eof_num the byte length to trigger in_suc_eof interrupt */ -static inline void i2s_ll_get_intr_status(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->int_st.val; -} - -/** - * @brief Clear I2S interrupt status - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to clear interrupt status - */ -static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t val) -{ - hw->int_clr.val = val; -} - -/** - * @brief Get I2S out eof des address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get out eof des address - */ -static inline void i2s_ll_get_out_eof_des_addr(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->out_eof_des_addr; -} - -/** - * @brief Get I2S in eof des address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get in eof des address - */ -static inline void i2s_ll_get_in_eof_des_addr(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->in_eof_des_addr; -} - -/** - * @brief Get I2S tx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx fifo mode - */ -static inline void i2s_ll_get_tx_fifo_mod(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->fifo_conf.tx_fifo_mod; -} - -/** - * @brief Set I2S tx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mode - */ -static inline void i2s_ll_set_tx_fifo_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->fifo_conf.tx_fifo_mod = val; -} - -/** - * @brief Get I2S rx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx fifo mode - */ -static inline void i2s_ll_get_rx_fifo_mod(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->fifo_conf.rx_fifo_mod; -} - -/** - * @brief Set I2S rx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mode - */ -static inline void i2s_ll_set_rx_fifo_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->fifo_conf.rx_fifo_mod = val; -} - -/** - * @brief Set I2S tx chan mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx chan mode - */ -static inline void i2s_ll_set_tx_chan_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf_chan.tx_chan_mod = val; -} - -/** - * @brief Set I2S rx chan mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx chan mode - */ -static inline void i2s_ll_set_rx_chan_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf_chan.rx_chan_mod = val; -} - -/** - * @brief Set I2S out link address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set out link address - */ -static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val) -{ - hw->out_link.addr = val; -} - -/** - * @brief Set I2S in link address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set in link address - */ -static inline void i2s_ll_set_in_link_addr(i2s_dev_t *hw, uint32_t val) -{ - hw->in_link.addr = val; -} - -/** - * @brief Set I2S rx eof num - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx eof num - */ -static inline void i2s_ll_set_rx_eof_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, int eof_num) { // On ESP32, the eof_num count in words. - hw->rx_eof_num = val / 4; + hw->rx_eof_num = eof_num / 4; } /** - * @brief Set I2S clkm div num + * @brief Congfigure TX chan bit and audio data bit, on ESP32, sample_bit should equals to data_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div num + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_set_clkm_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->clkm_conf.clkm_div_num = val; + hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->sample_rate_conf.tx_bits_mod = data_bit; } /** - * @brief Set I2S clkm div b + * @brief Congfigure RX chan bit and audio data bit, on ESP32, sample_bit should equals to data_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div b + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_set_clkm_div_b(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->clkm_conf.clkm_div_b = val; + hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->sample_rate_conf.rx_bits_mod = data_bit; } /** - * @brief Set I2S clkm div a + * @brief Enable I2S DMA * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div a + * @param ena Set true to enable DMA */ -static inline void i2s_ll_set_clkm_div_a(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_enable_dma(i2s_dev_t *hw, bool ena) { - hw->clkm_conf.clkm_div_a = val; + hw->fifo_conf.dscr_en = ena; } /** - * @brief Set I2S tx bck div num + * @brief Configure TX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bck div num + * @param width WS width in BCK cycle */ -static inline void i2s_ll_set_tx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width) { - hw->sample_rate_conf.tx_bck_div_num = val; + hw->conf.tx_short_sync = width == 1 ? 1 : 0; } /** - * @brief Set I2S rx bck div num + * @brief Configure RX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bck div num + * @param width WS width in BCK cycle */ -static inline void i2s_ll_set_rx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width) { - hw->sample_rate_conf.rx_bck_div_num = val; + hw->conf.rx_short_sync = width == 1 ? 1 : 0; } /** - * @brief Set I2S clk sel + * @brief Enable TX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clk sel + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_clk_sel(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - hw->clkm_conf.clka_en = (val == 1) ? 1 : 0; + hw->conf.tx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S tx bits mod + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bits mod + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_tx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - hw->sample_rate_conf.tx_bits_mod = val; + hw->conf.rx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S rx bits mod + * @brief Enable TX mono mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bits mod + * @param mono_ena Set true to enable mono mde. */ -static inline void i2s_ll_set_rx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { - hw->sample_rate_conf.rx_bits_mod = val; + int data_bit = hw->sample_rate_conf.tx_bits_mod; + hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->conf_chan.tx_chan_mod = mono_ena; } /** - * @brief Set I2S dscr en + * @brief Enable RX mono mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set dscr en + * @param mono_ena Set true to enable mono mde. */ -static inline void i2s_ll_set_dscr_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { - hw->fifo_conf.dscr_en = val; + int data_bit = hw->sample_rate_conf.rx_bits_mod; + hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->conf_chan.rx_chan_mod = mono_ena; } /** - * @brief Set I2S lcd en + * @brief Enable I2S loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set lcd en + * @param loopback_en Set true to enable loopback mode. */ -static inline void i2s_ll_set_lcd_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool loopback_en) { - hw->conf2.lcd_en = val; + hw->conf.sig_loopback = loopback_en; +} + + + +/******************************I2S PDM Configurations*************************************/ +/** + * @brief Configure RX PDM downsample + * + * @param hw Peripheral I2S hardware instance address. + * @param dsr PDM downsample configuration paramater + */ +static inline void i2s_ll_rx_set_pdm_dsr(i2s_dev_t *hw, i2s_pdm_dsr_t dsr) +{ + hw->pdm_conf.rx_sinc_dsr_16_en = dsr; } /** - * @brief Set I2S camera en + * @brief Get RX PDM downsample configuration * * @param hw Peripheral I2S hardware instance address. - * @param val value to set camera en + * @param dsr Pointer to accept PDM downsample configuration */ -static inline void i2s_ll_set_camera_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_get_pdm_dsr(i2s_dev_t *hw, i2s_pdm_dsr_t *dsr) { - hw->conf2.camera_en = val; + *dsr = hw->pdm_conf.rx_sinc_dsr_16_en; } /** - * @brief Set I2S tx fifo mod force en + * @brief Enable I2S TX PDM mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mod force en + * @param pdm_ena Set true to enable TX PDM mode */ -static inline void i2s_ll_set_tx_fifo_mod_force_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_ena) { - hw->fifo_conf.tx_fifo_mod_force_en = val; + hw->pdm_conf.tx_pdm_en = pdm_ena; + hw->pdm_conf.pcm2pdm_conv_en = pdm_ena; } /** - * @brief Set I2S rx fifo mod force en + * @brief Enable I2S RX PDM mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mod force en + * @param pdm_ena Set true to enable RX PDM mode */ -static inline void i2s_ll_set_rx_fifo_mod_force_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_ena) { - hw->fifo_conf.rx_fifo_mod_force_en = val; + hw->pdm_conf.rx_pdm_en = pdm_ena; + hw->pdm_conf.pdm2pcm_conv_en = pdm_ena; } /** - * @brief Set I2S tx right first + * @brief Set I2S TX PDM prescale * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx right first + * @param prescale I2S TX PDM prescale */ -static inline void i2s_ll_set_tx_right_first(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_prescale(i2s_dev_t *hw, bool prescale) { - hw->conf.tx_right_first = val; + hw->pdm_conf.tx_prescale = prescale; } /** - * @brief Set I2S rx right first + * @brief Set I2S TX PDM high pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx right first + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_rx_right_first(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - hw->conf.rx_right_first = val; + hw->pdm_conf.tx_hp_in_shift = sig_scale; } /** - * @brief Set I2S tx slave mod + * @brief Set I2S TX PDM low pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx slave mod + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_tx_slave_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_lp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - hw->conf.tx_slave_mod = val; + hw->pdm_conf.tx_lp_in_shift = sig_scale; } /** - * @brief Set I2S rx slave mod + * @brief Set I2S TX PDM sinc filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx slave mod + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_rx_slave_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sinc_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - hw->conf.rx_slave_mod = val; + hw->pdm_conf.tx_sinc_in_shift = sig_scale; } /** - * @brief Get I2S tx msb right + * @brief Set I2S TX PDM sigma-delta filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx msb right + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_get_tx_msb_right(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_pdm_sd_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - *val = hw->conf.tx_msb_right; + hw->pdm_conf.tx_sigmadelta_in_shift = sig_scale; } /** - * @brief Get I2S rx msb right + * @brief Configure I2S TX PDM sample rate + * Fpdm = 64*Fpcm*fp/fs * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx msb right + * @param fp The fp value of TX PDM filter module group0. + * @param fs The fs value of TX PDM filter module group0. */ -static inline void i2s_ll_get_rx_msb_right(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_pdm_fpfs(i2s_dev_t *hw, uint32_t fp, uint32_t fs) { - *val = hw->conf.rx_msb_right; + hw->pdm_freq_conf.tx_pdm_fp = fp; + hw->pdm_freq_conf.tx_pdm_fs = fs; + hw->pdm_conf.tx_sinc_osr2 = fp / fs; } /** - * @brief Set I2S tx msb right + * @brief Get I2S TX PDM fp configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb right + * @return + * - fp configuration paramater */ -static inline void i2s_ll_set_tx_msb_right(i2s_dev_t *hw, uint32_t val) +static inline uint32_t i2s_ll_tx_get_pdm_fp(i2s_dev_t *hw) { - hw->conf.tx_msb_right = val; + return hw->pdm_freq_conf.tx_pdm_fp; } /** - * @brief Set I2S rx msb right + * @brief Get I2S TX PDM fs configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb right + * @return + * - fs configuration paramater */ -static inline void i2s_ll_set_rx_msb_right(i2s_dev_t *hw, uint32_t val) +static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw) { - hw->conf.rx_msb_right = val; + return hw->pdm_freq_conf.tx_pdm_fs; +} + + + + +/****************************I2S ADC/DAC Configurations***********************************/ +/** + * @brief Enable I2S LCD mode + * @note Have to enable LCD mode to use build in ADC/DAC + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable LCD mode. + */ +static inline void i2s_ll_enable_lcd(i2s_dev_t *hw, bool enable) +{ + hw->conf2.lcd_en = enable; } /** - * @brief Set I2S tx mono + * @brief Enable I2S camera mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx mono + * @param enable Set true to enable camera mode. */ -static inline void i2s_ll_set_tx_mono(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_enable_camera(i2s_dev_t *hw, bool enable) { - hw->conf.tx_mono = val; -} - -/** - * @brief Set I2S rx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx mono - */ -static inline void i2s_ll_set_rx_mono(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_mono = val; -} - -/** - * @brief Set I2S sig loopback - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set sig loopback - */ -static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.sig_loopback = val; -} - -/** - * @brief Set I2S TX to philip standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) -{ - hw->conf.tx_short_sync = 0; - hw->conf.tx_msb_shift = 1; -} - -/** - * @brief Set I2S RX to philip standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) -{ - hw->conf.rx_short_sync = 0; - hw->conf.rx_msb_shift = 1; -} - -/** - * @brief Set I2S TX to MSB Alignment Standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) -{ - hw->conf.tx_short_sync = 0; - hw->conf.tx_msb_shift = 0; -} - -/** - * @brief Set I2S RX to MSB Alignment Standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) -{ - hw->conf.rx_short_sync = 0; - hw->conf.rx_msb_shift = 0; -} - -/** - * @brief Set I2S TX to PCM short standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) -{ - hw->conf.tx_short_sync = 1; - hw->conf.tx_msb_shift = 0; -} - -/** - * @brief Set I2S RX to PCM short standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) -{ - hw->conf.rx_short_sync = 1; - hw->conf.rx_msb_shift = 0; -} - -/** - * @brief Set I2S TX to PCM long standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) -{ - hw->conf.tx_short_sync = 0; - hw->conf.tx_msb_shift = 0; -} - -/** - * @brief Set I2S RX to PCM long standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_pcm_long(i2s_dev_t *hw) -{ - hw->conf.rx_short_sync = 0; - hw->conf.rx_msb_shift = 0; + hw->conf2.camera_en = enable; } /** * @brief Enable I2S build in ADC mode * * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable build in ADC */ -static inline void i2s_ll_build_in_adc_ena(i2s_dev_t *hw) +static inline void i2s_ll_enable_builtin_adc(i2s_dev_t *hw, bool enable) { - hw->conf2.lcd_en = 1; + hw->conf2.lcd_en = enable; hw->conf2.camera_en = 0; + hw->conf.rx_right_first = 0; hw->conf.rx_msb_shift = 0; + hw->conf.rx_mono = 0; hw->conf.rx_short_sync = 0; + hw->fifo_conf.rx_fifo_mod = enable; + hw->conf_chan.rx_chan_mod = enable; } /** * @brief Enable I2S build in DAC mode * * @param hw Peripheral I2S hardware instance address. + * * @param enable Set true to enable build in DAC */ -static inline void i2s_ll_build_in_dac_ena(i2s_dev_t *hw) +static inline void i2s_ll_enable_builtin_dac(i2s_dev_t *hw, bool enable) { - hw->conf2.lcd_en = 1; + hw->conf2.lcd_en = enable; hw->conf2.camera_en = 0; - hw->conf.tx_right_first = 1; + hw->conf.tx_right_first = enable; hw->conf.tx_msb_shift = 0; hw->conf.tx_short_sync = 0; } - -/** - * @brief Enable I2S RX PDM mode - * - * @param hw Peripheral I2S hardware instance address. - * @param pdm_en Set true to enable rx PDM mode - */ -static inline void i2s_ll_set_rx_pdm_en(i2s_dev_t *hw, bool pdm_en) -{ - hw->pdm_conf.rx_pdm_en = pdm_en; -} - -/** - * @brief Enable I2S tx pdm mode - * - * @param hw Peripheral I2S hardware instance address. - * @param pdm_en Set true to enable tx PDM mode - */ -static inline void i2s_ll_set_tx_pdm_en(i2s_dev_t *hw, bool pdm_en) -{ - hw->pdm_conf.tx_pdm_en = pdm_en; -} - -/** - * @brief Configure I2S tx PDM filter module group0 - * - * @param hw Peripheral I2S hardware instance address. - * @param fp The fp value of TX PDM filter module group0. - * @param fs The fs value of TX PDM filter module group0. - */ -static inline void i2s_ll_tx_pdm_cfg(i2s_dev_t *hw, uint32_t fp, uint32_t fs) -{ - hw->pdm_freq_conf.tx_pdm_fp = fp; - hw->pdm_freq_conf.tx_pdm_fs = fs; - hw->pdm_conf.tx_sinc_osr2 = fp/fs; - hw->pdm_conf.pcm2pdm_conv_en = 1; - hw->pdm_conf.tx_pdm_en = 1; -} - -/** - * @brief Configure I2S rx PDM - * - * @param hw Peripheral I2S hardware instance address. - * @param dsr Down-sampling rate value of rx PDM - */ -static inline void i2s_ll_rx_pdm_cfg(i2s_dev_t *hw, uint32_t dsr) -{ - hw->pdm_conf.rx_sinc_dsr_16_en = dsr; - hw->pdm_conf.pdm2pcm_conv_en = 1; - hw->pdm_conf.rx_pdm_en = 1; -} - -/** - * @brief Get I2S tx PDM configuration - * - * @param hw Peripheral I2S hardware instance address. - * @param fp Pointer to store tx PDM fp configuration - * @param fs Pointer to store tx PDM fs configuration - */ -static inline void i2s_ll_get_tx_pdm(i2s_dev_t *hw, uint32_t *fp, uint32_t *fs) -{ - *fp = hw->pdm_freq_conf.tx_pdm_fp; - *fs = hw->pdm_freq_conf.tx_pdm_fs; -} - -/** - * @brief Get I2S rx PDM configuration - * - * @param hw Peripheral I2S hardware instance address. - * @param dsr Pointer to stoe the rx PDM down-sample rate configuration - */ -static inline void i2s_ll_get_rx_pdm(i2s_dev_t *hw, uint32_t *dsr) -{ - *dsr = hw->pdm_conf.rx_sinc_dsr_16_en; -} - #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index 4a6042cf12..c67586aaf0 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,18 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +// The LL layer for I2S register operations /******************************************************************************* * NOTICE * The hal is not public api, don't use in application code. - * See readme.md in soc/include/hal/readme.md + * See readme.md in hal/include/hal/readme.md ******************************************************************************/ -// The LL layer for ESP32-S3 I2S register operations - #pragma once - #include -#include #include "soc/i2s_periph.h" #include "hal/i2s_types.h" @@ -31,880 +28,747 @@ extern "C" { #endif -// Get I2S hardware instance with giving i2s num -#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL) +#define I2S_LL_GET_HW(num) (&I2S0) -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (0xFFFFFFFF) - -/** - * @brief Reset rx fifo - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_reset_rx_fifo(i2s_dev_t *hw) -{ - abort(); // TODO ESP32-C3 IDF-2098 - - -} - -/** - * @brief Reset tx fifo - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_reset_tx_fifo(i2s_dev_t *hw) -{ - abort(); // TODO ESP32-C3 IDF-2098 +#define I2S_LL_TDM_CH_MASK (0xffff) +#define I2S_LL_PDM_BCK_FACTOR (64) +#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ) +#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9) +#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) -} +/* I2S clock configuration structure */ +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 + uint16_t bck_div; // The BCK devider, Fbck = Fmclk / bck_div +} i2s_ll_clk_cal_t; /** - * @brief Enable rx interrupt + * @brief I2S module general init, enable I2S clock. * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->tx_clkm_conf.clk_en = 1; } /** - * @brief Disable rx interrupt + * @brief Enable I2S tx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_disable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->tx_clkm_conf.tx_clk_active = 1; } /** - * @brief Disable tx interrupt + * @brief Enable I2S rx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_disable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->rx_clkm_conf.rx_clk_active = 1; } /** - * @brief Enable tx interrupt + * @brief I2S mclk use tx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->rx_clkm_conf.mclk_sel = 0; } /** - * @brief Reset dma in + * @brief I2S mclk use rx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) +static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->rx_clkm_conf.mclk_sel = 1; } /** - * @brief Reset dma out + * @brief Enable I2S TX slave mode * * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode */ -static inline void i2s_ll_reset_dma_out(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->tx_conf.tx_slave_mod = slave_en; } /** - * @brief Reset tx + * @brief Enable I2S RX slave mode * * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode */ -static inline void i2s_ll_reset_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->rx_conf.rx_slave_mod = slave_en; } /** - * @brief Reset rx + * @brief Reset I2S TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - - + hw->tx_conf.tx_reset = 1; + hw->tx_conf.tx_reset = 0; } /** - * @brief Start out link + * @brief Reset I2S RX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf.rx_reset = 1; + hw->rx_conf.rx_reset = 0; } /** - * @brief Start tx + * @brief Reset I2S TX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.tx_fifo_reset = 1; + hw->tx_conf.tx_fifo_reset = 0; } /** - * @brief Start in link + * @brief Reset I2S RX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf.rx_fifo_reset = 1; + hw->rx_conf.rx_fifo_reset = 0; } /** - * @brief Start rx + * @brief Set TX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock. */ -static inline void i2s_ll_start_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_clkm_conf.tx_clk_sel = 2; } /** - * @brief Stop out link + * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock, ESP32-C3 only support `I2S_CLK_D2CLK` */ -static inline void i2s_ll_stop_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_clkm_conf.rx_clk_sel = 2; } /** - * @brief Stop tx + * @brief Configure I2S TX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_stop_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { - abort(); // TODO ESP32-C3 IDF-2098 - + if (set->a == 0 || set->b == 0) { + hw->tx_clkm_div_conf.tx_clkm_div_x = 0; + hw->tx_clkm_div_conf.tx_clkm_div_y = 0; + hw->tx_clkm_div_conf.tx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); + hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; + } else { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; + } + } + hw->tx_clkm_conf.tx_clkm_div_num = set->mclk_div; + hw->tx_conf1.tx_bck_div_num = set->bck_div - 1; } /** - * @brief Stop in link + * @brief Configure I2S RX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_stop_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { - abort(); // TODO ESP32-C3 IDF-2098 - + if (set->a == 0 || set->b == 0) { + hw->rx_clkm_div_conf.rx_clkm_div_x = 0; + hw->rx_clkm_div_conf.rx_clkm_div_y = 0; + hw->rx_clkm_div_conf.rx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); + hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; + } else { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; + } + } + hw->rx_clkm_conf.rx_clkm_div_num = set->mclk_div; + hw->rx_conf1.rx_bck_div_num = set->bck_div - 1; } /** - * @brief Stop rx + * @brief Start I2S TX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_start(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.tx_update = 0; + hw->tx_conf.tx_update = 1; + hw->tx_conf.tx_start = 1; } /** - * @brief Enable dma + * @brief Start I2S RX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_dma(i2s_dev_t *hw) +static inline void i2s_ll_rx_start(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - // //Enable and configure DMA - // typeof(hw->lc_conf) lc_conf; - // lc_conf.val = 0; - // lc_conf.out_eof_mode = 1; - + hw->rx_conf.rx_update = 0; + hw->rx_conf.rx_update = 1; + hw->rx_conf.rx_start = 1; } /** - * @brief Get I2S interrupt status + * @brief Stop I2S TX * * @param hw Peripheral I2S hardware instance address. - * @param val value to get interrupt status */ -static inline void i2s_ll_get_intr_status(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_stop(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->int_st.val; + hw->tx_conf.tx_start = 0; } /** - * @brief Clear I2S interrupt status + * @brief Stop I2S RX * * @param hw Peripheral I2S hardware instance address. - * @param val value to clear interrupt status */ -static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_stop(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf.rx_start = 0; } /** - * @brief Get I2S out eof des address + * @brief Configure TX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to get out eof des address + * @param width WS width in BCK cycle */ -static inline void i2s_ll_get_out_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->out_eof_des_addr; + hw->tx_conf1.tx_tdm_ws_width = width - 1; } /** - * @brief Get I2S in eof des address + * @brief Configure RX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to get in eof des address + * @param width WS width in BCK cycle */ -static inline void i2s_ll_get_in_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->in_eof_des_addr; + hw->rx_conf1.rx_tdm_ws_width = width - 1; } /** - * @brief Get I2S tx fifo mode + * @brief Configure the received length to trigger in_suc_eof interrupt * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx fifo mode + * @param eof_num the byte length to trigger in_suc_eof interrupt */ -static inline void i2s_ll_get_tx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, int eof_num) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->fifo_conf.tx_fifo_mod; + hw->rx_eof_num.rx_eof_num = eof_num; } /** - * @brief Set I2S tx fifo mode + * @brief Congfigure TX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mode + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_set_tx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf1.tx_bits_mod = data_bit - 1; + hw->tx_conf1.tx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Get I2S rx fifo mode + * @brief Congfigure RX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx fifo mode + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_get_rx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->fifo_conf.rx_fifo_mod; + hw->rx_conf1.rx_bits_mod = data_bit - 1; + hw->rx_conf1.rx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Set I2S rx fifo mode + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mode + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_set_rx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf1.tx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Set I2S tx chan mode + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx chan mode + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_set_tx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf1.rx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Set I2S rx chan mode + * @brief Enable TX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx chan mode + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_rx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf1.tx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S out link address + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set out link address + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf1.rx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S in link address + * @brief Configure TX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to set in link address + * @param total_num Total chan number */ -static inline void i2s_ll_set_in_link_addr(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_chan_num(i2s_dev_t *hw, int total_num) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Set I2S rx eof num + * @brief Configure RX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx eof num + * @param total_num Total chan number */ -static inline void i2s_ll_set_rx_eof_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_chan_num(i2s_dev_t *hw, int total_num) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Get I2S tx pdm fp + * @brief Set the bimap of the active TX chan, only the active chan can launch audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fp + * @param chan_mask mask of tx active chan */ -static inline void i2s_ll_get_tx_pdm_fp(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->pdm_freq_conf.tx_pdm_fp; + typeof(hw->tx_tdm_ctrl) tdm_ctrl_reg = hw->tx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->tx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Get I2S tx pdm fs + * @brief Set the bimap of the active RX chan, only the active chan can receive audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fs + * @param chan_mask mask of rx active chan */ -static inline void i2s_ll_get_tx_pdm_fs(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->pdm_freq_conf.tx_pdm_fs; + typeof(hw->rx_tdm_ctrl) tdm_ctrl_reg = hw->rx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->rx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Set I2S tx pdm fp + * @brief Set TX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fp + * @param ws_pol_level pin level of WS(output) when receiving left channel data */ -static inline void i2s_ll_set_tx_pdm_fp(i2s_dev_t *hw, uint32_t val) +static inline void i2s_tx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.tx_ws_idle_pol = ws_pol_level; } /** - * @brief Set I2S tx pdm fs + * @brief Set RX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fs + * @param ws_pol_level pin level of WS(input) when receiving left channel data */ -static inline void i2s_ll_set_tx_pdm_fs(i2s_dev_t *hw, uint32_t val) +static inline void i2s_rx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } /** - * @brief Get I2S rx sinc dsr 16 en + * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx sinc dsr 16 en + * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_get_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool *val) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->pdm_conf.rx_sinc_dsr_16_en; + hw->tx_conf.tx_pdm_en = pdm_enable; + hw->tx_conf.tx_tdm_en = !pdm_enable; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; } /** - * @brief Set I2S clkm div num + * @brief Set I2S TX PDM prescale * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div num + * @param prescale I2S TX PDM prescale */ -static inline void i2s_ll_set_clkm_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_prescale(i2s_dev_t *hw, bool prescale) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_prescale = prescale; } /** - * @brief Set I2S clkm div b + * @brief Set I2S TX PDM high pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div b + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_clkm_div_b(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_hp_in_shift = sig_scale; } /** - * @brief Set I2S clkm div a + * @brief Set I2S TX PDM low pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div a + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_clkm_div_a(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_lp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_lp_in_shift = sig_scale; } /** - * @brief Set I2S tx bck div num + * @brief Set I2S TX PDM sinc filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bck div num + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_tx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sinc_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sinc_in_shift = sig_scale; } /** - * @brief Set I2S rx bck div num + * @brief Set I2S TX PDM sigma-delta filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bck div num + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_rx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_in_shift = sig_scale; } /** - * @brief Set I2S clk sel + * @brief Set I2S TX PDM high pass filter param0 * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clk sel + * @param param The fourth parameter of PDM TX IIR_HP filter stage 1 is (504 + I2S_TX_IIR_HP_MULT12_0[2:0]) */ -static inline void i2s_ll_set_clk_sel(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param0(i2s_dev_t *hw, uint32_t param) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_0 = param; } /** - * @brief Set I2S tx bits mod + * @brief Set I2S TX PDM high pass filter param5 * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bits mod + * @param param The fourth parameter of PDM TX IIR_HP filter stage 2 is (504 + I2S_TX_IIR_HP_MULT12_5[2:0]) */ -static inline void i2s_ll_set_tx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param5(i2s_dev_t *hw, uint32_t param) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_5 = param; } /** - * @brief Set I2S rx bits mod + * @brief Enable I2S TX PDM high pass filter * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bits mod + * @param enable Set true to enable I2S TX PDM high pass filter, set false to bypass it */ -static inline void i2s_ll_set_rx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_hp_bypass = !enable; } /** - * @brief Set I2S rx sinc dsr 16 en + * @brief Enable I2S TX PDM sigma-delta codec * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx sinc dsr 16 en + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = enable; + hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = enable; } /** - * @brief Set I2S dscr en + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set dscr en + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_dscr_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_pdm_sd_dither(i2s_dev_t *hw, uint32_t dither) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_dither = dither; } /** - * @brief Set I2S lcd en + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set lcd en + * @param dither2 I2S TX PDM sigmadelta dither2 value */ -static inline void i2s_ll_set_lcd_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_pdm_sd_dither2(i2s_dev_t *hw, uint32_t dither2) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_dither2 = dither2; } /** - * @brief Set I2S camera en + * @brief Configure I2S TX PDM sample rate + * Fpdm = 64*Fpcm*fp/fs * * @param hw Peripheral I2S hardware instance address. - * @param val value to set camera en + * @param fp The fp value of TX PDM filter module group0. + * @param fs The fs value of TX PDM filter module group0. */ -static inline void i2s_ll_set_camera_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_pdm_fpfs(i2s_dev_t *hw, uint32_t fp, uint32_t fs) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_pdm_fp = fp; + hw->tx_pcm2pdm_conf1.tx_pdm_fs = fs; + hw->tx_pcm2pdm_conf.tx_pdm_sinc_osr2 = fp / fs; } /** - * @brief Set I2S pcm2pdm conv en + * @brief Get I2S TX PDM fp configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set pcm2pdm conv en + * @return + * - fp configuration paramater */ -static inline void i2s_ll_set_pcm2pdm_conv_en(i2s_dev_t *hw, bool val) +static inline uint32_t i2s_ll_tx_get_pdm_fp(i2s_dev_t *hw) { - abort(); // TODO ESP32-C3 IDF-2098 - + return hw->tx_pcm2pdm_conf1.tx_pdm_fp; } /** - * @brief Set I2S TX to MSB Alignment Standard + * @brief Get I2S TX PDM fs configuration paramater * * @param hw Peripheral I2S hardware instance address. + * @return + * - fs configuration paramater */ -static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) -{ -} - - -static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) +static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw) { + return hw->tx_pcm2pdm_conf1.tx_pdm_fs; } /** - * @brief Set I2S TX to PCM long standard + * @brief Enable RX PDM mode. + * @note ESP32-C3 doesn't support pdm in rx mode, disable anyway * * @param hw Peripheral I2S hardware instance address. + * @param pdm_enable Set true to RX enable PDM mode (ignored) */ -static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { + hw->rx_conf.rx_pdm_en = 0; + hw->rx_conf.rx_tdm_en = 1; } /** - * @brief Set I2S RX to PCM long standard + * @brief Configura TX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_rx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->tx_conf.tx_pcm_conf = pcm_cfg; + hw->tx_conf.tx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S RX to PCM short standard + * @brief Configure RX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->rx_conf.rx_pcm_conf = pcm_cfg; + hw->rx_conf.rx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S RX to philip standard + * @brief Enable TX audio data left alignment * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->tx_conf.tx_left_align = ena; } /** - * @brief Set I2S TX to PCM short standard + * @brief Enable RX audio data left alignment * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_left_align = ena; } /** - * @brief Set I2S TX to philip standard + * @brief Enable TX big endian mode * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_big_endian(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_big_endian = ena; } /** - * @brief Set I2S pdm2pcm conv en + * @brief Enable RX big endian mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set pdm2pcm conv en + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_pdm2pcm_conv_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_enable_big_endian(i2s_dev_t *hw, bool ena) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.tx_big_endian = ena; } /** - * @brief Set I2S rx pdm en + * @brief Configure TX bit order * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx pdm en + * @param lsb_order_ena Set true to enable LSB bit order */ -static inline void i2s_ll_set_rx_pdm_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.tx_bit_order = lsb_order_ena; } /** - * @brief Set I2S tx pdm en + * @brief Configure RX bit order * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm en + * @param lsb_order_ena Set true to enable LSB bit order */ -static inline void i2s_ll_set_tx_pdm_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->rx_conf.rx_bit_order = lsb_order_ena; } /** - * @brief Set I2S tx fifo mod force en + * @brief Configure TX skip mask enable * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mod force en + * @param skip_mask_ena Set true to skip inactive channels. */ -static inline void i2s_ll_set_tx_fifo_mod_force_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_skip_mask(i2s_dev_t *hw, bool skip_mask_ena) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_tdm_ctrl.tx_tdm_skip_msk_en = skip_mask_ena; } -/** - * @brief Set I2S rx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mod force en - */ -static inline void i2s_ll_set_rx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} /** - * @brief Set I2S tx right first + * @brief Configure single data * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx right first + * @param data Single data to be set */ -static inline void i2s_ll_set_tx_right_first(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) { - abort(); // TODO ESP32-C3 IDF-2098 - + hw->conf_single_data = data; } /** - * @brief Set I2S rx right first + * @brief Enable loopback mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx right first + * @param ena Set true to enable loopback mode. */ -static inline void i2s_ll_set_rx_right_first(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) { - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S tx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx slave mod - */ -static inline void i2s_ll_set_tx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S rx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx slave mod - */ -static inline void i2s_ll_set_rx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Get I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx msb right - */ -static inline void i2s_ll_get_tx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->conf.tx_msb_right; -} - -/** - * @brief Get I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx msb right - */ -static inline void i2s_ll_get_rx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - // *val = hw->conf.rx_msb_right; -} - -/** - * @brief Set I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb right - */ -static inline void i2s_ll_set_tx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb right - */ -static inline void i2s_ll_set_rx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S tx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx mono - */ -static inline void i2s_ll_set_tx_mono(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S rx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx mono - */ -static inline void i2s_ll_set_rx_mono(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S tx sinc osr2 - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx sinc osr2 - */ -static inline void i2s_ll_set_tx_sinc_osr2(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - -} - -/** - * @brief Set I2S sig loopback - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set sig loopback - */ -static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-C3 IDF-2098 - + hw->tx_conf.sig_loopback = ena; } #ifdef __cplusplus diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index c54d7de83a..a96c03c7c2 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -18,12 +18,10 @@ * See readme.md in soc/include/hal/readme.md ******************************************************************************/ -// The LL layer for ESP32-S3 I2S register operations +// The LL layer for ESP32-H2 I2S register operations #pragma once - #include -#include #include "soc/i2s_periph.h" #include "hal/i2s_types.h" @@ -31,676 +29,746 @@ extern "C" { #endif -// Get I2S hardware instance with giving i2s num -#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL) +#define I2S_LL_GET_HW(num) (&I2S0) -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (0xFFFFFFFF) +#define I2S_LL_TDM_CH_MASK (0xffff) +#define I2S_LL_PDM_BCK_FACTOR (64) +#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ) + +#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9) +#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) + +/* I2S clock configuration structure */ +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 + uint16_t bck_div; // The BCK devider, Fbck = Fmclk / bck_div +} i2s_ll_clk_cal_t; /** - * @brief Reset rx fifo + * @brief I2S module general init, enable I2S clock. * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->tx_clkm_conf.clk_en = 1; } /** - * @brief Reset tx fifo + * @brief Enable I2S tx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->tx_clkm_conf.tx_clk_active = 1; } /** - * @brief Enable rx interrupt + * @brief Enable I2S rx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_clock(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->rx_clkm_conf.rx_clk_active = 1; } /** - * @brief Disable rx interrupt + * @brief I2S mclk use tx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_disable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->rx_clkm_conf.mclk_sel = 0; } /** - * @brief Disable tx interrupt + * @brief I2S mclk use rx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_disable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->rx_clkm_conf.mclk_sel = 1; } /** - * @brief Enable tx interrupt + * @brief Enable I2S TX slave mode * * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode */ -static inline void i2s_ll_enable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->tx_conf.tx_slave_mod = slave_en; } /** - * @brief Reset dma in + * @brief Enable I2S RX slave mode * * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode */ -static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->rx_conf.rx_slave_mod = slave_en; } /** - * @brief Reset dma out + * @brief Reset I2S TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_out(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->tx_conf.tx_reset = 1; + hw->tx_conf.tx_reset = 0; } /** - * @brief Reset tx + * @brief Reset I2S RX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->rx_conf.rx_reset = 1; + hw->rx_conf.rx_reset = 0; } /** - * @brief Reset rx + * @brief Reset I2S TX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - - + hw->tx_conf.tx_fifo_reset = 1; + hw->tx_conf.tx_fifo_reset = 0; } /** - * @brief Start out link + * @brief Reset I2S RX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_conf.rx_fifo_reset = 1; + hw->rx_conf.rx_fifo_reset = 0; } /** - * @brief Start tx + * @brief Set TX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock. */ -static inline void i2s_ll_start_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_clkm_conf.tx_clk_sel = 2; } /** - * @brief Start in link + * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock, ESP32-H2 only support `I2S_CLK_D2CLK` */ -static inline void i2s_ll_start_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_clkm_conf.rx_clk_sel = 2; } /** - * @brief Start rx + * @brief Configure I2S TX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_start_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { - abort(); // TODO ESP32-H2 IDF-2098 - + if (set->a == 0 || set->b == 0) { + hw->tx_clkm_div_conf.tx_clkm_div_x = 0; + hw->tx_clkm_div_conf.tx_clkm_div_y = 0; + hw->tx_clkm_div_conf.tx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); + hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; + } else { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; + } + } + hw->tx_clkm_conf.tx_clkm_div_num = set->mclk_div; + hw->tx_conf1.tx_bck_div_num = set->bck_div - 1; } /** - * @brief Stop out link + * @brief Configure I2S RX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_stop_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { - abort(); // TODO ESP32-H2 IDF-2098 - + if (set->a == 0 || set->b == 0) { + hw->rx_clkm_div_conf.rx_clkm_div_x = 0; + hw->rx_clkm_div_conf.rx_clkm_div_y = 0; + hw->rx_clkm_div_conf.rx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); + hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; + } else { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; + } + } + hw->rx_clkm_conf.rx_clkm_div_num = set->mclk_div; + hw->rx_conf1.rx_bck_div_num = set->bck_div - 1; } /** - * @brief Stop tx + * @brief Start I2S TX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_start(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_conf.tx_update = 0; + hw->tx_conf.tx_update = 1; + hw->tx_conf.tx_start = 1; } /** - * @brief Stop in link + * @brief Start I2S RX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_start(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_conf.rx_update = 0; + hw->rx_conf.rx_update = 1; + hw->rx_conf.rx_start = 1; } /** - * @brief Stop rx + * @brief Stop I2S TX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_conf.tx_start = 0; } /** - * @brief Enable dma + * @brief Stop I2S RX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_dma(i2s_dev_t *hw) +static inline void i2s_ll_rx_stop(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - // //Enable and configure DMA - // typeof(hw->lc_conf) lc_conf; - // lc_conf.val = 0; - // lc_conf.out_eof_mode = 1; - + hw->rx_conf.rx_start = 0; } /** - * @brief Get I2S interrupt status + * @brief Configure TX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to get interrupt status + * @param width WS width in BCK cycle */ -static inline void i2s_ll_get_intr_status(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->int_st.val; + hw->tx_conf1.tx_tdm_ws_width = width - 1; } /** - * @brief Clear I2S interrupt status + * @brief Configure RX WS signal width * * @param hw Peripheral I2S hardware instance address. - * @param val value to clear interrupt status + * @param width WS width in BCK cycle */ -static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_conf1.rx_tdm_ws_width = width - 1; } /** - * @brief Get I2S out eof des address + * @brief Configure the received length to trigger in_suc_eof interrupt * * @param hw Peripheral I2S hardware instance address. - * @param val value to get out eof des address + * @param eof_num the byte length to trigger in_suc_eof interrupt */ -static inline void i2s_ll_get_out_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, int eof_num) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->out_eof_des_addr; + hw->rx_eof_num.rx_eof_num = eof_num; } /** - * @brief Get I2S in eof des address + * @brief Congfigure TX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to get in eof des address + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_get_in_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->in_eof_des_addr; + hw->tx_conf1.tx_bits_mod = data_bit - 1; + hw->tx_conf1.tx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Get I2S tx fifo mode + * @brief Congfigure RX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx fifo mode + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_get_tx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->fifo_conf.tx_fifo_mod; + hw->rx_conf1.rx_bits_mod = data_bit - 1; + hw->rx_conf1.rx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Set I2S tx fifo mode + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mode + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_set_tx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_conf1.tx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Get I2S rx fifo mode + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx fifo mode + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_get_rx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->fifo_conf.rx_fifo_mod; + hw->rx_conf1.rx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Set I2S rx fifo mode + * @brief Enable TX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mode + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_rx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_conf1.tx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S tx chan mode + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx chan mode + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_set_tx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_conf1.rx_msb_shift = msb_shift_enable; } /** - * @brief Set I2S rx chan mode + * @brief Configure TX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx chan mode + * @param total_num Total chan number */ -static inline void i2s_ll_set_rx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_chan_num(i2s_dev_t *hw, int total_num) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Set I2S out link address + * @brief Configure RX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to set out link address + * @param total_num Total chan number */ -static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_chan_num(i2s_dev_t *hw, int total_num) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Set I2S in link address + * @brief Set the bimap of the active TX chan, only the active chan can launch audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set in link address + * @param chan_mask mask of tx active chan */ -static inline void i2s_ll_set_in_link_addr(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { - abort(); // TODO ESP32-H2 IDF-2098 - + typeof(hw->tx_tdm_ctrl) tdm_ctrl_reg = hw->tx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->tx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Set I2S rx eof num + * @brief Set the bimap of the active RX chan, only the active chan can receive audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx eof num + * @param chan_mask mask of rx active chan */ -static inline void i2s_ll_set_rx_eof_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { - abort(); // TODO ESP32-H2 IDF-2098 - + typeof(hw->rx_tdm_ctrl) tdm_ctrl_reg = hw->rx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->rx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Get I2S tx pdm fp + * @brief Set TX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fp + * @param ws_pol_level pin level of WS(output) when receiving left channel data */ -static inline void i2s_ll_get_tx_pdm_fp(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_tx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->pdm_freq_conf.tx_pdm_fp; + hw->tx_conf.tx_ws_idle_pol = ws_pol_level; } /** - * @brief Get I2S tx pdm fs + * @brief Set RX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fs + * @param ws_pol_level pin level of WS(input) when receiving left channel data */ -static inline void i2s_ll_get_tx_pdm_fs(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_rx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->pdm_freq_conf.tx_pdm_fs; + hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } /** - * @brief Set I2S tx pdm fp + * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fp + * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_set_tx_pdm_fp(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_conf.tx_pdm_en = pdm_enable; + hw->tx_conf.tx_tdm_en = !pdm_enable; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; } /** - * @brief Set I2S tx pdm fs + * @brief Set I2S TX PDM prescale * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fs + * @param prescale I2S TX PDM prescale */ -static inline void i2s_ll_set_tx_pdm_fs(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_prescale(i2s_dev_t *hw, bool prescale) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_prescale = prescale; } /** - * @brief Get I2S rx sinc dsr 16 en + * @brief Set I2S TX PDM high pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx sinc dsr 16 en + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_get_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool *val) +static inline void i2s_ll_tx_set_pdm_hp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->pdm_conf.rx_sinc_dsr_16_en; + hw->tx_pcm2pdm_conf.tx_pdm_hp_in_shift = sig_scale; } /** - * @brief Set I2S clkm div num + * @brief Set I2S TX PDM low pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div num + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_clkm_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_lp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_lp_in_shift = sig_scale; } /** - * @brief Set I2S clkm div b + * @brief Set I2S TX PDM sinc filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div b + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_clkm_div_b(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sinc_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sinc_in_shift = sig_scale; } /** - * @brief Set I2S clkm div a + * @brief Set I2S TX PDM sigma-delta filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div a + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_clkm_div_a(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_in_shift = sig_scale; } /** - * @brief Set I2S tx bck div num + * @brief Set I2S TX PDM high pass filter param0 * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bck div num + * @param param The fourth parameter of PDM TX IIR_HP filter stage 1 is (504 + I2S_TX_IIR_HP_MULT12_0[2:0]) */ -static inline void i2s_ll_set_tx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param0(i2s_dev_t *hw, uint32_t param) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_0 = param; } /** - * @brief Set I2S rx bck div num + * @brief Set I2S TX PDM high pass filter param5 * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bck div num + * @param param The fourth parameter of PDM TX IIR_HP filter stage 2 is (504 + I2S_TX_IIR_HP_MULT12_5[2:0]) */ -static inline void i2s_ll_set_rx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param5(i2s_dev_t *hw, uint32_t param) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_5 = param; } /** - * @brief Set I2S clk sel + * @brief Enable I2S TX PDM high pass filter * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clk sel + * @param enable Set true to enable I2S TX PDM high pass filter, set false to bypass it */ -static inline void i2s_ll_set_clk_sel(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_hp_bypass = !enable; } /** - * @brief Set I2S tx bits mod + * @brief Enable I2S TX PDM sigma-delta codec * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bits mod + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_tx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_dac_2out_en = enable; + hw->tx_pcm2pdm_conf.tx_pdm_dac_mode_en = enable; } /** - * @brief Set I2S rx bits mod + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bits mod + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_rx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_dither(i2s_dev_t *hw, uint32_t dither) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_dither = dither; } /** - * @brief Set I2S rx sinc dsr 16 en + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx sinc dsr 16 en + * @param dither2 I2S TX PDM sigmadelta dither2 value */ -static inline void i2s_ll_set_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_pdm_sd_dither2(i2s_dev_t *hw, uint32_t dither2) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf.tx_pdm_sigmadelta_dither2 = dither2; } /** - * @brief Set I2S dscr en + * @brief Configure I2S TX PDM sample rate + * Fpdm = 64*Fpcm*fp/fs * * @param hw Peripheral I2S hardware instance address. - * @param val value to set dscr en + * @param fp The fp value of TX PDM filter module group0. + * @param fs The fs value of TX PDM filter module group0. */ -static inline void i2s_ll_set_dscr_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_pdm_fpfs(i2s_dev_t *hw, uint32_t fp, uint32_t fs) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->tx_pcm2pdm_conf1.tx_pdm_fp = fp; + hw->tx_pcm2pdm_conf1.tx_pdm_fs = fs; + hw->tx_pcm2pdm_conf.tx_pdm_sinc_osr2 = fp / fs; } /** - * @brief Set I2S lcd en + * @brief Get I2S TX PDM fp configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set lcd en + * @return + * - fp configuration paramater */ -static inline void i2s_ll_set_lcd_en(i2s_dev_t *hw, bool val) +static inline uint32_t i2s_ll_tx_get_pdm_fp(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + return hw->tx_pcm2pdm_conf1.tx_pdm_fp; } /** - * @brief Set I2S camera en + * @brief Get I2S TX PDM fs configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set camera en + * @return + * - fs configuration paramater */ -static inline void i2s_ll_set_camera_en(i2s_dev_t *hw, bool val) +static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw) { - abort(); // TODO ESP32-H2 IDF-2098 - + return hw->tx_pcm2pdm_conf1.tx_pdm_fs; } /** - * @brief Set I2S pcm2pdm conv en + * @brief Enable RX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set pcm2pdm conv en + * @param pdm_enable Set true to RX enable PDM mode */ -static inline void i2s_ll_set_pcm2pdm_conv_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { - abort(); // TODO ESP32-H2 IDF-2098 - + hw->rx_conf.rx_pdm_en = pdm_enable; + hw->rx_conf.rx_tdm_en = !pdm_enable; } /** - * @brief Set I2S TX to MSB Alignment Standard + * @brief Configura TX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) -{ -} - - -static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->tx_conf.tx_pcm_conf = pcm_cfg; + hw->tx_conf.tx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S TX to PCM long standard + * @brief Configure RX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->rx_conf.rx_pcm_conf = pcm_cfg; + hw->rx_conf.rx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S RX to PCM long standard + * @brief Enable TX audio data left alignment * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_rx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->tx_conf.tx_left_align = ena; } /** - * @brief Set I2S RX to PCM short standard + * @brief Enable RX audio data left alignment * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_left_align = ena; } /** - * @brief Set I2S RX to philip standard + * @brief Enable TX big endian mode * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_big_endian(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_big_endian = ena; } /** - * @brief Set I2S TX to PCM short standard + * @brief Enable RX big endian mode * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_big_endian(i2s_dev_t *hw, bool ena) { + hw->tx_conf.tx_big_endian = ena; } /** - * @brief Set I2S TX to philip standard + * @brief Configure TX bit order * * @param hw Peripheral I2S hardware instance address. + * @param lsb_order_ena Set true to enable LSB bit order */ -static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) { + hw->tx_conf.tx_bit_order = lsb_order_ena; +} + +/** + * @brief Configure RX bit order + * + * @param hw Peripheral I2S hardware instance address. + * @param lsb_order_ena Set true to enable LSB bit order + */ +static inline void i2s_ll_rx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) +{ + hw->rx_conf.rx_bit_order = lsb_order_ena; +} + +/** + * @brief Configure TX skip mask enable + * + * @param hw Peripheral I2S hardware instance address. + * @param skip_mask_ena Set true to skip inactive channels. + */ +static inline void i2s_ll_tx_set_skip_mask(i2s_dev_t *hw, bool skip_mask_ena) +{ + hw->tx_tdm_ctrl.tx_tdm_skip_msk_en = skip_mask_ena; +} + + +/** + * @brief Configure single data + * + * @param hw Peripheral I2S hardware instance address. + * @param data Single data to be set + */ +static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) +{ + hw->conf_single_data = data; +} + +/** + * @brief Enable loopback mode + * + * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable loopback mode. + */ +static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) +{ + hw->tx_conf.sig_loopback = ena; } /** @@ -715,198 +783,6 @@ static inline void i2s_ll_set_pdm2pcm_conv_en(i2s_dev_t *hw, bool val) } -/** - * @brief Set I2S rx pdm en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx pdm en - */ -static inline void i2s_ll_set_rx_pdm_en(i2s_dev_t *hw, bool val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx pdm en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm en - */ -static inline void i2s_ll_set_tx_pdm_en(i2s_dev_t *hw, bool val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mod force en - */ -static inline void i2s_ll_set_tx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S rx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mod force en - */ -static inline void i2s_ll_set_rx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx right first - */ -static inline void i2s_ll_set_tx_right_first(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S rx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx right first - */ -static inline void i2s_ll_set_rx_right_first(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx slave mod - */ -static inline void i2s_ll_set_tx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S rx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx slave mod - */ -static inline void i2s_ll_set_rx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Get I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx msb right - */ -static inline void i2s_ll_get_tx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->conf.tx_msb_right; -} - -/** - * @brief Get I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx msb right - */ -static inline void i2s_ll_get_rx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - // *val = hw->conf.rx_msb_right; -} - -/** - * @brief Set I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb right - */ -static inline void i2s_ll_set_tx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb right - */ -static inline void i2s_ll_set_rx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx mono - */ -static inline void i2s_ll_set_tx_mono(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S rx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx mono - */ -static inline void i2s_ll_set_rx_mono(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S tx sinc osr2 - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx sinc osr2 - */ -static inline void i2s_ll_set_tx_sinc_osr2(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - -/** - * @brief Set I2S sig loopback - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set sig loopback - */ -static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) -{ - abort(); // TODO ESP32-H2 IDF-2098 - -} - #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 3e0a7eeb20..b0c4085ed8 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,89 +31,282 @@ extern "C" { #endif // Get I2S hardware instance with giving i2s num -#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL) +#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL) -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (0xFFFFFFFF) +#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ) + +#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6) +#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) + +/* I2S clock configuration structure */ +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 + uint16_t bck_div; // The BCK devider, Fbck = Fmclk / bck_div +} i2s_ll_clk_cal_t; /** - * @brief Reset rx fifo + * @brief I2S module general init, enable I2S clock. * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_enable_clock(i2s_dev_t *hw) { - hw->conf.rx_fifo_reset = 1; - hw->conf.rx_fifo_reset = 0; + if (hw->clkm_conf.clk_en == 0) { + hw->clkm_conf.clk_sel = 2; + hw->clkm_conf.clk_en = 1; + hw->conf2.val = 0; + } } /** - * @brief Reset tx fifo + * @brief I2S tx msb right enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable tx msb right + */ +static inline void i2s_ll_tx_enable_msb_right(i2s_dev_t *hw, bool enable) +{ + hw->conf.tx_msb_right = enable; +} + +/** + * @brief I2S rx msb right enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable rx msb right + */ +static inline void i2s_ll_rx_enable_msb_right(i2s_dev_t *hw, bool enable) +{ + hw->conf.rx_msb_right = enable; +} + +/** + * @brief I2S tx right channel first + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable send right channel first + */ +static inline void i2s_ll_tx_enable_right_first(i2s_dev_t *hw, bool enable) +{ + hw->conf.tx_right_first = enable; +} + +/** + * @brief I2S rx right channel first + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable receive right channel first + */ +static inline void i2s_ll_rx_enable_right_first(i2s_dev_t *hw, bool enable) +{ + hw->conf.rx_right_first = enable; +} + +/** + * @brief I2S tx fifo module force enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable tx fifo module + */ +static inline void i2s_ll_tx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable) +{ + hw->fifo_conf.tx_fifo_mod_force_en = enable; +} + +/** + * @brief I2S rx fifo module force enable + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable rx fifo module + */ +static inline void i2s_ll_rx_force_enable_fifo_mod(i2s_dev_t *hw, bool enable) +{ + hw->fifo_conf.rx_fifo_mod_force_en = enable; +} + +/** + * @brief Enable I2S TX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->conf.tx_slave_mod = slave_en; +} + +/** + * @brief Enable I2S RX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->conf.rx_slave_mod = slave_en; +} + +/** + * @brief Reset TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset(i2s_dev_t *hw) +{ + hw->conf.tx_reset = 1; + hw->conf.tx_reset = 0; +} + +/** + * @brief Reset RX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_reset(i2s_dev_t *hw) +{ + hw->conf.rx_reset = 1; + hw->conf.rx_reset = 0; +} + +/** + * @brief Reset TX FIFO + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw) { hw->conf.tx_fifo_reset = 1; hw->conf.tx_fifo_reset = 0; } /** - * @brief Enable rx interrupt + * @brief Reset RX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) { - hw->int_ena.in_suc_eof = 1; - hw->int_ena.in_dscr_err = 1; + hw->conf.rx_fifo_reset = 1; + hw->conf.rx_fifo_reset = 0; } /** - * @brief Disable rx interrupt + * @brief Set TX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock */ -static inline void i2s_ll_disable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->int_ena.in_suc_eof = 0; - hw->int_ena.in_dscr_err = 0; + hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2; } /** - * @brief Disable tx interrupt + * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock */ -static inline void i2s_ll_disable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->int_ena.out_eof = 0; - hw->int_ena.out_dscr_err = 0; + hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2; } /** - * @brief Enable tx interrupt + * @brief Configure I2S TX clock devider + * + * @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) +{ + hw->clkm_conf.clkm_div_num = set->mclk_div; + hw->clkm_conf.clkm_div_b = set->b; + hw->clkm_conf.clkm_div_a = set->a; + hw->sample_rate_conf.tx_bck_div_num = set->bck_div; +} + +/** + * @brief Configure I2S RX clock devider + * + * @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) +{ + hw->clkm_conf.clkm_div_num = set->mclk_div; + hw->clkm_conf.clkm_div_b = set->b; + hw->clkm_conf.clkm_div_a = set->a; + hw->sample_rate_conf.rx_bck_div_num = set->bck_div; +} + +/** + * @brief Enable TX interrupt * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 1; hw->int_ena.out_dscr_err = 1; } /** - * @brief Reset dma in + * @brief Disable TX interrupt * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) +static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) { - hw->lc_conf.in_rst = 1; - hw->lc_conf.in_rst = 0; + hw->int_ena.out_eof = 0; + hw->int_ena.out_dscr_err = 0; +} + +/** + * @brief Enable RX interrupt + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) +{ + hw->int_ena.in_suc_eof = 1; + hw->int_ena.in_dscr_err = 1; +} + +/** + * @brief Disable RX interrupt + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_disable_intr(i2s_dev_t *hw) +{ + hw->int_ena.in_suc_eof = 0; + hw->int_ena.in_dscr_err = 0; +} + +/** + * @brief Get I2S interrupt status + * + * @param hw Peripheral I2S hardware instance address. + * @return + * - module interrupt status + */ +static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw) +{ + return hw->int_st.val; +} + +/** + * @brief Clear I2S interrupt status + * + * @param hw Peripheral I2S hardware instance address. + * @param clr_mask Interrupt mask to be cleared. + */ +static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t clr_mask) +{ + hw->int_clr.val = clr_mask; } /** @@ -121,72 +314,111 @@ static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_out(i2s_dev_t *hw) +static inline void i2s_ll_tx_reset_dma(i2s_dev_t *hw) { hw->lc_conf.out_rst = 1; hw->lc_conf.out_rst = 0; } /** - * @brief Reset tx + * @brief Reset dma in * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_dma(i2s_dev_t *hw) { - hw->conf.tx_reset = 1; - hw->conf.tx_reset = 0; + hw->lc_conf.in_rst = 1; + hw->lc_conf.in_rst = 0; } /** - * @brief Reset rx + * @brief Enable TX PDM mode. + * @note ESP32-S2 doesn't support pdm + * This function is used to be compatible with those support pdm * - * @param hw Peripheral I2S hardware instance address. + * @param hw Peripheral I2S hardware instance address (ignored) + * @param pdm_enable Set true to TX enable PDM mode (ignored) */ -static inline void i2s_ll_reset_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { - hw->conf.rx_reset = 1; - hw->conf.rx_reset = 0; + // Remain empty } /** - * @brief Start out link + * @brief Enable RX PDM mode. + * @note ESP32-S2 doesn't support pdm + * This function is used to be compatible with those support pdm * - * @param hw Peripheral I2S hardware instance address. + * @param hw Peripheral I2S hardware instance address (ignored) + * @param pdm_enable Set true to RX enable PDM mode (ignored) */ -static inline void i2s_ll_start_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { - hw->out_link.start = 1; + // Remain empty } /** - * @brief Start tx + * @brief Start TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_start(i2s_dev_t *hw) { hw->conf.tx_start = 1; } /** - * @brief Start in link + * @brief Start RX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_start(i2s_dev_t *hw) { + hw->conf.rx_start = 1; +} + +/** + * @brief Configure TX DMA descriptor address and start TX DMA + * + * @param hw Peripheral I2S hardware instance address. + * @param link_addr DMA descriptor link address. + */ +static inline void i2s_ll_tx_start_link(i2s_dev_t *hw, uint32_t link_addr) +{ + hw->out_link.addr = link_addr; + hw->out_link.start = 1; +} + +/** + * @brief Configure RX DMA descriptor address and start TX DMA + * + * @param hw Peripheral I2S hardware instance address. + * @param link_addr DMA descriptor link address. + */ +static inline void i2s_ll_rx_start_link(i2s_dev_t *hw, uint32_t link_addr) +{ + hw->in_link.addr = link_addr; hw->in_link.start = 1; } /** - * @brief Start rx + * @brief Stop TX module * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop(i2s_dev_t *hw) { - hw->conf.rx_start = 1; + hw->conf.tx_start = 0; +} + +/** + * @brief Stop RX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_stop(i2s_dev_t *hw) +{ + hw->conf.rx_start = 0; } /** @@ -194,482 +426,89 @@ static inline void i2s_ll_start_rx(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_out_link(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop_link(i2s_dev_t *hw) { hw->out_link.stop = 1; } -/** - * @brief Stop tx - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_stop_tx(i2s_dev_t *hw) -{ - hw->conf.tx_start = 0; -} - /** * @brief Stop in link * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_stop_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_stop_link(i2s_dev_t *hw) { hw->in_link.stop = 1; } /** - * @brief Stop rx + * @brief Get I2S out eof descriptor address * * @param hw Peripheral I2S hardware instance address. + * @param eof_addr Pointer to accept out eof des address */ -static inline void i2s_ll_stop_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr) { - hw->conf.rx_start = 0; + *eof_addr = hw->out_eof_des_addr; } /** - * @brief Enable dma + * @brief Get I2S in eof descriptor address * * @param hw Peripheral I2S hardware instance address. + * @param eof_addr Pointer to accept in eof des address */ -static inline void i2s_ll_enable_dma(i2s_dev_t *hw) +static inline void i2s_ll_rx_get_eof_des_addr(i2s_dev_t *hw, uint32_t *eof_addr) { - //Enable and configure DMA - typeof(hw->lc_conf) lc_conf; - lc_conf.val = 0; - lc_conf.out_eof_mode = 1; - hw->lc_conf.val = lc_conf.val; + *eof_addr = hw->in_eof_des_addr; } /** - * @brief Get I2S interrupt status + * @brief Configure the received length to trigger in_suc_eof interrupt * * @param hw Peripheral I2S hardware instance address. - * @param val value to get interrupt status + * @param eof_num the byte length to trigger in_suc_eof interrupt */ -static inline void i2s_ll_get_intr_status(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, uint32_t eof_num) { - *val = hw->int_st.val; + hw->rx_eof_num = eof_num; } /** - * @brief Clear I2S interrupt status + * @brief Congfigure TX chan bit and audio data bit, on ESP32-S2, sample_bit should equals to data_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to clear interrupt status + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->int_clr.val = val; + hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->sample_rate_conf.tx_bits_mod = data_bit; } /** - * @brief Get I2S out eof des address + * @brief Congfigure RX chan bit and audio data bit, on ESP32-S2, sample_bit should equals to data_bit * * @param hw Peripheral I2S hardware instance address. - * @param val value to get out eof des address + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_get_out_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - *val = hw->out_eof_des_addr; + hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->sample_rate_conf.rx_bits_mod = data_bit; } /** - * @brief Get I2S in eof des address + * @brief Enable I2S DMA * * @param hw Peripheral I2S hardware instance address. - * @param val value to get in eof des address + * @param ena Set true to enable DMA */ -static inline void i2s_ll_get_in_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_enable_dma(i2s_dev_t *hw, bool ena) { - *val = hw->in_eof_des_addr; -} - -/** - * @brief Get I2S tx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx fifo mode - */ -static inline void i2s_ll_get_tx_fifo_mod(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->fifo_conf.tx_fifo_mod; -} - -/** - * @brief Set I2S tx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mode - */ -static inline void i2s_ll_set_tx_fifo_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->fifo_conf.tx_fifo_mod = val; -} - -/** - * @brief Get I2S rx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx fifo mode - */ -static inline void i2s_ll_get_rx_fifo_mod(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->fifo_conf.rx_fifo_mod; -} - -/** - * @brief Set I2S rx fifo mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mode - */ -static inline void i2s_ll_set_rx_fifo_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->fifo_conf.rx_fifo_mod = val; -} - -/** - * @brief Set I2S tx chan mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx chan mode - */ -static inline void i2s_ll_set_tx_chan_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf_chan.tx_chan_mod = val; -} - -/** - * @brief Set I2S rx chan mode - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx chan mode - */ -static inline void i2s_ll_set_rx_chan_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf_chan.rx_chan_mod = val; -} - -/** - * @brief Set I2S tx dma equal - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx dma equal - */ -static inline void i2s_ll_set_tx_dma_equal(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.tx_dma_equal = val; -} - -/** - * @brief Set I2S rx dma equal - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx dma equal - */ -static inline void i2s_ll_set_rx_dma_equal(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_dma_equal = val; -} - -/** - * @brief Set I2S out link address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set out link address - */ -static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val) -{ - hw->out_link.addr = val; -} - -/** - * @brief Set I2S in link address - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set in link address - */ -static inline void i2s_ll_set_in_link_addr(i2s_dev_t *hw, uint32_t val) -{ - hw->in_link.addr = val; -} - -/** - * @brief Set I2S rx eof num - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx eof num - */ -static inline void i2s_ll_set_rx_eof_num(i2s_dev_t *hw, uint32_t val) -{ - hw->rx_eof_num = val; -} - -/** - * @brief Set I2S clkm div num - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div num - */ -static inline void i2s_ll_set_clkm_div_num(i2s_dev_t *hw, uint32_t val) -{ - hw->clkm_conf.clkm_div_num = val; -} - -/** - * @brief Set I2S clkm div b - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div b - */ -static inline void i2s_ll_set_clkm_div_b(i2s_dev_t *hw, uint32_t val) -{ - hw->clkm_conf.clkm_div_b = val; -} - -/** - * @brief Set I2S clkm div a - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div a - */ -static inline void i2s_ll_set_clkm_div_a(i2s_dev_t *hw, uint32_t val) -{ - hw->clkm_conf.clkm_div_a = val; -} - -/** - * @brief Set I2S tx bck div num - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bck div num - */ -static inline void i2s_ll_set_tx_bck_div_num(i2s_dev_t *hw, uint32_t val) -{ - hw->sample_rate_conf.tx_bck_div_num = val; -} - -/** - * @brief Set I2S rx bck div num - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bck div num - */ -static inline void i2s_ll_set_rx_bck_div_num(i2s_dev_t *hw, uint32_t val) -{ - hw->sample_rate_conf.rx_bck_div_num = val; -} - -/** - * @brief Set I2S clk sel - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set clk sel - */ -static inline void i2s_ll_set_clk_sel(i2s_dev_t *hw, uint32_t val) -{ - hw->clkm_conf.clk_sel = (val == 1) ? 1 : 2; -} - -/** - * @brief Set I2S tx bits mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bits mod - */ -static inline void i2s_ll_set_tx_bits_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->sample_rate_conf.tx_bits_mod = val; -} - -/** - * @brief Set I2S rx bits mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bits mod - */ -static inline void i2s_ll_set_rx_bits_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->sample_rate_conf.rx_bits_mod = val; -} - -/** - * @brief Set I2S dscr en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set dscr en - */ -static inline void i2s_ll_set_dscr_en(i2s_dev_t *hw, bool val) -{ - hw->fifo_conf.dscr_en = val; -} - -/** - * @brief Set I2S lcd en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set lcd en - */ -static inline void i2s_ll_set_lcd_en(i2s_dev_t *hw, bool val) -{ - hw->conf2.lcd_en = val; -} - -/** - * @brief Set I2S camera en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set camera en - */ -static inline void i2s_ll_set_camera_en(i2s_dev_t *hw, bool val) -{ - hw->conf2.camera_en = val; -} - -/** - * @brief Set I2S tx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mod force en - */ -static inline void i2s_ll_set_tx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ - hw->fifo_conf.tx_fifo_mod_force_en = val; -} - -/** - * @brief Set I2S rx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mod force en - */ -static inline void i2s_ll_set_rx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ - hw->fifo_conf.rx_fifo_mod_force_en = val; -} - -/** - * @brief Set I2S tx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx right first - */ -static inline void i2s_ll_set_tx_right_first(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.tx_right_first = val; -} - -/** - * @brief Set I2S rx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx right first - */ -static inline void i2s_ll_set_rx_right_first(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_right_first = val; -} - -/** - * @brief Set I2S tx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx slave mod - */ -static inline void i2s_ll_set_tx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.tx_slave_mod = val; -} - -/** - * @brief Set I2S rx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx slave mod - */ -static inline void i2s_ll_set_rx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_slave_mod = val; -} - -/** - * @brief Get I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx msb right - */ -static inline void i2s_ll_get_tx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->conf.tx_msb_right; -} - -/** - * @brief Get I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx msb right - */ -static inline void i2s_ll_get_rx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ - *val = hw->conf.rx_msb_right; -} - -/** - * @brief Set I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb right - */ -static inline void i2s_ll_set_tx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.tx_msb_right = val; -} - -/** - * @brief Set I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb right - */ -static inline void i2s_ll_set_rx_msb_right(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_msb_right = val; -} - -/** - * @brief Set I2S tx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx mono - */ -static inline void i2s_ll_set_tx_mono(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.tx_mono = val; -} - -/** - * @brief Set I2S rx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx mono - */ -static inline void i2s_ll_set_rx_mono(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.rx_mono = val; -} - -/** - * @brief Set I2S sig loopback - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set sig loopback - */ -static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) -{ - hw->conf.sig_loopback = val; + hw->fifo_conf.dscr_en = ena; } /** @@ -677,7 +516,7 @@ static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_format_philip(i2s_dev_t *hw) { hw->conf.tx_short_sync = 0; hw->conf.tx_msb_shift = 1; @@ -688,7 +527,7 @@ static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_format_philip(i2s_dev_t *hw) { hw->conf.rx_short_sync = 0; hw->conf.rx_msb_shift = 1; @@ -699,7 +538,7 @@ static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_format_msb_align(i2s_dev_t *hw) { hw->conf.tx_short_sync = 0; hw->conf.tx_msb_shift = 0; @@ -710,7 +549,7 @@ static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_format_msb_align(i2s_dev_t *hw) { hw->conf.rx_short_sync = 0; hw->conf.rx_msb_shift = 0; @@ -721,7 +560,7 @@ static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_pcm_short(i2s_dev_t *hw) { hw->conf.tx_short_sync = 1; hw->conf.tx_msb_shift = 0; @@ -732,7 +571,7 @@ static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_pcm_short(i2s_dev_t *hw) { hw->conf.rx_short_sync = 1; hw->conf.rx_msb_shift = 0; @@ -743,7 +582,7 @@ static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_pcm_long(i2s_dev_t *hw) { hw->conf.tx_short_sync = 0; hw->conf.tx_msb_shift = 0; @@ -754,12 +593,95 @@ static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_set_rx_pcm_long(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_pcm_long(i2s_dev_t *hw) { hw->conf.rx_short_sync = 0; hw->conf.rx_msb_shift = 0; } +/** + * @brief Configure TX WS signal width + * + * @param hw Peripheral I2S hardware instance address. + * @param width WS width in BCK cycle + */ +static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width) +{ + hw->conf.tx_short_sync = width == 1 ? 1 : 0; +} + +/** + * @brief Configure RX WS signal width + * + * @param hw Peripheral I2S hardware instance address. + * @param width WS width in BCK cycle + */ +static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width) +{ + hw->conf.rx_short_sync = width == 1 ? 1 : 0; +} + +/** + * @brief Enable TX MSB shift, the data will be launch at the first BCK clock + * + * @param hw Peripheral I2S hardware instance address. + * @param msb_shift_enable Set true to enable MSB shift + */ +static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) +{ + hw->conf.tx_msb_shift = msb_shift_enable; +} + +/** + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock + * + * @param hw Peripheral I2S hardware instance address. + * @param msb_shift_enable Set true to enable MSB shift + */ +static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) +{ + hw->conf.rx_msb_shift = msb_shift_enable; +} + +/** + * @brief Enable TX mono mode + * + * @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) +{ + int data_bit = hw->sample_rate_conf.tx_bits_mod; + hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->conf.tx_dma_equal = mono_ena; + hw->conf_chan.tx_chan_mod = 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) +{ + int data_bit = hw->sample_rate_conf.rx_bits_mod; + hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->conf.rx_dma_equal = mono_ena; + hw->conf_chan.rx_chan_mod = mono_ena; +} + +/** + * @brief Enable I2S loopback mode + * + * @param hw Peripheral I2S hardware instance address. + * @param loopback_en Set true to enable loopback mode. + */ +static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool loopback_en) +{ + hw->conf.sig_loopback = loopback_en; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index 645e84793e..76ef727b88 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/components/hal/esp32s3/include/hal/i2s_ll.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +// The LL layer for I2S register operations /******************************************************************************* * NOTICE * The hal is not public api, don't use in application code. * See readme.md in hal/include/hal/readme.md ******************************************************************************/ -// The LL layer for ESP32-S3 I2S register operations - #pragma once - #include #include "soc/i2s_periph.h" #include "hal/i2s_types.h" @@ -30,819 +28,772 @@ extern "C" { #endif -// Get I2S hardware instance with giving i2s num -#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : NULL) -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (0xFFFFFFFF) +#define I2S_LL_GET_HW(num) (((num) == 0) ? (&I2S0) : &I2S1) + +#define I2S_LL_TDM_CH_MASK (0xffff) +#define I2S_LL_PDM_BCK_FACTOR (64) +#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ) + +#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9) +#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) + +/* I2S clock configuration structure */ +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 + uint16_t bck_div; // The BCK devider, Fbck = Fmclk / bck_div +} i2s_ll_clk_cal_t; /** - * @brief Reset rx fifo + * @brief I2S module general init, enable I2S clock. * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_enable_clock(i2s_dev_t *hw) { - hw->rx_conf.rx_fifo_reset = 1; - hw->rx_conf.rx_fifo_reset = 0; + hw->tx_clkm_conf.clk_en = 1; } /** - * @brief Reset tx fifo + * @brief Enable I2S tx module clock * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx_fifo(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_clock(i2s_dev_t *hw) +{ + hw->tx_clkm_conf.tx_clk_active = 1; +} + +/** + * @brief Enable I2S rx module clock + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_clock(i2s_dev_t *hw) +{ + hw->rx_clkm_conf.rx_clk_active = 1; +} + +/** + * @brief I2S mclk use tx module clock + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) +{ + hw->rx_clkm_conf.mclk_sel = 0; +} + +/** + * @brief I2S mclk use rx module clock + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) +{ + hw->rx_clkm_conf.mclk_sel = 1; +} + +/** + * @brief Enable I2S TX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_tx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->tx_conf.tx_slave_mod = slave_en; +} + +/** + * @brief Enable I2S RX slave mode + * + * @param hw Peripheral I2S hardware instance address. + * @param slave_en Set true to enable slave mode + */ +static inline void i2s_ll_rx_set_slave_mod(i2s_dev_t *hw, bool slave_en) +{ + hw->rx_conf.rx_slave_mod = slave_en; +} + +/** + * @brief Reset I2S TX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_reset(i2s_dev_t *hw) +{ + hw->tx_conf.tx_reset = 1; + hw->tx_conf.tx_reset = 0; +} + +/** + * @brief Reset I2S RX module + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_reset(i2s_dev_t *hw) +{ + hw->rx_conf.rx_reset = 1; + hw->rx_conf.rx_reset = 0; +} + +/** + * @brief Reset I2S TX FIFO + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_reset_fifo(i2s_dev_t *hw) { hw->tx_conf.tx_fifo_reset = 1; hw->tx_conf.tx_fifo_reset = 0; } /** - * @brief Enable rx interrupt + * @brief Reset I2S RX FIFO * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_enable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) { + hw->rx_conf.rx_fifo_reset = 1; + hw->rx_conf.rx_fifo_reset = 0; } /** - * @brief Disable rx interrupt + * @brief Set TX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK` + * TX and RX share the same clock setting */ -static inline void i2s_ll_disable_rx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { + hw->tx_clkm_conf.tx_clk_sel = 2; } /** - * @brief Disable tx interrupt + * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. + * @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK` + * TX and RX share the same clock setting */ -static inline void i2s_ll_disable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { + hw->rx_clkm_conf.rx_clk_sel = 2; } /** - * @brief Enable tx interrupt + * @brief Configure I2S TX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_enable_tx_intr(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { + if (set->a == 0 || set->b == 0) { + hw->tx_clkm_div_conf.tx_clkm_div_x = 0; + hw->tx_clkm_div_conf.tx_clkm_div_y = 0; + hw->tx_clkm_div_conf.tx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); + hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; + } else { + hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; + } + } + hw->tx_clkm_conf.tx_clkm_div_num = set->mclk_div; + hw->tx_conf1.tx_bck_div_num = set->bck_div - 1; } /** - * @brief Reset dma in + * @brief Configure I2S RX clock devider * * @param hw Peripheral I2S hardware instance address. + * @param set Pointer to I2S clock devider configuration paramater */ -static inline void i2s_ll_reset_dma_in(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_clk_cal_t *set) { + if (set->a == 0 || set->b == 0) { + hw->rx_clkm_div_conf.rx_clkm_div_x = 0; + hw->rx_clkm_div_conf.rx_clkm_div_y = 0; + hw->rx_clkm_div_conf.rx_clkm_div_z = 0; + } else { + if (set->b > set->a / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); + hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; + } else { + hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; + } + } + hw->rx_clkm_conf.rx_clkm_div_num = set->mclk_div; + hw->rx_conf1.rx_bck_div_num = set->bck_div - 1; } /** - * @brief Reset dma out + * @brief Start I2S TX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_dma_out(i2s_dev_t *hw) +static inline void i2s_ll_tx_start(i2s_dev_t *hw) { + hw->tx_conf.tx_update = 0; + hw->tx_conf.tx_update = 1; + hw->tx_conf.tx_start = 1; } /** - * @brief Reset tx + * @brief Start I2S RX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_start(i2s_dev_t *hw) { + hw->rx_conf.rx_update = 0; + hw->rx_conf.rx_update = 1; + hw->rx_conf.rx_start = 1; } /** - * @brief Reset rx + * @brief Stop I2S TX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_reset_rx(i2s_dev_t *hw) +static inline void i2s_ll_tx_stop(i2s_dev_t *hw) { + hw->tx_conf.tx_start = 0; } /** - * @brief Start out link + * @brief Stop I2S RX * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_start_out_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_stop(i2s_dev_t *hw) { + hw->rx_conf.rx_start = 0; } /** - * @brief Start tx + * @brief Configure TX WS signal width * * @param hw Peripheral I2S hardware instance address. + * @param width WS width in BCK cycle */ -static inline void i2s_ll_start_tx(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_ws_width(i2s_dev_t *hw, int width) { + hw->tx_conf1.tx_tdm_ws_width = width - 1; } /** - * @brief Start in link + * @brief Configure RX WS signal width * * @param hw Peripheral I2S hardware instance address. + * @param width WS width in BCK cycle */ -static inline void i2s_ll_start_in_link(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_ws_width(i2s_dev_t *hw, int width) { + hw->rx_conf1.rx_tdm_ws_width = width - 1; } /** - * @brief Start rx + * @brief Configure the received length to trigger in_suc_eof interrupt * * @param hw Peripheral I2S hardware instance address. + * @param eof_num the byte length to trigger in_suc_eof interrupt */ -static inline void i2s_ll_start_rx(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, int eof_num) { + hw->rx_eof_num.rx_eof_num = eof_num; } /** - * @brief Stop out link + * @brief Congfigure TX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_stop_out_link(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { + hw->tx_conf1.tx_bits_mod = data_bit - 1; + hw->tx_conf1.tx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Stop tx + * @brief Congfigure RX chan bit and audio data bit * * @param hw Peripheral I2S hardware instance address. + * @param chan_bit The chan bit width + * @param data_bit The audio data bit width */ -static inline void i2s_ll_stop_tx(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { + hw->rx_conf1.rx_bits_mod = data_bit - 1; + hw->rx_conf1.rx_tdm_chan_bits = chan_bit - 1; } /** - * @brief Stop in link + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_stop_in_link(i2s_dev_t *hw) +static inline void i2s_ll_tx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { + hw->tx_conf1.tx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Stop rx + * @brief Configure RX half_sample_bit * * @param hw Peripheral I2S hardware instance address. + * @param half_sample_bits half sample bit width */ -static inline void i2s_ll_stop_rx(i2s_dev_t *hw) +static inline void i2s_ll_rx_set_half_sample_bit(i2s_dev_t *hw, int half_sample_bits) { + hw->rx_conf1.rx_half_sample_bits = half_sample_bits - 1; } /** - * @brief Enable dma + * @brief Enable TX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_enable_dma(i2s_dev_t *hw) +static inline void i2s_ll_tx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { + hw->tx_conf1.tx_msb_shift = msb_shift_enable; } /** - * @brief Get I2S interrupt status + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock * * @param hw Peripheral I2S hardware instance address. - * @param val value to get interrupt status + * @param msb_shift_enable Set true to enable MSB shift */ -static inline void i2s_ll_get_intr_status(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enable) { - *val = hw->int_st.val; + hw->rx_conf1.rx_msb_shift = msb_shift_enable; } /** - * @brief Clear I2S interrupt status + * @brief Configure TX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to clear interrupt status + * @param total_num Total chan number */ -static inline void i2s_ll_clear_intr_status(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_chan_num(i2s_dev_t *hw, int total_num) { - hw->int_clr.val = val; + hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Get I2S out eof des address + * @brief Configure RX total chan number * * @param hw Peripheral I2S hardware instance address. - * @param val value to get out eof des address + * @param total_num Total chan number */ -static inline void i2s_ll_get_out_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_chan_num(i2s_dev_t *hw, int total_num) { + hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = total_num - 1; } /** - * @brief Get I2S in eof des address + * @brief Set the bimap of the active TX chan, only the active chan can launch audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to get in eof des address + * @param chan_mask mask of tx active chan */ -static inline void i2s_ll_get_in_eof_des_addr(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { + typeof(hw->tx_tdm_ctrl) tdm_ctrl_reg = hw->tx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->tx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Get I2S tx fifo mode + * @brief Set the bimap of the active RX chan, only the active chan can receive audio data. * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx fifo mode + * @param chan_mask mask of rx active chan */ -static inline void i2s_ll_get_tx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_mask) { + typeof(hw->rx_tdm_ctrl) tdm_ctrl_reg = hw->rx_tdm_ctrl; + tdm_ctrl_reg.val &= ~I2S_LL_TDM_CH_MASK; + tdm_ctrl_reg.val |= chan_mask & I2S_LL_TDM_CH_MASK; + hw->rx_tdm_ctrl.val = tdm_ctrl_reg.val; } /** - * @brief Set I2S tx fifo mode + * @brief Set TX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mode + * @param ws_pol_level pin level of WS(output) when receiving left channel data */ -static inline void i2s_ll_set_tx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_tx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { + hw->tx_conf.tx_ws_idle_pol = ws_pol_level; } /** - * @brief Get I2S rx fifo mode + * @brief Set RX WS signal pol level * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx fifo mode + * @param ws_pol_level pin level of WS(input) when receiving left channel data */ -static inline void i2s_ll_get_rx_fifo_mod(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_rx_set_ws_idle_pol(i2s_dev_t *hw, int ws_pol_level) { + hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } /** - * @brief Set I2S rx fifo mode + * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mode + * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_set_rx_fifo_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { + hw->tx_conf.tx_pdm_en = pdm_enable; + hw->tx_conf.tx_tdm_en = !pdm_enable; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; } /** - * @brief Set I2S tx chan mode + * @brief Enable RX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx chan mode + * @param pdm_enable Set true to RX enable PDM mode */ -static inline void i2s_ll_set_tx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) { + hw->rx_conf.rx_pdm_en = pdm_enable; + hw->rx_conf.rx_tdm_en = !pdm_enable; + hw->rx_conf.rx_pdm2pcm_en = pdm_enable; } /** - * @brief Set I2S rx chan mode + * @brief Configure I2S TX PDM sample rate + * Fpdm = 64*Fpcm*fp/fs * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx chan mode + * @param fp The fp value of TX PDM filter module group0. + * @param fs The fs value of TX PDM filter module group0. */ -static inline void i2s_ll_set_rx_chan_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_fpfs(i2s_dev_t *hw, uint32_t fp, uint32_t fs) { + hw->tx_pcm2pdm_conf1.tx_pdm_fp = fp; + hw->tx_pcm2pdm_conf1.tx_pdm_fs = fs; + hw->tx_pcm2pdm_conf.tx_sinc_osr2 = fp / fs; } /** - * @brief Set I2S out link address + * @brief Get I2S TX PDM fp configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set out link address + * @return + * - fp configuration paramater */ -static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val) +static inline uint32_t i2s_ll_tx_get_pdm_fp(i2s_dev_t *hw) { + return hw->tx_pcm2pdm_conf1.tx_pdm_fp; } /** - * @brief Set I2S in link address + * @brief Get I2S TX PDM fs configuration paramater * * @param hw Peripheral I2S hardware instance address. - * @param val value to set in link address + * @return + * - fs configuration paramater */ -static inline void i2s_ll_set_in_link_addr(i2s_dev_t *hw, uint32_t val) +static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw) { + return hw->tx_pcm2pdm_conf1.tx_pdm_fs; } /** - * @brief Set I2S rx eof num + * @brief Set I2S TX PDM prescale * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx eof num + * @param prescale I2S TX PDM prescale */ -static inline void i2s_ll_set_rx_eof_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_prescale(i2s_dev_t *hw, bool prescale) { + hw->tx_pcm2pdm_conf.tx_prescale = prescale; } /** - * @brief Get I2S tx pdm fp + * @brief Set I2S TX PDM high pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fp + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_get_tx_pdm_fp(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_pdm_hp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { + hw->tx_pcm2pdm_conf.tx_hp_in_shift = sig_scale; } /** - * @brief Get I2S tx pdm fs + * @brief Set I2S TX PDM low pass filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx pdm fs + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_get_tx_pdm_fs(i2s_dev_t *hw, uint32_t *val) +static inline void i2s_ll_tx_set_pdm_lp_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { + hw->tx_pcm2pdm_conf.tx_lp_in_shift = sig_scale; } /** - * @brief Set I2S tx pdm fp + * @brief Set I2S TX PDM sinc filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fp + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_tx_pdm_fp(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sinc_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { + hw->tx_pcm2pdm_conf.tx_sinc_in_shift = sig_scale; } /** - * @brief Set I2S tx pdm fs + * @brief Set I2S TX PDM sigma-delta filter scaling * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm fs + * @param sig_scale I2S TX PDM signal scaling before transmit to the filter */ -static inline void i2s_ll_set_tx_pdm_fs(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_scale(i2s_dev_t *hw, i2s_pdm_sig_scale_t sig_scale) { + hw->tx_pcm2pdm_conf.tx_sigmadelta_in_shift = sig_scale; } /** - * @brief Get I2S rx sinc dsr 16 en + * @brief Set I2S TX PDM high pass filter param0 * * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx sinc dsr 16 en + * @param param The fourth parameter of PDM TX IIR_HP filter stage 1 is (504 + I2S_TX_IIR_HP_MULT12_0[2:0]) */ -static inline void i2s_ll_get_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool *val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param0(i2s_dev_t *hw, uint32_t param) { + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_0 = param; } /** - * @brief Set I2S clkm div num + * @brief Set I2S TX PDM high pass filter param5 * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div num + * @param param The fourth parameter of PDM TX IIR_HP filter stage 2 is (504 + I2S_TX_IIR_HP_MULT12_5[2:0]) */ -static inline void i2s_ll_set_clkm_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_hp_filter_param5(i2s_dev_t *hw, uint32_t param) { + hw->tx_pcm2pdm_conf1.tx_iir_hp_mult12_5 = param; } /** - * @brief Set I2S clkm div b + * @brief Enable I2S TX PDM high pass filter * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div b + * @param enable Set true to enable I2S TX PDM high pass filter, set false to bypass it */ -static inline void i2s_ll_set_clkm_div_b(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm_hp_filter(i2s_dev_t *hw, bool enable) { + hw->tx_pcm2pdm_conf.tx_hp_bypass = !enable; } /** - * @brief Set I2S clkm div a + * @brief Enable I2S TX PDM sigma-delta codec * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clkm div a + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_clkm_div_a(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_enable_pdm_sd_codec(i2s_dev_t *hw, bool enable) { + hw->tx_pcm2pdm_conf.tx_dac_2out_en = enable; + hw->tx_pcm2pdm_conf.tx_dac_mode_en = enable; } /** - * @brief Set I2S tx bck div num + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bck div num + * @param dither I2S TX PDM sigmadelta dither value */ -static inline void i2s_ll_set_tx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_dither(i2s_dev_t *hw, uint32_t dither) { + hw->tx_pcm2pdm_conf.tx_sigmadelta_dither = dither; } /** - * @brief Set I2S rx bck div num + * @brief Set I2S TX PDM sigma-delta codec dither * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bck div num + * @param dither2 I2S TX PDM sigmadelta dither2 value */ -static inline void i2s_ll_set_rx_bck_div_num(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pdm_sd_dither2(i2s_dev_t *hw, uint32_t dither2) { + hw->tx_pcm2pdm_conf.tx_sigmadelta_dither2 = dither2; } /** - * @brief Set I2S clk sel + * @brief Configure RX PDM downsample * * @param hw Peripheral I2S hardware instance address. - * @param val value to set clk sel + * @param dsr PDM downsample configuration paramater */ -static inline void i2s_ll_set_clk_sel(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_set_pdm_dsr(i2s_dev_t *hw, i2s_pdm_dsr_t dsr) { + hw->rx_conf.rx_pdm_sinc_dsr_16_en = dsr; } /** - * @brief Set I2S tx bits mod + * @brief Get RX PDM downsample configuration * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx bits mod + * @param dsr Pointer to accept PDM downsample configuration */ -static inline void i2s_ll_set_tx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_rx_get_pdm_dsr(i2s_dev_t *hw, i2s_pdm_dsr_t *dsr) { + *dsr = hw->rx_conf.rx_pdm_sinc_dsr_16_en; } /** - * @brief Set I2S rx bits mod + * @brief Configura TX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx bits mod + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_rx_bits_mod(i2s_dev_t *hw, uint32_t val) +static inline void i2s_ll_tx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->tx_conf.tx_pcm_conf = pcm_cfg; + hw->tx_conf.tx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S rx sinc dsr 16 en + * @brief Configure RX a/u-law decompress or compress * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx sinc dsr 16 en + * @param pcm_cfg PCM configuration paramater */ -static inline void i2s_ll_set_rx_sinc_dsr_16_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_set_pcm_type(i2s_dev_t *hw, i2s_pcm_compress_t pcm_cfg) { + hw->rx_conf.rx_pcm_conf = pcm_cfg; + hw->rx_conf.rx_pcm_bypass = !pcm_cfg; } /** - * @brief Set I2S dscr en + * @brief Enable TX audio data left alignment * * @param hw Peripheral I2S hardware instance address. - * @param val value to set dscr en + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_dscr_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->tx_conf.tx_left_align = ena; } /** - * @brief Set I2S lcd en + * @brief Enable RX audio data left alignment * * @param hw Peripheral I2S hardware instance address. - * @param val value to set lcd en + * @param ena Set true to enable left alignment */ -static inline void i2s_ll_set_lcd_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_enable_left_align(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_left_align = ena; } /** - * @brief Set I2S camera en + * @brief Enable TX big endian mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set camera en + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_camera_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_enable_big_endian(i2s_dev_t *hw, bool ena) { + hw->rx_conf.rx_big_endian = ena; } /** - * @brief Set I2S pcm2pdm conv en + * @brief Enable RX big endian mode * * @param hw Peripheral I2S hardware instance address. - * @param val value to set pcm2pdm conv en + * @param ena Set true to enable big endian mode */ -static inline void i2s_ll_set_pcm2pdm_conv_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_enable_big_endian(i2s_dev_t *hw, bool ena) { + hw->tx_conf.tx_big_endian = ena; } /** - * @brief Set I2S pdm2pcm conv en + * @brief Configure TX bit order * * @param hw Peripheral I2S hardware instance address. - * @param val value to set pdm2pcm conv en + * @param lsb_order_ena Set true to enable LSB bit order */ -static inline void i2s_ll_set_pdm2pcm_conv_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) { + hw->tx_conf.tx_bit_order = lsb_order_ena; } /** - * @brief Set I2S rx pdm en + * @brief Configure RX bit order * * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx pdm en + * @param lsb_order_ena Set true to enable LSB bit order */ -static inline void i2s_ll_set_rx_pdm_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_rx_set_bit_order(i2s_dev_t *hw, bool lsb_order_ena) { + hw->rx_conf.rx_bit_order = lsb_order_ena; } /** - * @brief Set I2S tx pdm en + * @brief Configure TX skip mask enable * * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx pdm en + * @param skip_mask_ena Set true to skip inactive channels. */ -static inline void i2s_ll_set_tx_pdm_en(i2s_dev_t *hw, bool val) +static inline void i2s_ll_tx_set_skip_mask(i2s_dev_t *hw, bool skip_mask_ena) { + hw->tx_tdm_ctrl.tx_tdm_skip_msk_en = skip_mask_ena; } -/** - * @brief Set I2S tx msb shift - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb shift - */ -static inline void i2s_ll_set_tx_msb_shift(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx msb shift - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb shift - */ -static inline void i2s_ll_set_rx_msb_shift(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S tx short sync - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx short sync - */ -static inline void i2s_ll_set_tx_short_sync(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx short sync - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx short sync - */ -static inline void i2s_ll_set_rx_short_sync(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S tx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx fifo mod force en - */ -static inline void i2s_ll_set_tx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ -} - -/** - * @brief Set I2S rx fifo mod force en - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx fifo mod force en - */ -static inline void i2s_ll_set_rx_fifo_mod_force_en(i2s_dev_t *hw, bool val) -{ -} - -/** - * @brief Set I2S tx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx right first - */ -static inline void i2s_ll_set_tx_right_first(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx right first - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx right first - */ -static inline void i2s_ll_set_rx_right_first(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S tx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx slave mod - */ -static inline void i2s_ll_set_tx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx slave mod - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx slave mod - */ -static inline void i2s_ll_set_rx_slave_mod(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Get I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get tx msb right - */ -static inline void i2s_ll_get_tx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ -} - -/** - * @brief Get I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to get rx msb right - */ -static inline void i2s_ll_get_rx_msb_right(i2s_dev_t *hw, uint32_t *val) -{ -} - -/** - * @brief Set I2S tx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx msb right - */ -static inline void i2s_ll_set_tx_msb_right(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx msb right - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx msb right - */ -static inline void i2s_ll_set_rx_msb_right(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S tx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx mono - */ -static inline void i2s_ll_set_tx_mono(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S rx mono - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set rx mono - */ -static inline void i2s_ll_set_rx_mono(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S tx sinc osr2 - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set tx sinc osr2 - */ -static inline void i2s_ll_set_tx_sinc_osr2(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S sig loopback - * - * @param hw Peripheral I2S hardware instance address. - * @param val value to set sig loopback - */ -static inline void i2s_ll_set_sig_loopback(i2s_dev_t *hw, uint32_t val) -{ -} - -/** - * @brief Set I2S TX to philip standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_format_philip(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S RX to philip standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_format_philip(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S TX to MSB Alignment Standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_format_msb_align(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S RX to MSB Alignment Standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_format_msb_align(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S TX to PCM short standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_pcm_short(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S RX to PCM short standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_pcm_short(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S TX to PCM long standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_tx_pcm_long(i2s_dev_t *hw) -{ -} - -/** - * @brief Set I2S RX to PCM long standard - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_set_rx_pcm_long(i2s_dev_t *hw) -{ -} - -/** - * @brief Configure I2S TX pdm - * - * @param sample_rate The sample rate to be set. - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_tx_pdm_cfg(i2s_dev_t *hw, uint32_t sample_rate) -{ -} - -/** - * @brief Configure I2S TX pdm - * - * @param hw Peripheral I2S hardware instance address. - */ -static inline void i2s_ll_rx_pdm_cfg(i2s_dev_t *hw) -{ -} /** - * @brief Enable I2S build in ADC mode + * @brief Configure single data * * @param hw Peripheral I2S hardware instance address. + * @param data Single data to be set */ -static inline void i2s_ll_build_in_adc_ena(i2s_dev_t *hw) +static inline void i2s_ll_set_single_data(i2s_dev_t *hw, uint32_t data) { + hw->conf_single_data = data; } /** - * @brief Enable I2S build in DAC mode + * @brief Enable loopback mode * * @param hw Peripheral I2S hardware instance address. + * @param ena Set true to enable loopback mode. */ -static inline void i2s_ll_build_in_dac_ena(i2s_dev_t *hw) +static inline void i2s_ll_enable_loop_back(i2s_dev_t *hw, bool ena) { + hw->tx_conf.sig_loopback = ena; } #ifdef __cplusplus diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index f994add41b..df0a24bf0e 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,263 +12,269 @@ // See the License for the specific language governing permissions and // limitations under the License. + // The HAL layer for I2S (common part) #include "soc/soc.h" #include "soc/soc_caps.h" #include "hal/i2s_hal.h" -#define I2S_TX_PDM_FP_DEF 960 // Set to the recommended value(960) in TRM -#define I2S_RX_PDM_DSR_DEF 0 +#define I2S_MODE_I2S (I2S_MODE_MASTER|I2S_MODE_SLAVE|I2S_MODE_TX|I2S_MODE_RX) /*!< I2S normal mode*/ -void i2s_hal_set_tx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits) +/** + * @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. + */ +static void i2s_hal_clk_cal(uint32_t fsclk, uint32_t fbck, int bck_div, i2s_ll_clk_cal_t *cal) { - if (bits <= I2S_BITS_PER_SAMPLE_16BIT) { - i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); - } else { - i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3); - } - i2s_ll_set_tx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#if SOC_I2S_SUPPORTS_DMA_EQUAL - i2s_ll_set_tx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#endif -} - -void i2s_hal_set_rx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits) -{ - if (bits <= I2S_BITS_PER_SAMPLE_16BIT) { - i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); - } else { - i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3); - } - i2s_ll_set_rx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#if SOC_I2S_SUPPORTS_DMA_EQUAL - i2s_ll_set_rx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#endif -} - -void i2s_hal_set_in_link(i2s_hal_context_t *hal, uint32_t bytes_num, uint32_t addr) -{ - i2s_ll_set_in_link_addr(hal->dev, addr); - i2s_ll_set_rx_eof_num(hal->dev, bytes_num); -} - -#if SOC_I2S_SUPPORTS_PDM -void i2s_hal_tx_pdm_cfg(i2s_hal_context_t *hal, uint32_t fp, uint32_t fs) -{ - i2s_ll_tx_pdm_cfg(hal->dev, fp, fs); -} - -void i2s_hal_get_tx_pdm(i2s_hal_context_t *hal, uint32_t *fp, uint32_t *fs) -{ - i2s_ll_get_tx_pdm(hal->dev, fp, fs); -} - -void i2s_hal_rx_pdm_cfg(i2s_hal_context_t *hal, uint32_t dsr) -{ - i2s_ll_rx_pdm_cfg(hal->dev, dsr); -} - -void i2s_hal_get_rx_pdm(i2s_hal_context_t *hal, uint32_t *dsr) -{ - i2s_ll_get_rx_pdm(hal->dev, dsr); -} -#endif - -void i2s_hal_set_clk_div(i2s_hal_context_t *hal, int div_num, int div_a, int div_b, int tx_bck_div, int rx_bck_div) -{ - i2s_ll_set_clkm_div_num(hal->dev, div_num); - i2s_ll_set_clkm_div_a(hal->dev, div_a); - i2s_ll_set_clkm_div_b(hal->dev, div_b); - i2s_ll_set_tx_bck_div_num(hal->dev, tx_bck_div); - i2s_ll_set_rx_bck_div_num(hal->dev, rx_bck_div); -} - -void i2s_hal_set_tx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits) -{ - i2s_ll_set_tx_bits_mod(hal->dev, bits); -} - -void i2s_hal_set_rx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits) -{ - i2s_ll_set_rx_bits_mod(hal->dev, bits); -} - -void i2s_hal_reset(i2s_hal_context_t *hal) -{ - // Reset I2S TX/RX module first, and then, reset DMA and FIFO. - i2s_ll_reset_tx(hal->dev); - i2s_ll_reset_rx(hal->dev); - i2s_ll_reset_dma_in(hal->dev); - i2s_ll_reset_dma_out(hal->dev); - i2s_ll_reset_rx_fifo(hal->dev); - i2s_ll_reset_tx_fifo(hal->dev); -} - -void i2s_hal_start_tx(i2s_hal_context_t *hal) -{ - i2s_ll_start_out_link(hal->dev); - i2s_ll_start_tx(hal->dev); -} - -void i2s_hal_start_rx(i2s_hal_context_t *hal) -{ - i2s_ll_start_in_link(hal->dev); - i2s_ll_start_rx(hal->dev); -} - -void i2s_hal_stop_tx(i2s_hal_context_t *hal) -{ - i2s_ll_stop_out_link(hal->dev); - i2s_ll_stop_tx(hal->dev); -} - -void i2s_hal_stop_rx(i2s_hal_context_t *hal) -{ - i2s_ll_stop_in_link(hal->dev); - i2s_ll_stop_rx(hal->dev); -} - -void i2s_hal_format_config(i2s_hal_context_t *hal, const i2s_config_t *i2s_config) -{ - switch (i2s_config->communication_format) { - case I2S_COMM_FORMAT_STAND_MSB: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_format_msb_align(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_format_msb_align(hal->dev); - } - break; - case I2S_COMM_FORMAT_STAND_PCM_SHORT: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_pcm_long(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_pcm_long(hal->dev); - } - break; - case I2S_COMM_FORMAT_STAND_PCM_LONG: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_pcm_short(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_pcm_short(hal->dev); - } - break; - default: //I2S_COMM_FORMAT_STAND_I2S - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_format_philip(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_format_philip(hal->dev); - } - break; - } -} - -void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_config_t *i2s_config) -{ - //reset i2s - i2s_ll_reset_tx(hal->dev); - i2s_ll_reset_rx(hal->dev); - - //reset dma - i2s_ll_reset_dma_in(hal->dev); - i2s_ll_reset_dma_out(hal->dev); - - i2s_ll_enable_dma(hal->dev); - - i2s_ll_set_lcd_en(hal->dev, 0); - i2s_ll_set_camera_en(hal->dev, 0); - - i2s_ll_set_dscr_en(hal->dev, 0); - - i2s_ll_set_tx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left - i2s_ll_set_tx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel - i2s_ll_set_tx_mono(hal->dev, 0); - - i2s_ll_set_rx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left - i2s_ll_set_rx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel - i2s_ll_set_rx_mono(hal->dev, 0); - - i2s_ll_set_dscr_en(hal->dev, 1); //connect dma to fifo - - i2s_ll_stop_tx(hal->dev); - i2s_ll_stop_rx(hal->dev); - - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_msb_right(hal->dev, 0); - i2s_ll_set_tx_right_first(hal->dev, 0); - - i2s_ll_set_tx_slave_mod(hal->dev, 0); // Master - i2s_ll_set_tx_fifo_mod_force_en(hal->dev, 1); - - if (i2s_config->mode & I2S_MODE_SLAVE) { - i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave - } - } - - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_msb_right(hal->dev, 0); - i2s_ll_set_rx_right_first(hal->dev, 0); - i2s_ll_set_rx_slave_mod(hal->dev, 0); // Master - i2s_ll_set_rx_fifo_mod_force_en(hal->dev, 1); - - if (i2s_config->mode & I2S_MODE_SLAVE) { - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave - } - } - -#if SOC_I2S_SUPPORTS_PDM - if (!(i2s_config->mode & I2S_MODE_PDM)) { - i2s_ll_set_rx_pdm_en(hal->dev, 0); - i2s_ll_set_tx_pdm_en(hal->dev, 0); - } else { - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_tx_pdm_cfg(hal->dev, I2S_TX_PDM_FP_DEF, i2s_config->sample_rate/100); - } - if(i2s_config->mode & I2S_MODE_RX) { - i2s_ll_rx_pdm_cfg(hal->dev, I2S_RX_PDM_DSR_DEF); - } - // PDM mode have nothing to do with communication format configuration. + int ma = 0; + int mb = 0; + uint32_t mclk = fbck * bck_div; + cal->mclk_div = fsclk / mclk; + cal->bck_div = bck_div; + cal->a = 1; + cal->b = 0; + uint32_t freq_diff = fsclk - mclk * cal->mclk_div; + uint32_t min = ~0; + if (freq_diff == 0) { return; } -#endif - -#if SOC_I2S_SUPPORTS_ADC_DAC - if (i2s_config->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) { - if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_ll_build_in_dac_ena(hal->dev); + 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; + if (ma == mb) { + cal->a = a; + cal->b = b; + return; + } + if (abs((mb - ma)) < min) { + cal->a = a; + cal->b = b; + min = abs(mb - ma); + } } - if (i2s_config->mode & I2S_MODE_ADC_BUILT_IN) { - i2s_ll_build_in_adc_ena(hal->dev); - i2s_ll_set_rx_chan_mod(hal->dev, 1); - i2s_ll_set_rx_fifo_mod(hal->dev, 1); - i2s_ll_set_rx_mono(hal->dev, 0); - } - // Buildin ADC and DAC have nothing to do with communication format configuration. - return; } -#endif - - i2s_hal_format_config(hal, i2s_config); } -void i2s_hal_enable_master_mode(i2s_hal_context_t *hal) +void i2s_hal_set_clock_src(i2s_hal_context_t *hal, i2s_clock_src_t sel) { - i2s_ll_set_tx_slave_mod(hal->dev, 0); //MASTER Slave - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave + i2s_ll_tx_clk_set_src(hal->dev, sel); + i2s_ll_rx_clk_set_src(hal->dev, sel); } -void i2s_hal_enable_slave_mode(i2s_hal_context_t *hal) +void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor) { - i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave + 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); +} + +void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor) +{ + 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); +} + +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 +} + +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 } void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num) { //Get hardware instance. hal->dev = I2S_LL_GET_HW(i2s_num); + i2s_ll_enable_clock(hal->dev); +} + +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 */ + i2s_ll_tx_set_pdm_prescale(hal->dev, 0); + /* set pdm tx default sacle of high pass filter */ + i2s_ll_tx_set_pdm_hp_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); + /* set pdm tx default sacle of low pass filter */ + i2s_ll_tx_set_pdm_lp_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); + /* set pdm tx default sacle of sinc filter */ + i2s_ll_tx_set_pdm_sinc_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); + /* set pdm tx default sacle of sigma-delta filter */ + i2s_ll_tx_set_pdm_sd_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); + /* set pdm tx sample rate */ + i2s_ll_tx_set_pdm_fpfs(hal->dev, 960, sample_rate / 100); + +#if SOC_I2S_SUPPORTS_PDM_CODEC + /* enable pdm high pass filter */ + i2s_ll_tx_enable_pdm_hp_filter(hal->dev, true); + /* set pdm tx high pass filter parameters */ + i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, 6); + i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, 7); + /* enable pdm sigma-delta codec */ + i2s_ll_tx_enable_pdm_sd_codec(hal->dev, true); + /* set pdm tx sigma-delta codec dither */ + i2s_ll_tx_set_pdm_sd_dither(hal->dev, 0); + i2s_ll_tx_set_pdm_sd_dither2(hal->dev, 0); + +#endif // SOC_I2S_SUPPORTS_PDM_CODEC +#endif // SOC_I2S_SUPPORTS_PDM_TX +} + +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 +} + + +void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +{ + /* disable pdm tx mode */ + i2s_ll_tx_enable_pdm(hal->dev, false); + +#if SOC_I2S_SUPPORTS_TDM + i2s_ll_tx_enable_clock(hal->dev); + i2s_ll_tx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default + i2s_ll_mclk_use_tx_clk(hal->dev); + + i2s_ll_tx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask); + i2s_ll_tx_enable_left_align(hal->dev, hal_cfg->left_align); + i2s_ll_tx_enable_big_endian(hal->dev, hal_cfg->big_edin); + i2s_ll_tx_set_bit_order(hal->dev, hal_cfg->bit_order_msb); + i2s_ll_tx_set_skip_mask(hal->dev, hal_cfg->skip_msk); +#else + i2s_ll_tx_enable_msb_right(hal->dev, false); + i2s_ll_tx_enable_right_first(hal->dev, false); + i2s_ll_tx_force_enable_fifo_mod(hal->dev, true); +#endif +} + +void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +{ + /* disable pdm rx mode */ + i2s_ll_rx_enable_pdm(hal->dev, false); + +#if SOC_I2S_SUPPORTS_TDM + i2s_ll_rx_enable_clock(hal->dev); + i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default + i2s_ll_mclk_use_rx_clk(hal->dev); + + i2s_ll_rx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask); + i2s_ll_rx_enable_left_align(hal->dev, hal_cfg->left_align); + i2s_ll_rx_enable_big_endian(hal->dev, hal_cfg->big_edin); + i2s_ll_rx_set_bit_order(hal->dev, hal_cfg->bit_order_msb); +#else + i2s_ll_rx_enable_msb_right(hal->dev, false); + i2s_ll_rx_enable_right_first(hal->dev, false); + i2s_ll_rx_force_enable_fifo_mod(hal->dev, true); +#endif +} + +static uint32_t i2s_hal_get_ws_bit(i2s_comm_format_t fmt, uint32_t chan_num, uint32_t chan_bits) +{ + switch (fmt) { + case I2S_COMM_FORMAT_STAND_MSB: + return chan_num * chan_bits / 2; + case I2S_COMM_FORMAT_STAND_PCM_SHORT: + return 1; + case I2S_COMM_FORMAT_STAND_PCM_LONG: + return chan_bits; + default: //I2S_COMM_FORMAT_STAND_I2S + return chan_num * chan_bits / 2; + } +} + +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; + + /* Set channel number and valid data bits */ +#if SOC_I2S_SUPPORTS_TDM + chan_num = hal_cfg->total_chan; + i2s_ll_tx_set_chan_num(hal->dev, chan_num); +#endif + i2s_ll_tx_set_sample_bit(hal->dev, chan_bits, data_bits); + + /* Set communication format */ + bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; + uint32_t ws_width = i2s_hal_get_ws_bit(hal_cfg->comm_fmt, chan_num, chan_bits); + i2s_ll_tx_enable_msb_shift(hal->dev, shift_en); + i2s_ll_tx_set_ws_width(hal->dev, ws_width); +#if SOC_I2S_SUPPORTS_TDM + i2s_ll_tx_set_half_sample_bit(hal->dev, chan_num * chan_bits / 2); +#endif +} + +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; + +#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); + + /* Set communication format */ + bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; + uint32_t ws_width = i2s_hal_get_ws_bit(hal_cfg->comm_fmt, chan_num, chan_bits); + i2s_ll_rx_enable_msb_shift(hal->dev, shift_en); + i2s_ll_rx_set_ws_width(hal->dev, ws_width); +#if SOC_I2S_SUPPORTS_TDM + i2s_ll_rx_set_half_sample_bit(hal->dev, chan_num * chan_bits / 2); +#endif +} + +void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +{ + 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 (hal_cfg->mode & I2S_MODE_PDM) { + /* Set tx pdm mode */ + i2s_hal_tx_set_pdm_mode_default(hal, hal_cfg->sample_rate); + } else { + /* Set tx common mode */ + i2s_hal_tx_set_common_mode(hal, hal_cfg); + i2s_hal_tx_set_channel_style(hal, hal_cfg); + } + } + 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 (hal_cfg->mode & I2S_MODE_PDM) { + /* Set rx pdm mode */ + i2s_hal_rx_set_pdm_mode_default(hal); + } else { + /* Set rx common mode */ + i2s_hal_rx_set_common_mode(hal, hal_cfg); + i2s_hal_rx_set_channel_style(hal, hal_cfg); + } + } } diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index 6964bfdf94..d0446f7a47 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/components/hal/include/hal/i2s_hal.h @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,13 +25,45 @@ #include "soc/i2s_periph.h" #include "soc/soc_caps.h" -#include "hal/i2s_ll.h" #include "hal/i2s_types.h" +#include "hal/i2s_ll.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief I2S channel bits configurations + * + */ +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; + +/** + * @brief I2S HAL configurations + */ +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 total_chan; /*!< Total number of I2S channels */ + 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, status) - -/** - * @brief Clear I2S interrupt status - * - * @param hal Context of the HAL layer - * @param mask interrupt status mask - */ -#define i2s_hal_clear_intr_status(hal, mask) i2s_ll_clear_intr_status((hal)->dev, mask) - -/** - * @brief Get I2S out eof des address - * - * @param hal Context of the HAL layer - * @param addr out eof des address - */ -#define i2s_hal_get_out_eof_des_addr(hal, addr) i2s_ll_get_out_eof_des_addr((hal)->dev, addr) - -/** - * @brief Get I2S in eof des address - * - * @param hal Context of the HAL layer - * @param addr in eof des address - */ -#define i2s_hal_get_in_eof_des_addr(hal, addr) i2s_ll_get_in_eof_des_addr((hal)->dev, addr) - -/** - * @brief Enable I2S rx interrupt + * @brief Reset I2S TX channel * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_rx_intr(hal) i2s_ll_enable_rx_intr((hal)->dev) +#define i2s_hal_reset_tx(hal) i2s_ll_tx_reset((hal)->dev) /** - * @brief Disable I2S rx interrupt + * @brief Reset I2S TX fifo * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_rx_intr(hal) i2s_ll_disable_rx_intr((hal)->dev) +#define i2s_hal_reset_tx_fifo(hal) i2s_ll_tx_reset_fifo((hal)->dev) /** - * @brief Disable I2S tx interrupt + * @brief Reset I2S RX channel * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_tx_intr(hal) i2s_ll_disable_tx_intr((hal)->dev) +#define i2s_hal_reset_rx(hal) i2s_ll_rx_reset((hal)->dev) /** - * @brief Enable I2S tx interrupt + * @brief Reset I2S RX fifo * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_tx_intr(hal) i2s_ll_enable_tx_intr((hal)->dev) +#define i2s_hal_reset_rx_fifo(hal) i2s_ll_rx_reset_fifo((hal)->dev) /** - * @brief Set I2S tx mode - * - * @param hal Context of the HAL layer - * @param ch i2s channel - * @param bits bits per sample - */ -void i2s_hal_set_tx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits); - -/** - * @brief Set I2S rx mode - * - * @param hal Context of the HAL layer - * @param ch i2s channel - * @param bits bits per sample - */ -void i2s_hal_set_rx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits); - -/** - * @brief Set I2S out link address - * - * @param hal Context of the HAL layer - * @param addr out link address - */ -#define i2s_hal_set_out_link_addr(hal, addr) i2s_ll_set_out_link_addr((hal)->dev, addr) - -/** - * @brief Set I2S out link address - * - * @param hal Context of the HAL layer - * @param addr out link address - */ -#define i2s_hal_set_out_link_addr(hal, addr) i2s_ll_set_out_link_addr((hal)->dev, addr) - -/** - * @brief Set I2S out link address - * - * @param hal Context of the HAL layer - * @param addr out link address - */ -#define i2s_hal_set_out_link_addr(hal, addr) i2s_ll_set_out_link_addr((hal)->dev, addr) - -/** - * @brief Set I2S in link - * - * @param hal Context of the HAL layer - * @param rx_eof_num in link eof num - * @param addr in link address - */ -void i2s_hal_set_in_link(i2s_hal_context_t *hal, uint32_t rx_eof_num, uint32_t addr); - -/** - * @brief Set I2S clk div - * - * @param hal Context of the HAL layer - * @param div_num i2s clkm div num - * @param div_a i2s clkm div a - * @param div_b i2s clkm div b - * @param tx_bck_div tx bck div num - * @param rx_bck_div rx bck div num - */ -void i2s_hal_set_clk_div(i2s_hal_context_t *hal, int div_num, int div_a, int div_b, int tx_bck_div, int rx_bck_div); - -/** - * @brief Set I2S clock sel - * - * @param hal Context of the HAL layer - * @param sel clock sel - */ -#define i2s_hal_set_clock_sel(hal, sel) i2s_ll_set_clk_sel((hal)->dev, sel) - -/** - * @brief Set I2S tx bits mod - * - * @param hal Context of the HAL layer - * @param bits bit width per sample. - */ -void i2s_hal_set_tx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits); - -/** - * @brief Set I2S rx bits mod - * - * @param hal Context of the HAL layer - * @param bits bit width per sample. - */ -void i2s_hal_set_rx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits); - -/** - * @brief Reset I2S TX & RX module, including DMA and FIFO - * - * @param hal Context of the HAL layer - */ -void i2s_hal_reset(i2s_hal_context_t *hal); - -/** - * @brief Start I2S tx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_start_tx(i2s_hal_context_t *hal); - -/** - * @brief Start I2S rx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_start_rx(i2s_hal_context_t *hal); - -/** - * @brief Stop I2S tx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_stop_tx(i2s_hal_context_t *hal); - -/** - * @brief Stop I2S rx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_stop_rx(i2s_hal_context_t *hal); - -/** - * @brief Config I2S param - * - * @param hal Context of the HAL layer - * @param i2s_config I2S configurations - see i2s_config_t struct - */ -void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_config_t *i2s_config); - -/** - * @brief Enable I2S sig loopback - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_enable_sig_loopback(hal) i2s_ll_set_sig_loopback((hal)->dev, 1) - -/** - * @brief Enable I2S master mode - * - * @param hal Context of the HAL layer - */ -void i2s_hal_enable_master_mode(i2s_hal_context_t *hal); - -/** - * @brief Enable I2S slave mode - * - * @param hal Context of the HAL layer - */ -void i2s_hal_enable_slave_mode(i2s_hal_context_t *hal); - -/** - * @brief Init the I2S hal and set the I2S to the default configuration. This function should be called first before other hal layer function is called + * @brief Init the I2S hal. 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) */ void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num); -#if SOC_I2S_SUPPORTS_PDM /** - * @brief Set I2S tx pdm + * @brief Configure I2S source clock * * @param hal Context of the HAL layer - * @param fp tx pdm fp - * @param fs tx pdm fs + * @param sel The source clock index */ -void i2s_hal_tx_pdm_cfg(i2s_hal_context_t *hal, uint32_t fp, uint32_t fs); +void i2s_hal_set_clock_src(i2s_hal_context_t *hal, i2s_clock_src_t sel); /** - * @brief Get I2S tx pdm + * @brief Set Tx channel style * * @param hal Context of the HAL layer - * @param dsr rx pdm dsr + * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` */ -void i2s_hal_rx_pdm_cfg(i2s_hal_context_t *hal, uint32_t dsr); +void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); /** - * @brief Get I2S tx pdm configuration + * @brief Set Rx channel style * * @param hal Context of the HAL layer - * @param fp Pointer to receive tx PDM fp configuration - * @param fs Pointer to receive tx PDM fs configuration + * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` */ -void i2s_hal_get_tx_pdm(i2s_hal_context_t *hal, uint32_t *fp, uint32_t *fs); +void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); /** - * @brief Get I2S rx pdm configuration + * @brief Config I2S param * * @param hal Context of the HAL layer - * @param dsr rx pdm dsr + * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` */ -void i2s_hal_get_rx_pdm(i2s_hal_context_t *hal, uint32_t *dsr); +void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); + +/** + * @brief Enable I2S master full-duplex mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_enable_master_fd_mode(i2s_hal_context_t *hal); + +/** + * @brief Enable I2S slave full-duplex mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_enable_slave_fd_mode(i2s_hal_context_t *hal); + +/** + * @brief Start I2S tx + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_start_tx(hal) i2s_ll_tx_start((hal)->dev) + +/** + * @brief Start I2S rx + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_start_rx(hal) i2s_ll_rx_start((hal)->dev) + +/** + * @brief Stop I2S tx + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_stop_tx(hal) i2s_ll_tx_stop((hal)->dev) + +/** + * @brief Stop I2S rx + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_stop_rx(hal) i2s_ll_rx_stop((hal)->dev) + +/** + * @brief Set the received data length to trigger `in_suc_eof` interrupt. + * + * @param hal Context of the HAL layer + * @param eof_byte The byte length that trigger in_suc_eof interrupt. + */ +#define i2s_hal_set_rx_eof_num(hal, eof_byte) i2s_ll_rx_set_eof_num((hal)->dev, eof_byte) + +/** + * @brief Set I2S TX sample bit + * + * @param hal Context of the HAL layer + * @param chan_bit I2S TX chan bit + * @param data_bit The sample data bit length. + */ +#define i2s_hal_set_tx_sample_bit(hal, chan_bit, data_bit) i2s_ll_tx_set_sample_bit((hal)->dev, chan_bit, data_bit) + +/** + * @brief Set I2S RX sample bit + * + * @param hal Context of the HAL layer + * @param chan_bit I2S RX chan bit + * @param data_bit The sample data bit length. + */ +#define i2s_hal_set_rx_sample_bit(hal, chan_bit, data_bit) i2s_ll_rx_set_sample_bit((hal)->dev, chan_bit, data_bit) + +/** + * @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 + */ +void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbck, int factor); + +/** + * @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 + */ +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) + +/** + * @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 + +/** + * @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) + +/** + * @brief Set I2S configuration for common TX mode + * @note Common mode is for non-PDM mode like philip/MSB/PCM + * + * @param hal Context of the HAL layer + * @param hal_cfg hal configuration structure + */ +void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); + +/** + * @brief Set I2S configuration for common RX mode + * @note Common mode is for non-PDM mode like philip/MSB/PCM + * + * @param hal Context of the HAL layer + * @param hal_cfg hal configuration structure + */ +void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); + +#if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief Configure I2S TX PDM sample rate + * Fpdm = 64*Fpcm*fp/fs + * + * @param hal Context of the HAL layer + * @param fp TX PDM fp paramater configuration + * @param fs TX PDM fs paramater configuration + */ +#define i2s_hal_set_tx_pdm_fpfs(hal, fp, fs) i2s_ll_tx_set_pdm_fpfs((hal)->dev, fp, fs) + +/** + * @brief Get I2S TX PDM fp + * + * @param hal Context of the HAL layer + * @return + * - fp configuration paramater + */ +#define i2s_hal_get_tx_pdm_fp(hal) i2s_ll_tx_get_pdm_fp((hal)->dev) + +/** + * @brief Get I2S TX PDM fs + * + * @param hal Context of the HAL layer + * @return + * - fs configuration paramater + */ +#define i2s_hal_get_tx_pdm_fs(hal) i2s_ll_tx_get_pdm_fs((hal)->dev) + +/** + * @brief Set I2S default configuration for PDM TX mode + * + * @param hal Context of the HAL layer + * @param sample_rate PDM sample rate + */ +void i2s_hal_tx_set_pdm_mode_default(i2s_hal_context_t *hal, uint32_t sample_rate); +#endif + +#if SOC_I2S_SUPPORTS_PDM_RX + +/** + * @brief Configure RX PDM downsample + * + * @param hal Context of the HAL layer + * @param dsr PDM downsample configuration paramater + */ +#define i2s_hal_set_rx_pdm_dsr(hal, dsr) i2s_ll_rx_set_pdm_dsr((hal)->dev, dsr) + +/** + * @brief Get RX PDM downsample configuration + * + * @param hal Context of the HAL layer + * @param dsr Pointer to accept PDM downsample configuration + */ +#define i2s_hal_get_rx_pdm_dsr(hal, dsr) i2s_ll_rx_get_pdm_dsr((hal)->dev, dsr) + +/** + * @brief Set I2S default configuration for PDM R mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); +#endif + +#if !SOC_GDMA_SUPPORTED +/** + * @brief Enable I2S TX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_enable_tx_dma(hal) i2s_ll_enable_dma((hal)->dev,true) + +/** + * @brief Enable I2S RX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_enable_rx_dma(hal) i2s_ll_enable_dma((hal)->dev,true) + +/** + * @brief Disable I2S TX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_tx_dma(hal) i2s_ll_enable_dma((hal)->dev,false) + +/** + * @brief Disable I2S RX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_rx_dma(hal) i2s_ll_enable_dma((hal)->dev,false) + +/** + * @brief Get I2S interrupt status + * + * @param hal Context of the HAL layer + * @return + * - module interrupt status + */ +#define i2s_hal_get_intr_status(hal) i2s_ll_get_intr_status((hal)->dev) + +/** + * @brief Get I2S interrupt status + * + * @param hal Context of the HAL layer + * @param mask Interrupt mask to be cleared. + */ +#define i2s_hal_clear_intr_status(hal, mask) i2s_ll_clear_intr_status((hal)->dev, mask) + +/** + * @brief Enable I2S RX interrupt + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_enable_rx_intr(hal) i2s_ll_rx_enable_intr((hal)->dev) + +/** + * @brief Disable I2S RX interrupt + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_rx_intr(hal) i2s_ll_rx_disable_intr((hal)->dev) + +/** + * @brief Disable I2S TX interrupt + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_tx_intr(hal) i2s_ll_tx_disable_intr((hal)->dev) + +/** + * @brief Enable I2S TX interrupt + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_enable_tx_intr(hal) i2s_ll_tx_enable_intr((hal)->dev) + +/** + * @brief Configure TX DMA descriptor address and start TX DMA + * + * @param hal Context of the HAL layer + * @param link_addr DMA descriptor link address. + */ +#define i2s_hal_start_tx_link(hal, link_addr) i2s_ll_tx_start_link((hal)->dev, link_addr) + +/** + * @brief Configure RX DMA descriptor address and start RX DMA + * + * @param hal Context of the HAL layer + * @param link_addr DMA descriptor link address. + */ +#define i2s_hal_start_rx_link(hal, link_addr) i2s_ll_rx_start_link((hal)->dev, link_addr) + +/** + * @brief Stop TX DMA link + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_stop_tx_link(hal) i2s_ll_tx_stop_link((hal)->dev) + +/** + * @brief Stop RX DMA link + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_stop_rx_link(hal) i2s_ll_rx_stop_link((hal)->dev) + +/** + * @brief Reset RX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_reset_rxdma(hal) i2s_ll_rx_reset_dma((hal)->dev) + +/** + * @brief Reset TX DMA + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_reset_txdma(hal) i2s_ll_tx_reset_dma((hal)->dev) + +/** + * @brief Get I2S out eof descriptor address + * + * @param hal Context of the HAL layer + * @param addr Pointer to accept out eof des address + */ +#define i2s_hal_get_out_eof_des_addr(hal, addr) i2s_ll_tx_get_eof_des_addr((hal)->dev, addr) + +/** + * @brief Get I2S in suc eof descriptor address + * + * @param hal Context of the HAL layer + * @param addr Pointer to accept in suc eof des address + */ +#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 +/** + * @brief Enable Builtin DAC + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_enable_builtin_dac(hal) i2s_ll_enable_builtin_dac((hal)->dev, true); + +/** + * @brief Enable Builtin ADC + * + * @param hal Context of the HAL layer + */ +#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 + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_disable_builtin_adc(hal) i2s_ll_enable_builtin_adc((hal)->dev, false); #endif #ifdef __cplusplus diff --git a/components/hal/include/hal/i2s_types.h b/components/hal/include/hal/i2s_types.h index ba150cc7b3..630aedb149 100644 --- a/components/hal/include/hal/i2s_types.h +++ b/components/hal/include/hal/i2s_types.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ #include #include +#include #include #include "soc/soc_caps.h" @@ -23,35 +24,60 @@ extern "C" { #endif -/** - * @brief I2S port number, the max port number is (I2S_NUM_MAX -1). - */ -typedef enum { - I2S_NUM_0 = 0, /*!< I2S port 0 */ -#if SOC_I2S_NUM > 1 - I2S_NUM_1 = 1, /*!< I2S port 1 */ -#endif - I2S_NUM_MAX, /*!< I2S port max */ -} i2s_port_t; - /** * @brief I2S bit width per sample. * */ typedef enum { - I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< I2S bits per sample: 8-bits*/ - I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< I2S bits per sample: 16-bits*/ - I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< I2S bits per sample: 24-bits*/ - I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< I2S bits per sample: 32-bits*/ + I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< data bit-width: 8 */ + I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< data bit-width: 16 */ + I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< data bit-width: 24 */ + I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< data bit-width: 32 */ } i2s_bits_per_sample_t; +/** + * @brief I2S bit width per chan. + * + */ +typedef enum { + I2S_BITS_PER_CHAN_DEFAULT = (0), /*!< channel bit-width equals to data bit-width */ + I2S_BITS_PER_CHAN_8BIT = (8), /*!< channel bit-width: 8 */ + I2S_BITS_PER_CHAN_16BIT = (16), /*!< channel bit-width: 16 */ + I2S_BITS_PER_CHAN_24BIT = (24), /*!< channel bit-width: 24 */ + I2S_BITS_PER_CHAN_32BIT = (32), /*!< channel bit-width: 32 */ +} i2s_bits_per_chan_t; + /** * @brief I2S channel. * */ typedef enum { - I2S_CHANNEL_MONO = 1, /*!< I2S 1 channel (mono)*/ - I2S_CHANNEL_STEREO = 2 /*!< I2S 2 channel (stereo)*/ + I2S_CHANNEL_MONO = (0x01 << 31) | 0x03, /*!< I2S channel (mono), two channel enabled. In this mode, you only need to send one channel data but the fifo will copy same data for another channel automatically, then both channels will transmit same data. The highest bit is for differentiating I2S_CHANNEL_STEREO since they both use two channels */ + I2S_CHANNEL_STEREO = 0x03, /*!< I2S channel (stereo), two channel enabled. In this mode, two channels will transmit different data. */ +#if SOC_I2S_SUPPORTS_TDM + // Bit map of active chan. + // There are 16 channels in TDM mode. + // For TX module, only the active channel send the audio data, the inactive channel send a constant(configurable) or will be skiped if 'skip_msk' is set. + // For RX module, only receive the audio data in active channels, the data in inactive channels will be ignored. + // the bit map of active channel can not exceed (0x1< 0, then the clock output for i2s is fixed and equal to the fixed_mclk value.*/ -} i2s_config_t; - -/** - * @brief I2S event types - * + * @brief The multiple of mclk to sample rate */ typedef enum { - I2S_EVENT_DMA_ERROR, - I2S_EVENT_TX_DONE, /*!< I2S DMA finish sent 1 buffer*/ - I2S_EVENT_RX_DONE, /*!< I2S DMA finish received 1 buffer*/ - I2S_EVENT_MAX, /*!< I2S event max index*/ -} i2s_event_type_t; + I2S_MCLK_MULTIPLE_DEFAULT = 0, /*!< Default value. mclk = sample_rate * 256 */ + I2S_MCLK_MULTIPLE_128 = 128, /*!< mclk = sample_rate * 128 */ + I2S_MCLK_MULTIPLE_256 = 256, /*!< mclk = sample_rate * 256 */ + I2S_MCLK_MULTIPLE_384 = 384, /*!< mclk = sample_rate * 384 */ +} i2s_mclk_multiple_t; #if SOC_I2S_SUPPORTS_ADC_DAC /** * @brief I2S DAC mode for i2s_set_dac_mode. * - * @note PDM and built-in DAC functions are only supported on I2S0 for current ESP32 chip. + * @note Built-in DAC functions are only supported on I2S0 for current ESP32 chip. */ typedef enum { I2S_DAC_CHANNEL_DISABLE = 0, /*!< Disable I2S built-in DAC signals*/ @@ -159,27 +167,21 @@ typedef enum { } i2s_dac_mode_t; #endif //SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_PCM /** - * @brief Event structure used in I2S event queue + * @brief A/U-law decompress or compress configuration. * */ -typedef struct { - i2s_event_type_t type; /*!< I2S event type */ - size_t size; /*!< I2S data size for I2S_DATA event*/ -} i2s_event_t; +typedef enum { + I2S_PCM_DISABLE = 0, /*!< Disable A/U law decopress or compress*/ + I2S_PCM_A_DECOMPRESS, /*!< A-law decompress*/ + I2S_PCM_A_COMPRESS, /*!< A-law compress*/ + I2S_PCM_U_DECOMPRESS, /*!< U-law decompress*/ + I2S_PCM_U_COMPRESS, /*!< U-law compress*/ +} i2s_pcm_compress_t; +#endif -/** - * @brief I2S pin number for i2s_set_pin - * - */ -typedef struct { - int bck_io_num; /*!< BCK in out pin*/ - int ws_io_num; /*!< WS in out pin*/ - int data_out_num; /*!< DATA out pin*/ - int data_in_num; /*!< DATA in pin*/ -} i2s_pin_config_t; - -#if SOC_I2S_SUPPORTS_PDM +#if SOC_I2S_SUPPORTS_PDM_RX /** * @brief I2S PDM RX downsample mode */ @@ -188,17 +190,16 @@ typedef enum { I2S_PDM_DSR_16S, /*!< downsampling number is 16 for PDM RX mode*/ I2S_PDM_DSR_MAX, } i2s_pdm_dsr_t; - -/** - * @brief PDM PCM convter enable/disable. - * - */ -typedef enum { - PDM_PCM_CONV_ENABLE, /*!< Enable PDM PCM convert*/ - PDM_PCM_CONV_DISABLE, /*!< Disable PDM PCM convert*/ -} pdm_pcm_conv_t; #endif +#if SOC_I2S_SUPPORTS_PDM_TX +typedef enum { + I2S_PDM_SIG_SCALING_DIV_2 = 0, /*!< I2S TX PDM sigmadelta signal scaling: /2 */ + I2S_PDM_SIG_SCALING_MUL_1 = 1, /*!< I2S TX PDM sigmadelta signal scaling: x1 */ + I2S_PDM_SIG_SCALING_MUL_2 = 2, /*!< I2S TX PDM sigmadelta signal scaling: x2 */ + I2S_PDM_SIG_SCALING_MUL_4 = 3, /*!< I2S TX PDM sigmadelta signal scaling: x4 */ +} i2s_pdm_sig_scale_t; +#endif #ifdef __cplusplus } diff --git a/components/soc/esp32/i2s_periph.c b/components/soc/esp32/i2s_periph.c index 84985aaf6a..cf6d4ba992 100644 --- a/components/soc/esp32/i2s_periph.c +++ b/components/soc/esp32/i2s_periph.c @@ -1,9 +1,9 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -20,31 +20,25 @@ */ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { - .o_bck_in_sig = I2S0O_BCK_IN_IDX, - .o_ws_in_sig = I2S0O_WS_IN_IDX, - .o_bck_out_sig = I2S0O_BCK_OUT_IDX, - .o_ws_out_sig = I2S0O_WS_OUT_IDX, - .o_data_out_sig = I2S0O_DATA_OUT23_IDX, - .i_bck_in_sig = I2S0I_BCK_OUT_IDX, - .i_ws_in_sig = I2S0I_WS_OUT_IDX, - .i_bck_out_sig = I2S0I_BCK_IN_IDX, - .i_ws_out_sig = I2S0I_WS_IN_IDX, - .i_data_in_sig = I2S0I_DATA_IN15_IDX, - .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, + .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, + .data_out_sig = I2S0O_DATA_OUT23_IDX, + .data_in_sig = I2S0I_DATA_IN15_IDX, + .irq = ETS_I2S0_INTR_SOURCE, + .module = PERIPH_I2S0_MODULE, }, { - .o_bck_in_sig = I2S1O_BCK_IN_IDX, - .o_ws_in_sig = I2S1O_WS_IN_IDX, - .o_bck_out_sig = I2S1O_BCK_OUT_IDX, - .o_ws_out_sig = I2S1O_WS_OUT_IDX, - .o_data_out_sig = I2S1O_DATA_OUT23_IDX, - .i_bck_in_sig = I2S1I_BCK_OUT_IDX, - .i_ws_in_sig = I2S1I_WS_OUT_IDX, - .i_bck_out_sig = I2S1I_BCK_IN_IDX, - .i_ws_out_sig = I2S1I_WS_IN_IDX, - .i_data_in_sig = I2S1I_DATA_IN15_IDX, - .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_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, + .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 dd6f994b1a..544209a925 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -136,12 +136,11 @@ /*-------------------------- I2S CAPS ----------------------------------------*/ // ESP32 have 2 I2S #define SOC_I2S_NUM (2) - -#define SOC_I2S_SUPPORTS_PDM (1) // ESP32 support PDM +#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_MAX_BUFFER_SIZE (4 * 1024 * 1024) //the maximum RAM can be allocated - +#define SOC_I2S_SUPPORTS_APLL (1)// ESP32 support APLL #define SOC_I2S_APLL_MIN_FREQ (250000000) #define SOC_I2S_APLL_MAX_FREQ (500000000) #define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware diff --git a/components/soc/esp32c3/i2s_periph.c b/components/soc/esp32c3/i2s_periph.c index b20ff7fbf8..e654a0c62e 100644 --- a/components/soc/esp32c3/i2s_periph.c +++ b/components/soc/esp32c3/i2s_periph.c @@ -20,19 +20,14 @@ */ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { - // TODO ESP32-C3 IDF-2098 - - // .o_bck_in_sig = I2S0O_BCK_IN_IDX, - // .o_ws_in_sig = I2S0O_WS_IN_IDX, - // .o_bck_out_sig = I2S0O_BCK_OUT_IDX, - // .o_ws_out_sig = I2S0O_WS_OUT_IDX, - // .o_data_out_sig = I2S0O_SD_OUT_IDX, - // .i_bck_in_sig = I2S0I_BCK_OUT_IDX, - // .i_ws_in_sig = I2S0I_WS_OUT_IDX, - // .i_bck_out_sig = I2S0I_BCK_IN_IDX, - // .i_ws_out_sig = I2S0I_WS_IN_IDX, - // .i_data_in_sig = I2S0I_SD_IN_IDX, - .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, + .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, + .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/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index c792018c96..cb25945ed5 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -109,11 +109,10 @@ /*-------------------------- I2S CAPS ----------------------------------------*/ #define SOC_I2S_NUM (1) - -#define SOC_I2S_APLL_MIN_FREQ (250000000) -#define SOC_I2S_APLL_MAX_FREQ (500000000) -#define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware -#define SOC_I2S_MAX_BUFFER_SIZE (4 * 1024 * 1024) //the maximum RAM can be allocated +#define SOC_I2S_SUPPORTS_PCM (1) +#define SOC_I2S_SUPPORTS_PDM_TX (1) +#define SOC_I2S_SUPPORTS_PDM_CODEC (1) +#define SOC_I2S_SUPPORTS_TDM (1) /*-------------------------- LEDC CAPS ---------------------------------------*/ #define SOC_LEDC_SUPPORT_XTAL_CLOCK (1) diff --git a/components/soc/esp32c3/ld/esp32c3.peripherals.ld b/components/soc/esp32c3/ld/esp32c3.peripherals.ld index 494cbb9b6b..710ae9aeea 100644 --- a/components/soc/esp32c3/ld/esp32c3.peripherals.ld +++ b/components/soc/esp32c3/ld/esp32c3.peripherals.ld @@ -7,7 +7,7 @@ PROVIDE ( SIGMADELTA = 0x60004f00 ); PROVIDE ( RTCCNTL = 0x60008000 ); PROVIDE ( RTCIO = 0x60008400 ); PROVIDE ( HINF = 0x6000B000 ); -PROVIDE ( I2S1 = 0x6002d000 ); +PROVIDE ( I2S0 = 0x6002d000 ); PROVIDE ( I2C0 = 0x60013000 ); PROVIDE ( UHCI0 = 0x60014000 ); PROVIDE ( UHCI1 = 0x6000c000 ); diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 64fa8dd3b9..9b7f87eebc 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -106,11 +106,10 @@ /*-------------------------- I2S CAPS ----------------------------------------*/ #define SOC_I2S_NUM (1) - -#define SOC_I2S_APLL_MIN_FREQ (250000000) -#define SOC_I2S_APLL_MAX_FREQ (500000000) -#define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware -#define SOC_I2S_MAX_BUFFER_SIZE (4 * 1024 * 1024) //the maximum RAM can be allocated +#define SOC_I2S_SUPPORTS_PCM (1) +#define SOC_I2S_SUPPORTS_PDM_TX (1) +#define SOC_I2S_SUPPORTS_PDM_CODEC (1) +#define SOC_I2S_SUPPORTS_TDM (1) /*-------------------------- LEDC CAPS ---------------------------------------*/ #define SOC_LEDC_SUPPORT_XTAL_CLOCK (1) diff --git a/components/soc/esp32h2/ld/esp32h2.peripherals.ld b/components/soc/esp32h2/ld/esp32h2.peripherals.ld index 38e6b58258..ee3154ad43 100644 --- a/components/soc/esp32h2/ld/esp32h2.peripherals.ld +++ b/components/soc/esp32h2/ld/esp32h2.peripherals.ld @@ -7,7 +7,7 @@ PROVIDE ( SIGMADELTA = 0x60004f00 ); PROVIDE ( RTCCNTL = 0x60008000 ); PROVIDE ( RTCIO = 0x60008400 ); PROVIDE ( HINF = 0x6000B000 ); -PROVIDE ( I2S1 = 0x6002d000 ); +PROVIDE ( I2S0 = 0x6002d000 ); PROVIDE ( I2C0 = 0x60013000 ); PROVIDE ( UHCI0 = 0x60014000 ); PROVIDE ( UHCI1 = 0x6000c000 ); diff --git a/components/soc/esp32s2/i2s_periph.c b/components/soc/esp32s2/i2s_periph.c index 5029a68c8a..83700ed886 100644 --- a/components/soc/esp32s2/i2s_periph.c +++ b/components/soc/esp32s2/i2s_periph.c @@ -1,9 +1,9 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -20,17 +20,14 @@ */ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { - .o_bck_in_sig = I2S0O_BCK_IN_IDX, - .o_ws_in_sig = I2S0O_WS_IN_IDX, - .o_bck_out_sig = I2S0O_BCK_OUT_IDX, - .o_ws_out_sig = I2S0O_WS_OUT_IDX, - .o_data_out_sig = I2S0O_DATA_OUT23_IDX, - .i_bck_in_sig = I2S0I_BCK_OUT_IDX, - .i_ws_in_sig = I2S0I_WS_OUT_IDX, - .i_bck_out_sig = I2S0I_BCK_IN_IDX, - .i_ws_out_sig = I2S0I_WS_IN_IDX, - .i_data_in_sig = I2S0I_DATA_IN15_IDX, - .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, + .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, + .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/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 0f633b8ef7..30b85dc4b2 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -135,13 +135,9 @@ #define SOC_I2C_SUPPORT_APB (1) /*-------------------------- I2S CAPS ----------------------------------------*/ -// ESP32-S2 have 2 I2S +// ESP32-S2 have 1 I2S #define SOC_I2S_NUM (1) - -#define SOC_I2S_SUPPORTS_DMA_EQUAL (1) // ESP32-S2 need dma equal - -#define SOC_I2S_MAX_BUFFER_SIZE (4 * 1024 * 1024) //the maximum RAM can be allocated - +#define SOC_I2S_SUPPORTS_APLL (1)// ESP32-S2 support APLL #define SOC_I2S_APLL_MIN_FREQ (250000000) #define SOC_I2S_APLL_MAX_FREQ (500000000) #define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware diff --git a/components/soc/esp32s3/i2s_periph.c b/components/soc/esp32s3/i2s_periph.c index 52f714edf7..39ddfff5a5 100644 --- a/components/soc/esp32s3/i2s_periph.c +++ b/components/soc/esp32s3/i2s_periph.c @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,17 +20,25 @@ */ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { - .o_bck_in_sig = I2S0O_BCK_IN_IDX, - .o_ws_in_sig = I2S0O_WS_IN_IDX, - .o_bck_out_sig = I2S0O_BCK_OUT_IDX, - .o_ws_out_sig = I2S0O_WS_OUT_IDX, - .o_data_out_sig = I2S0O_SD_OUT_IDX, - .i_bck_in_sig = I2S0I_BCK_OUT_IDX, - .i_ws_in_sig = I2S0I_WS_OUT_IDX, - .i_bck_out_sig = I2S0I_BCK_IN_IDX, - .i_ws_out_sig = I2S0I_WS_IN_IDX, - .i_data_in_sig = I2S0I_SD_IN_IDX, - .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, + .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, + .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, + .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/esp32s3/include/soc/i2s_caps.h b/components/soc/esp32s3/include/soc/i2s_caps.h deleted file mode 100644 index cd21be8893..0000000000 --- a/components/soc/esp32s3/include/soc/i2s_caps.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#define SOC_I2S_APLL_MIN_FREQ (250000000) -#define SOC_I2S_APLL_MAX_FREQ (500000000) -#define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware -#define SOC_I2S_MAX_BUFFER_SIZE (4 * 1024 * 1024) //the maximum RAM can be allocated - -// ESP32-S3 have 1 I2S -#define SOC_I2S_NUM (1) diff --git a/components/soc/esp32s3/include/soc/i2s_struct.h b/components/soc/esp32s3/include/soc/i2s_struct.h index eff11a9ca6..fc02adc0b8 100644 --- a/components/soc/esp32s3/include/soc/i2s_struct.h +++ b/components/soc/esp32s3/include/soc/i2s_struct.h @@ -318,8 +318,8 @@ typedef volatile struct { uint32_t reserved12 : 20; /* Reserved*/ }; uint32_t val; - } rxeof_num; - uint32_t conf_sigle_data; + } rx_eof_num; + uint32_t conf_single_data; /*the right channel or left channel put out constant value stored in this register according to tx_chan_mod and reg_tx_msb_right*/ union { struct { uint32_t tx_idle : 1; /*1: i2s_tx is idle state. 0: i2s_tx is working.*/ diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 40942446cc..b3680c6bd8 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -63,7 +63,12 @@ #include "i2c_caps.h" /*-------------------------- I2S CAPS ----------------------------------------*/ -#include "i2s_caps.h" +#define SOC_I2S_NUM (2) +#define SOC_I2S_SUPPORTS_PCM (1) +#define SOC_I2S_SUPPORTS_PDM_TX (1) +#define SOC_I2S_SUPPORTS_PDM_RX (1) +#define SOC_I2S_SUPPORTS_PDM_CODEC (1) +#define SOC_I2S_SUPPORTS_TDM (1) /*-------------------------- LEDC CAPS ---------------------------------------*/ #include "ledc_caps.h" diff --git a/components/soc/esp32s3/ld/esp32s3.peripherals.ld b/components/soc/esp32s3/ld/esp32s3.peripherals.ld index 5b15a44618..bae22f0205 100644 --- a/components/soc/esp32s3/ld/esp32s3.peripherals.ld +++ b/components/soc/esp32s3/ld/esp32s3.peripherals.ld @@ -8,6 +8,7 @@ PROVIDE ( RTCIO = 0x60008400 ); PROVIDE ( SENS = 0x60008800 ); PROVIDE ( HINF = 0x6000B000 ); PROVIDE ( I2S0 = 0x6000F000 ); +PROVIDE ( I2S1 = 0x6002D000 ); PROVIDE ( UART1 = 0x60010000 ); PROVIDE ( I2C0 = 0x60013000 ); PROVIDE ( UHCI0 = 0x60014000 ); diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index a2b9c54274..59e31674d5 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -1,4 +1,4 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,16 +27,13 @@ extern "C" { Stores a bunch of per-I2S-peripheral data. */ typedef struct { - const uint8_t o_bck_in_sig; - const uint8_t o_ws_in_sig; - const uint8_t o_bck_out_sig; - const uint8_t o_ws_out_sig; - const uint8_t o_data_out_sig; - const uint8_t i_bck_in_sig; - const uint8_t i_ws_in_sig; - const uint8_t i_bck_out_sig; - const uint8_t i_ws_out_sig; - const uint8_t i_data_in_sig; + 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 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 feb582dc25..6fd3bd2415 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -1,27 +1,18 @@ I2S === -.. only:: esp32c3 - - .. warning:: - - This document is not updated for ESP32-C3 yet. +{IDF_TARGET_I2S_NUM:default="two", esp32s2="one", esp32c3="one"} Overview -------- I2S (Inter-IC Sound) is a serial, synchronous communication protocol that is usually used for transmitting audio data between two digital audio devices. -.. only:: esp32 - - {IDF_TARGET_NAME} contains two I2S peripherals. These peripherals can be configured to input and output sample data via the I2S driver. - -.. only:: esp32s2 - - {IDF_TARGET_NAME} contains one I2S peripheral. These peripherals can be configured to input and output sample data via the I2S driver. +{IDF_TARGET_NAME} contains {IDF_TARGET_I2S_NUM} I2S peripheral(s). These peripherals can be configured to input and output sample data via the I2S driver. An I2S bus consists of the following lines: +- Master clock line (operational) - Bit clock line - Channel select line - Serial data line @@ -30,29 +21,33 @@ Each I2S controller has the following features that can be configured using the - Operation as system master or slave - Capable of acting as transmitter or receiver -- Dedicated DMA controller that allows for streaming sample data without requiring the CPU to copy each data sample +- DMA controller that allows for streaming sample data without requiring the CPU to copy each data sample Each controller can operate in half-duplex communication mode. Thus, the two controllers can be combined to establish full-duplex communication. -I2S0 output can be routed directly to the digital-to-analog converter's (DAC) output channels (GPIO 25 & GPIO 26) to produce direct analog output without involving any external I2S codecs. I2S0 can also be used for transmitting PDM (Pulse-density modulation) signals. - -The I2S peripherals also support LCD mode for communicating data over a parallel bus, as used by some LCD displays and camera modules. LCD mode has the following operational modes: - -- LCD master transmitting mode -- Camera slave receiving mode -- ADC/DAC mode - .. only:: esp32 + I2S0 output can be routed directly to the digital-to-analog converter's (DAC) output channels (GPIO 25 & GPIO 26) to produce direct analog output without involving any external I2S codecs. I2S0 can also be used for transmitting PDM (Pulse-density modulation) signals. + +.. only:: esp32 or esp32s2 + + The I2S peripherals also support LCD mode for communicating data over a parallel bus, as used by some LCD displays and camera modules. LCD mode has the following operational modes: + + - LCD master transmitting mode + - Camera slave receiving mode + - ADC/DAC mode + For more information, see *{IDF_TARGET_NAME} Technical Reference Manual* > *I2S Controller (I2S)* > LCD Mode [`PDF <{IDF_TARGET_TRM_EN_URL}#camlcdctrl>`__]. -.. note:: +.. only:: SOC_I2S_SUPPORTS_APLL - For high accuracy clock applications, use the APLL_CLK clock source, which has the frequency range of 16 ~ 128 MHz. You can enable the APLL_CLK clock source by setting :cpp:member:`i2s_config_t::use_apll` to ``TRUE``. + .. note:: - If :cpp:member:`i2s_config_t::use_apll` = ``TRUE`` and :cpp:member:`i2s_config_t::fixed_mclk` > ``0``, then the master clock output frequency for I2S will be equal to the value of :cpp:member:`i2s_config_t::fixed_mclk`, which means that the mclk frequency is provided by the user, instead of being calculated by the driver. + For high accuracy clock applications, use the APLL_CLK clock source, which has the frequency range of 16 ~ 128 MHz. You can enable the APLL_CLK clock source by setting :cpp:member:`i2s_config_t::use_apll` to ``TRUE``. - The clock rate of the word select line, which is called audio left-right clock rate (LRCK) here, is always the divisor of the master clock output frequency and for which the following is always true: 0 < MCLK/LRCK/channels/bits_per_sample < 64. + If :cpp:member:`i2s_config_t::use_apll` = ``TRUE`` and :cpp:member:`i2s_config_t::fixed_mclk` > ``0``, then the master clock output frequency for I2S will be equal to the value of :cpp:member:`i2s_config_t::fixed_mclk`, which means that the mclk frequency is provided by the user, instead of being calculated by the driver. + + The clock rate of the word select line, which is called audio left-right clock rate (LRCK) here, is always the divisor of the master clock output frequency and for which the following is always true: 0 < MCLK/LRCK/channels/bits_per_sample < 64. Functional Overview @@ -68,26 +63,50 @@ Install the I2S driver by calling the function :cpp:func`i2s_driver_install` and - The structure :cpp:type:`i2s_config_t` with defined communication parameters - Event queue size and handle +Once :cpp:func`i2s_driver_install` returns ``ESP_OK``, it means I2S has started. + Configuration example: -.. code-block:: c +.. only:: not SOC_I2S_SUPPORTS_TDM - static const int i2s_num = 0; // i2s port number + .. code-block:: c - static const i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, - .sample_rate = 44100, - .bits_per_sample = 16, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, // default interrupt priority - .dma_buf_count = 8, - .dma_buf_len = 64, - .use_apll = false - }; + static const int i2s_num = 0; // i2s port number - i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S + .tx_desc_auto_clear = false, + .dma_buf_count = 8, + .dma_buf_len = 64, + .use_apll = false, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 // Interrupt level 1, default 0 + }; + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + +.. only:: SOC_I2S_SUPPORTS_TDM + + .. code-block:: c + + static const int i2s_num = 0; // i2s port number + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .tx_desc_auto_clear = false, + .dma_buf_count = 8, + .dma_buf_len = 64, + .bits_per_chan = I2S_BITS_PER_SAMPLE_16BIT + }; + + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); Setting Communication Pins ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,20 +114,24 @@ Setting Communication Pins Once the driver is installed, configure physical GPIO pins to which signals will be routed. For this, call the function :cpp:func`i2s_set_pin` and pass the following arguments to it: - Port number -- The structure :cpp:type:`i2s_pin_config_t` defining the GPIO pin numbers to which the driver should route the BCK, WS, DATA out, and DATA in signals. If you want to keep a currently allocated pin number for a specific signal, or if this signal is unused, then pass the macro :c:macro:`I2S_PIN_NO_CHANGE`. See the example below. +- The structure :cpp:type:`i2s_pin_config_t` defining the GPIO pin numbers to which the driver should route the MCK, BCK, WS, DATA out, and DATA in signals. If you want to keep a currently allocated pin number for a specific signal, or if this signal is unused, then pass the macro :c:macro:`I2S_PIN_NO_CHANGE`. See the example below. + +.. note:: + + MCK only takes effect in `I2S_MODE_MASTER` mode. .. code-block:: c static const i2s_pin_config_t pin_config = { - .bck_io_num = 26, - .ws_io_num = 25, - .data_out_num = 22, + .mck_io_num = 0, + .bck_io_num = 4, + .ws_io_num = 5, + .data_out_num = 18, .data_in_num = I2S_PIN_NO_CHANGE }; i2s_set_pin(i2s_num, &pin_config); - Running I2S Communication ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,14 +140,17 @@ To perform a transmission: - Prepare the data for sending - Call the function :cpp:func:`i2s_write` and pass the data buffer address and data length to it -The function will write the data to the I2S DMA Tx buffer, and then the data will be transmitted automatically. +The function will write the data to the DMA Tx buffer, and then the data will be transmitted automatically. .. code-block:: c i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100); +To retrieve received data, use the function :cpp:func:`i2s_read`. It will retrieve the data from the DMA Rx buffer, once the data is received by the I2S controller. -To retrieve received data, use the function :cpp:func:`i2s_read`. It will retrieve the data from the I2S DMA Rx buffer, once the data is received by the I2S controller. +.. code-block:: c + + i2s_read(I2S_NUM, data_recv, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_read, 100); You can temporarily stop the I2S driver by calling the function :cpp:func:`i2s_stop`, which will disable the I2S Tx/Rx units until the function :cpp:func:`i2s_start` is called. If the function :cpp:func`i2s_driver_install` is used, the driver will start up automatically eliminating the need to call :cpp:func:`i2s_start`. @@ -140,83 +166,186 @@ Application Example A code example for the I2S driver can be found in the directory :example:`peripherals/i2s`. -In addition, there are two short configuration examples for the I2S driver. +.. only:: SOC_I2S_SUPPORTS_ADC_DAC + In addition, there are two short configuration examples for the I2S driver. + +.. only:: not SOC_I2S_SUPPORTS_ADC_DAC + + In addition, there is a short configuration examples for the I2S driver. I2S configuration ^^^^^^^^^^^^^^^^^ -.. code-block:: c +Example for general usage. - #include "driver/i2s.h" - #include "freertos/queue.h" +.. only:: not SOC_I2S_SUPPORTS_TDM - static const int i2s_num = 0; // i2s port number + .. code-block:: c - static const i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, - .sample_rate = 44100, - .bits_per_sample = 16, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, // default interrupt priority - .dma_buf_count = 8, - .dma_buf_len = 64, - .use_apll = false - }; + #include "driver/i2s.h" - static const i2s_pin_config_t pin_config = { - .bck_io_num = 26, - .ws_io_num = 25, - .data_out_num = 22, - .data_in_num = I2S_PIN_NO_CHANGE - }; + static const int i2s_num = 0; // i2s port number - ... + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S + .tx_desc_auto_clear = false, + .dma_buf_count = 8, + .dma_buf_len = 64, + .use_apll = false, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 // Interrupt level 1, default 0 + }; + + static const i2s_pin_config_t pin_config = { + .bck_io_num = 4, + .ws_io_num = 5, + .data_out_num = 18, + .data_in_num = I2S_PIN_NO_CHANGE + }; i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver - i2s_set_pin(i2s_num, &pin_config); - i2s_set_sample_rates(i2s_num, 22050); //set sample rates + ... + /* You can reset parameters by calling 'i2s_set_clk' + * + * The low 16 bits are the valid data bits in one chan and the high 16 bits are + * the total bits in one chan. If high 16 bits is smaller than low 16 bits, it will + * be set to a same value as low 16 bits. + */ + uint32_t bits_cfg = (I2S_BITS_PER_CHAN_32BIT << 16) | I2S_BITS_PER_SAMPLE_16BIT; + i2s_set_clk(i2s_num, 22050, bits_cfg, I2S_CHANNEL_STEREO); + ... i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver +.. only:: SOC_I2S_SUPPORTS_TDM -Configuring I2S to use internal DAC for analog output -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. code-block:: c -.. code-block:: c + #include "driver/i2s.h" - #include "driver/i2s.h" - #include "freertos/queue.h" + static const int i2s_num = 0; // i2s port number - static const int i2s_num = 0; // i2s port number + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S + .tx_desc_auto_clear = false, + .dma_buf_count = 8, + .dma_buf_len = 64 + }; - static const i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, - .sample_rate = 44100, - .bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */ - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .intr_alloc_flags = 0, // default interrupt priority - .dma_buf_count = 8, - .dma_buf_len = 64, - .use_apll = false - }; - - ... + static const i2s_pin_config_t pin_config = { + .bck_io_num = 4, + .ws_io_num = 5, + .data_out_num = 18, + .data_in_num = I2S_PIN_NO_CHANGE + }; i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + i2s_set_pin(i2s_num, &pin_config); - i2s_set_pin(i2s_num, NULL); //for internal DAC, this will enable both of the internal channels - - //You can call i2s_set_dac_mode to set built-in DAC output mode. - //i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); - - i2s_set_sample_rates(i2s_num, 22050); //set sample rates + ... + /* You can reset parameters by calling 'i2s_set_clk' + * + * The low 16 bits are the valid data bits in one chan and the high 16 bits are + * the total bits in one chan. If high 16 bits is smaller than low 16 bits, it will + * be set to a same value as low 16 bits. + */ + uint32_t bits_cfg = (I2S_BITS_PER_CHAN_32BIT << 16) | I2S_BITS_PER_SAMPLE_16BIT; + i2s_set_clk(i2s_num, 22050, bits_cfg, I2S_CHANNEL_STEREO); + ... i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + I2S on {IDF_TARGET_NAME} support TDM mode, up to 16 channels are available in TDM mode. If you want to use TDM mode, set field ``channel_format`` of :cpp:type:`i2s_config_t` to ``I2S_CHANNEL_FMT_MULTIPLE``. Then enable the channels by setting ``chan_mask`` using masks in :cpp:type:`i2s_channel_t`, the number of active channels and total channels will be calculate automatically. Also you can set a particular total channel number for it, but it shouldn't be smaller than the largest channel you use. + + If active channels are discrete, the inactive channels within total channels will be filled by a constant automatically. But if ``skip_msk`` is enabled, these inactive channels will be skiped. + + .. code-block:: c + + #include "driver/i2s.h" + + static const int i2s_num = 0; // i2s port number + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_MULTIPLE, + .communication_format = I2S_COMM_FORMAT_STAND_I2S + .tx_desc_auto_clear = false, + .dma_buf_count = 8, + .dma_buf_len = 64, + .chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH2 + }; + + static const i2s_pin_config_t pin_config = { + .bck_io_num = 4, + .ws_io_num = 5, + .data_out_num = 18, + .data_in_num = I2S_PIN_NO_CHANGE + }; + + i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + i2s_set_pin(i2s_num, &pin_config); + + ... + /* You can reset parameters by calling 'i2s_set_clk' + * + * The low 16 bits are the valid data bits in one chan and the high 16 bits are + * the total bits in one chan. If high 16 bits is smaller than low 16 bits, it will + * be set to a same value as low 16 bits. + */ + uint32_t bits_cfg = (I2S_BITS_PER_CHAN_32BIT << 16) | I2S_BITS_PER_SAMPLE_16BIT; + i2s_set_clk(i2s_port_t i2s_num, 22050, bits_cfg, I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1); // set clock + ... + + i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + +.. only:: SOC_I2S_SUPPORTS_ADC_DAC + + Configuring I2S to use internal DAC for analog output + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + .. code-block:: c + + #include "driver/i2s.h" + #include "freertos/queue.h" + + static const int i2s_num = 0; // i2s port number + + static const i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, + .sample_rate = 44100, + .bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */ + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .intr_alloc_flags = 0, // default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 64, + .use_apll = false + }; + + ... + + i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + + i2s_set_pin(i2s_num, NULL); //for internal DAC, this will enable both of the internal channels + + //You can call i2s_set_dac_mode to set built-in DAC output mode. + //i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + + i2s_set_sample_rates(i2s_num, 22050); //set sample rates + + i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + API Reference ------------- diff --git a/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c b/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c index 0eca7953df..f4e8d8c9e4 100644 --- a/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c +++ b/examples/peripherals/i2s/i2s_basic/main/i2s_example_main.c @@ -15,6 +15,7 @@ #include "driver/i2s.h" #include "driver/gpio.h" #include "esp_system.h" +#include "esp_log.h" #include @@ -22,13 +23,15 @@ #define I2S_NUM (0) #define WAVE_FREQ_HZ (100) #define PI (3.14159265) -#define I2S_BCK_IO (GPIO_NUM_13) -#define I2S_WS_IO (GPIO_NUM_15) -#define I2S_DO_IO (GPIO_NUM_21) +#define I2S_BCK_IO (GPIO_NUM_4) +#define I2S_WS_IO (GPIO_NUM_5) +#define I2S_DO_IO (GPIO_NUM_18) #define I2S_DI_IO (-1) #define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ) +static const char* TAG = "i2s_example"; + static void setup_triangle_sine_waves(int bits) { int *samples_data = malloc(((bits+8)/16)*SAMPLE_PER_CYCLE*4); @@ -64,7 +67,7 @@ static void setup_triangle_sine_waves(int bits) } } - + ESP_LOGI(TAG, "set clock"); i2s_set_clk(I2S_NUM, SAMPLE_RATE, bits, 2); //Using push // for(i = 0; i < SAMPLE_PER_CYCLE; i++) { @@ -74,6 +77,7 @@ static void setup_triangle_sine_waves(int bits) // i2s_push_sample(0, &samples_data[i*2], 100); // } // or write + ESP_LOGI(TAG, "write data"); i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100); free(samples_data); @@ -87,10 +91,10 @@ void app_main(void) //if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes //if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .mode = I2S_MODE_MASTER | I2S_MODE_TX, .sample_rate = SAMPLE_RATE, - .bits_per_sample = 16, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_MSB, .dma_buf_count = 6, .dma_buf_len = 60,