mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
driver/i2s: support mclk
This commit is contained in:
parent
3c57a6ac36
commit
f863998e90
@ -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
|
||||
|
@ -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<<total_chan). */
|
||||
uint32_t total_chan; /*!< I2S Total number of channels. If it is smaller than the biggest active channel number, it will be set to this number automatically. */
|
||||
bool left_align_en; /*!< Set to enable left alignment */
|
||||
bool big_edin_en; /*!< Set to enable big edin */
|
||||
bool bit_order_msb_en; /*!< Set to enable msb order */
|
||||
bool skip_msk_en; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
|
||||
bool left_align; /*!< Set to enable left alignment */
|
||||
bool big_edin; /*!< Set to enable big edin */
|
||||
bool bit_order_msb; /*!< Set to enable msb order */
|
||||
bool skip_msk; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
||||
|
||||
} i2s_driver_config_t;
|
||||
@ -146,7 +173,7 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin);
|
||||
* In the second downsample process, the sampling number is fixed as 8.
|
||||
* So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly.
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
* @param dsr i2s RX down sample rate for PDM mode.
|
||||
* @param downsample i2s RX down sample rate for PDM mode.
|
||||
*
|
||||
* @note After calling this function, it would call i2s_set_clk inside to update the clock frequency.
|
||||
* Please call this function after I2S driver has been initialized.
|
||||
@ -156,31 +183,25 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin);
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM Out of memory
|
||||
*/
|
||||
esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr);
|
||||
esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsample);
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
/**
|
||||
* @brief Set TX PDM mode up-sample rate
|
||||
* 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
|
||||
* @note If you have set PDM mode while calling 'i2s_driver_install',
|
||||
* default PDM TX upsample parameters have already been set,
|
||||
* no need to call this function again if you don't have to change the default configuration
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0
|
||||
* @param sample_rate The sample rate to be set
|
||||
* @param fp PDM TX upsampling configuration paramater
|
||||
* @param fs PDM TX upsampling configuration paramater
|
||||
*
|
||||
* @note After calling this function, it would call i2s_set_clk inside to update the clock frequency.
|
||||
* Please call this function after I2S driver has been initialized.
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
* @param upsample_cfg Set I2S PDM up-sample rate configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM Out of memory
|
||||
*/
|
||||
esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, 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);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -356,6 +377,7 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num);
|
||||
* @brief Configure I2S a/u-law decompress or compress
|
||||
*
|
||||
* @note This function should be called after i2s driver installed
|
||||
* Only take effecttive when the i2s 'communication_format' is set to 'I2S_COMM_FORMAT_STAND_PCM_SHORT' or 'I2S_COMM_FORMAT_STAND_PCM_LONG'
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0
|
||||
*
|
||||
@ -385,8 +407,9 @@ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg);
|
||||
* @param bits_cfg I2S bits configuration
|
||||
* the low 16 bits is for data bits per sample in one channel (see 'i2s_bits_per_sample_t')
|
||||
* the high 16 bits is for total bits in one channel (see 'i2s_bits_per_chan_t')
|
||||
* high 16bits =0 means same as the bits per sample.
|
||||
*
|
||||
* @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO)
|
||||
* @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO or specific channel in TDM mode)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
|
@ -463,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,
|
||||
};
|
||||
|
||||
|
@ -14,13 +14,13 @@ if(NOT BOOTLOADER_BUILD)
|
||||
"spi_hal_iram.c"
|
||||
"spi_slave_hal.c"
|
||||
"spi_slave_hal_iram.c"
|
||||
"i2s_hal.c"
|
||||
"sigmadelta_hal.c"
|
||||
"timer_hal.c"
|
||||
"ledc_hal.c"
|
||||
"ledc_hal_iram.c"
|
||||
"i2c_hal.c"
|
||||
"i2c_hal_iram.c"
|
||||
"i2s_hal.c"
|
||||
"gpio_hal.c"
|
||||
"uart_hal.c"
|
||||
"uart_hal_iram.c"
|
||||
|
@ -202,7 +202,7 @@ static inline void i2s_ll_tx_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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 <stdbool.h>
|
||||
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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<<total_chan_num). */
|
||||
bool left_align_en; /*!< Set to enable left aligment */
|
||||
bool big_edin_en; /*!< Set to enable big edin */
|
||||
bool bit_order_msb_en; /*!< Set to enable msb order */
|
||||
bool skip_msk_en; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
|
||||
bool left_align; /*!< Set to enable left aligment */
|
||||
bool big_edin; /*!< Set to enable big edin */
|
||||
bool bit_order_msb; /*!< Set to enable msb order */
|
||||
bool skip_msk; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
|
||||
#endif
|
||||
} i2s_hal_config_t;
|
||||
|
||||
@ -253,6 +253,24 @@ void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, uint32_t sclk, uint32_t fbc
|
||||
*/
|
||||
#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
|
||||
@ -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
|
||||
|
@ -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<<total_chan_num).
|
||||
// e.g: active_chan_mask = (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH3), here the active_chan_number is 2 and total_chan_num is not supposed to be smaller than 4.
|
||||
@ -141,9 +137,21 @@ typedef enum {
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_D2CLK = 0, /*!< Clock from PLL_D2_CLK(160M)*/
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
I2S_CLK_APLL, /*!< Clock from APLL*/
|
||||
#endif
|
||||
} i2s_clock_src_t;
|
||||
|
||||
/**
|
||||
* @brief The multiple of mclk to sample rate
|
||||
*/
|
||||
typedef enum {
|
||||
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.
|
||||
@ -193,16 +201,6 @@ typedef enum {
|
||||
} i2s_pdm_sig_scale_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -20,23 +20,25 @@
|
||||
*/
|
||||
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
|
||||
{
|
||||
.mck_out_sig = -1, // Unavailable
|
||||
.rx_bck_sig = I2S0I_BCK_IN_IDX,
|
||||
.tx_bck_sig = I2S0O_BCK_OUT_IDX,
|
||||
.tx_ws_sig = I2S0O_WS_OUT_IDX,
|
||||
.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,
|
||||
.irq = ETS_I2S0_INTR_SOURCE,
|
||||
.module = PERIPH_I2S0_MODULE,
|
||||
},
|
||||
{
|
||||
.mck_out_sig = -1, // Unavailable
|
||||
.rx_bck_sig = I2S1I_BCK_IN_IDX,
|
||||
.tx_bck_sig = I2S1O_BCK_OUT_IDX,
|
||||
.tx_ws_sig = I2S1O_WS_OUT_IDX,
|
||||
.rx_ws_sig = I2S1I_WS_IN_IDX,
|
||||
.data_out_sig = I2S1O_DATA_OUT23_IDX,
|
||||
.data_in_sig = I2S1I_DATA_IN15_IDX,
|
||||
.irq = ETS_I2S1_INTR_SOURCE,
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
.irq = ETS_I2S1_INTR_SOURCE,
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
}
|
||||
};
|
||||
|
@ -20,13 +20,14 @@
|
||||
*/
|
||||
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
|
||||
{
|
||||
.mck_out_sig = I2S_MCLK_OUT_IDX,
|
||||
.rx_bck_sig = I2SI_BCK_IN_IDX,
|
||||
.tx_bck_sig = I2SO_BCK_OUT_IDX,
|
||||
.tx_ws_sig = I2SO_WS_OUT_IDX,
|
||||
.rx_ws_sig = I2SI_WS_IN_IDX,
|
||||
.data_out_sig = I2SO_SD_OUT_IDX,
|
||||
.data_in_sig = I2SI_SD_IN_IDX,
|
||||
.irq = -1,
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
.data_out_sig = I2SO_SD_OUT_IDX,
|
||||
.data_in_sig = I2SI_SD_IN_IDX,
|
||||
.irq = -1,
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
}
|
||||
};
|
||||
|
@ -20,13 +20,14 @@
|
||||
*/
|
||||
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
|
||||
{
|
||||
.mck_out_sig = CLK_I2S_MUX_IDX,
|
||||
.rx_bck_sig = I2S0I_BCK_IN_IDX,
|
||||
.tx_bck_sig = I2S0O_BCK_OUT_IDX,
|
||||
.tx_ws_sig = I2S0O_WS_OUT_IDX,
|
||||
.rx_ws_sig = I2S0I_WS_IN_IDX,
|
||||
.data_out_sig = I2S0O_DATA_OUT23_IDX,
|
||||
.data_in_sig = I2S0I_DATA_IN15_IDX,
|
||||
.irq = ETS_I2S0_INTR_SOURCE,
|
||||
.module = PERIPH_I2S0_MODULE,
|
||||
.irq = ETS_I2S0_INTR_SOURCE,
|
||||
.module = PERIPH_I2S0_MODULE,
|
||||
}
|
||||
};
|
||||
|
@ -20,23 +20,25 @@
|
||||
*/
|
||||
const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = {
|
||||
{
|
||||
.mck_out_sig = I2S0_MCLK_OUT_IDX,
|
||||
.rx_bck_sig = I2S0I_BCK_IN_IDX,
|
||||
.tx_bck_sig = I2S0O_BCK_OUT_IDX,
|
||||
.tx_ws_sig = I2S0O_WS_OUT_IDX,
|
||||
.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,
|
||||
.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,
|
||||
.data_out_sig = I2S1O_SD_OUT_IDX,
|
||||
.data_in_sig = I2S1I_SD_IN_IDX,
|
||||
.irq = -1,
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
}
|
||||
};
|
||||
|
@ -27,6 +27,7 @@ extern "C" {
|
||||
Stores a bunch of per-I2S-peripheral data.
|
||||
*/
|
||||
typedef struct {
|
||||
const uint8_t mck_out_sig;
|
||||
const uint8_t tx_bck_sig;
|
||||
const uint8_t rx_bck_sig;
|
||||
const uint8_t tx_ws_sig;
|
||||
|
@ -12,6 +12,7 @@ I2S (Inter-IC Sound) is a serial, synchronous communication protocol that is usu
|
||||
|
||||
An I2S bus consists of the following lines:
|
||||
|
||||
- Master clock line (operational)
|
||||
- Bit clock line
|
||||
- Channel select line
|
||||
- Serial data line
|
||||
@ -38,13 +39,15 @@ Each controller can operate in half-duplex communication mode. Thus, the two con
|
||||
|
||||
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
|
||||
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user