diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 36c4618d5b..f0dc634ac3 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -14,7 +14,6 @@ #include "freertos/semphr.h" #include "soc/lldesc.h" -#include "driver/periph_ctrl.h" #include "driver/gpio.h" #include "driver/i2s.h" #include "hal/gpio_hal.h" @@ -50,8 +49,6 @@ static const char *TAG = "I2S"; #define I2S_FULL_DUPLEX_SLAVE_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) #define I2S_FULL_DUPLEX_MASTER_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) -#define I2S_MAX_BUFFER_SIZE (4*1024*1024) //the maximum RAM can be allocated - #if !SOC_GDMA_SUPPORTED #define I2S_INTR_IN_SUC_EOF BIT(9) #define I2S_INTR_OUT_EOF BIT(12) @@ -101,6 +98,8 @@ typedef struct { bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ bool use_apll; /*!< I2S use APLL clock */ int fixed_mclk; /*!< I2S fixed MLCK clock */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of I2S master clock(MCLK) to sample rate */ + #ifdef CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; #endif @@ -123,6 +122,7 @@ static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma); * I2S GPIO operation * * - gpio_matrix_out_check_and_set * * - gpio_matrix_in_check_and_set * + * - i2s_check_set_mclk * * - i2s_set_pin * **************************************************************/ static void gpio_matrix_out_check_and_set(int gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) @@ -145,6 +145,34 @@ static void gpio_matrix_in_check_and_set(int gpio, uint32_t signal_idx, bool inv } } +static esp_err_t i2s_check_set_mclk(i2s_port_t i2s_num, gpio_num_t gpio_num) +{ + 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_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"); @@ -155,22 +183,16 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) return ESP_ERR_INVALID_ARG; #endif } - if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { - ESP_LOGE(TAG, "bck_io_num error"); - return ESP_ERR_INVALID_ARG; - } - if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { - ESP_LOGE(TAG, "ws_io_num error"); - return ESP_ERR_INVALID_ARG; - } - if (pin->data_out_num != -1 && !GPIO_IS_VALID_OUTPUT_GPIO(pin->data_out_num)) { - ESP_LOGE(TAG, "data_out_num error"); - return ESP_ERR_INVALID_ARG; - } - if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { - ESP_LOGE(TAG, "data_in_num error"); - return ESP_ERR_INVALID_ARG; - } + + 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); @@ -180,6 +202,7 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) 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); @@ -188,6 +211,7 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) 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; @@ -730,7 +754,8 @@ static esp_err_t i2s_fbclk_cal(int i2s_num, uint32_t rate, int channel, int chan //Default select I2S_D2CLK (160M) uint32_t _sclk = I2S_LL_BASE_CLK; uint32_t _fbck = rate * channel * channel_bit; - uint32_t _bck_div = (256 % channel_bit) ? 12 : 8; + 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; //ADC mode only support on ESP32, @@ -907,41 +932,43 @@ esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) #if SOC_I2S_SUPPORTS_PCM esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) { - ESP_RETURN_ON_FALSE(!p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], 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); - if (pcm_cfg->mode & I2S_MODE_TX) { + 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(pcm_cfg->mode & I2S_MODE_RX) { + } 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 #if SOC_I2S_SUPPORTS_PDM_RX -esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr) +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], 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), dsr); + 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, int sample_rate, int fp, int fs) +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], 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), fp, fs); + 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, sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); } #endif @@ -969,18 +996,29 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num) 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 (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(); } #endif - p_i2s[i2s_num]->communication_format = cfg->comm_fmt; - // configure I2S data port interface. - i2s_hal_config_param(&(p_i2s[i2s_num]->hal), cfg); 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) { @@ -1098,10 +1136,6 @@ esp_err_t i2s_start(i2s_port_t i2s_num) //start DMA link I2S_ENTER_CRITICAL(i2s_num); -#if !SOC_GDMA_SUPPORTED - esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle); - i2s_hal_clear_intr_status(&(p_i2s[i2s_num]->hal), I2S_INTR_MAX); -#endif if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { i2s_tx_reset(i2s_num); i2s_tx_start(i2s_num); @@ -1181,12 +1215,12 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, active_chan = 2; break; case I2S_CHANNEL_FMT_ONLY_RIGHT: - p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->left_align_en ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; + 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_en ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; + 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; @@ -1199,10 +1233,10 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, ESP_LOGE(TAG, "wrong i2s channel format, uninstalled i2s."); goto err; } - p_i2s[i2s_num]->hal_cfg.left_align_en = i2s_config->left_align_en; - p_i2s[i2s_num]->hal_cfg.big_edin_en = i2s_config->big_edin_en; - p_i2s[i2s_num]->hal_cfg.bit_order_msb_en = i2s_config->bit_order_msb_en; - p_i2s[i2s_num]->hal_cfg.skip_msk_en = i2s_config->skip_msk_en; + 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 @@ -1214,11 +1248,15 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, 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) { ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock); - } else { + } 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 (ret != ESP_OK) { @@ -1264,7 +1302,7 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, 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, err, TAG, "I2S queue create failed"); + 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 { @@ -1359,7 +1397,6 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by size_t bytes_can_write; *bytes_written = 0; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((size < I2S_MAX_BUFFER_SIZE), ESP_ERR_INVALID_ARG, TAG, "size is too large"); 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 @@ -1402,7 +1439,6 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz *bytes_written = 0; 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 * size < I2S_MAX_BUFFER_SIZE), ESP_ERR_INVALID_ARG, TAG, "size is too large"); 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) { @@ -1465,7 +1501,6 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re *bytes_read = 0; dest_byte = (char *)dest; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((size < I2S_MAX_BUFFER_SIZE), ESP_ERR_INVALID_ARG, TAG, "size is too large"); 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 diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index c7185bf52a..3f6031ed26 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -14,6 +14,7 @@ #include "soc/rtc_periph.h" #include "soc/soc_caps.h" #include "hal/i2s_types.h" +#include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" #if SOC_I2S_SUPPORTS_ADC_DAC @@ -37,28 +38,53 @@ typedef enum { I2S_NUM_MAX, /*!< I2S port max */ } i2s_port_t; -/** - * @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_PCM /** * @brief I2S PCM configuration * */ typedef struct { - i2s_mode_t mode; /*!< I2S mode. Usually only need to choose I2S_MODE_TX or I2S_MODE_RX */ 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 * @@ -75,16 +101,17 @@ typedef struct { 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.*/ + 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<clkm_conf.clka_en = (src == 1) ? 1 : 0; + hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0; } /** @@ -215,7 +215,7 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { //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 == 1) ? 1 : 0; + hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0; } /** diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index 41a8c36d50..c67586aaf0 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -176,7 +176,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. - * @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK` + * @param src I2S source clock, ESP32-C3 only support `I2S_CLK_D2CLK` */ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index 4f933c78bd..a96c03c7c2 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -18,7 +18,7 @@ * 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 @@ -177,7 +177,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) * @brief Set RX source clock * * @param hw Peripheral I2S hardware instance address. - * @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK` + * @param src I2S source clock, ESP32-H2 only support `I2S_CLK_D2CLK` */ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 91ba45e3f3..b0c4085ed8 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -200,7 +200,7 @@ static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw) */ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->clkm_conf.clk_sel = (src == 1) ? 1 : 2; + hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2; } /** @@ -211,7 +211,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) */ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) { - hw->clkm_conf.clk_sel = (src == 1) ? 1 : 2; + hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2; } /** diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index b7cbe45994..df0a24bf0e 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -102,7 +102,7 @@ void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num) i2s_ll_enable_clock(hal->dev); } -static void i2s_hal_tx_set_pdm_mode(i2s_hal_context_t *hal, uint32_t sample_rate) +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 */ @@ -136,7 +136,7 @@ static void i2s_hal_tx_set_pdm_mode(i2s_hal_context_t *hal, uint32_t sample_rate #endif // SOC_I2S_SUPPORTS_PDM_TX } -static void i2s_hal_rx_set_pdm_mode(i2s_hal_context_t *hal) +void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal) { #if SOC_I2S_SUPPORTS_PDM_RX /* enable pdm rx mode */ @@ -147,7 +147,7 @@ static void i2s_hal_rx_set_pdm_mode(i2s_hal_context_t *hal) } -static void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +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); @@ -158,10 +158,10 @@ static void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_con 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_en); - i2s_ll_tx_enable_big_endian(hal->dev, hal_cfg->big_edin_en); - i2s_ll_tx_set_bit_order(hal->dev, hal_cfg->bit_order_msb_en); - i2s_ll_tx_set_skip_mask(hal->dev, hal_cfg->skip_msk_en); + 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); @@ -169,7 +169,7 @@ static void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_con #endif } -static void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +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); @@ -180,9 +180,9 @@ static void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_con 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_en); - i2s_ll_rx_enable_big_endian(hal->dev, hal_cfg->big_edin_en); - i2s_ll_rx_set_bit_order(hal->dev, hal_cfg->bit_order_msb_en); + 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); @@ -251,29 +251,13 @@ void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) { -#if SOC_I2S_SUPPORTS_ADC_DAC - if ((hal_cfg->mode & I2S_MODE_DAC_BUILT_IN) || (hal_cfg->mode & I2S_MODE_ADC_BUILT_IN)) { - if (hal_cfg->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_ll_enable_builtin_dac(hal->dev, true); - } - if (hal_cfg->mode & I2S_MODE_ADC_BUILT_IN) { - i2s_ll_enable_builtin_adc(hal->dev, true); - } - /* Use builtin ADC/DAC, return directly. */ - return; - } else { - i2s_ll_enable_builtin_dac(hal->dev, false); - i2s_ll_enable_builtin_adc(hal->dev, false); - } -#endif - 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(hal, hal_cfg->sample_rate); + 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); @@ -286,7 +270,7 @@ void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cf 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(hal); + i2s_hal_rx_set_pdm_mode_default(hal); } else { /* Set rx common mode */ i2s_hal_rx_set_common_mode(hal, hal_cfg); diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index ee5c32abc3..d0446f7a47 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/components/hal/include/hal/i2s_hal.h @@ -57,10 +57,10 @@ typedef struct { #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, 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 @@ -281,6 +299,14 @@ void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbc * - 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 @@ -300,6 +326,13 @@ void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbc * @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 @@ -437,6 +470,36 @@ void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbc #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 } #endif diff --git a/components/hal/include/hal/i2s_types.h b/components/hal/include/hal/i2s_types.h index a42ad40d05..630aedb149 100644 --- a/components/hal/include/hal/i2s_types.h +++ b/components/hal/include/hal/i2s_types.h @@ -52,16 +52,12 @@ typedef enum { * */ typedef enum { - // I2S_CHANNEL_MONO and I2S_CHANNEL_STEREO values are changed to be compatible with TDM mode - // The lower 16 bits is for enabling specific channels - // The highest bit in I2S_CHANNEL_MONO is for differentiating I2S_CHANNEL_MONO and I2S_CHANNEL_STEREO because they both use two channels - // Two channels will transmit same data in I2S_CHANNEL_MONO mode, and different data in I2S_CHANNEL_STEREO mode - I2S_CHANNEL_MONO = (0x01 << 31) | 0x03, /*!< I2S channel (mono), two channel enabled */ - I2S_CHANNEL_STEREO = 0x03, /*!< I2S channel (stereo), two channel enabled */ + 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_en' in 'i2s_hal_tdm_flags_t' is set. + // 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< *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 @@ -60,7 +63,7 @@ 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 -I2S will start automatically once :cpp:func`i2s_driver_install` returns ``ESP_OK``. +Once :cpp:func`i2s_driver_install` returns ``ESP_OK``, it means I2S has started. Configuration example: @@ -111,11 +114,16 @@ 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 = { + .mck_io_num = 0, .bck_io_num = 4, .ws_io_num = 5, .data_out_num = 18, @@ -257,7 +265,9 @@ Example for general usage. 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 ``tdm_chan_cfg.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. + 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