i2s: update documents for driver-NG

This commit is contained in:
laokaiyao 2022-04-07 15:32:46 +08:00
parent 0fe3bb8ab7
commit 28b8fc6a7e
117 changed files with 10098 additions and 2657 deletions

View File

@ -60,7 +60,7 @@ endif()
if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "i2s/i2s_common.c"
"i2s/i2s_std.c"
"i2s/i2s_legacy.c")
"deprecated/i2s_legacy.c")
if(CONFIG_SOC_I2S_SUPPORTS_PDM)
list(APPEND srcs "i2s/i2s_pdm.c")
endif()

View File

@ -318,7 +318,14 @@ menu "Driver Configurations"
bool "Suppress leagcy driver deprecated warning"
default n
help
Enable this option will suppress the deprecation warnings of using APIs in deprecated I2S driver.
Enable this option will suppress the deprecation warnings of using APIs in legacy I2S driver.
config I2S_ENABLE_DEBUG_LOG
bool "Enable I2S debug log"
default n
help
Wether to enable the debug log message for I2S driver.
Note that, this option only controls the I2S driver log, will not affect other drivers.
endmenu # I2S Configuration
endmenu # Driver configurations

View File

@ -38,6 +38,7 @@
#include "driver/spi_common_internal.h"
#elif CONFIG_IDF_TARGET_ESP32
#include "hal/i2s_types.h"
#include "driver/i2s_types.h"
#include "soc/i2s_periph.h"
#include "esp_private/i2s_platform.h"
#endif

View File

@ -17,7 +17,6 @@
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/i2s_periph.h"
#include "driver/i2s_types_legacy.h"
#if SOC_I2S_SUPPORTS_ADC
@ -46,7 +45,7 @@ extern "C" {
*
* @param pin I2S Pin structure, or NULL to set 2-channel 8-bit internal DAC pin configuration (GPIO25 & GPIO26)
*
* Inside the pin configuration structure, set I2S_GPIO_UNUSED for any pin where
* Inside the pin configuration structure, set I2S_PIN_NO_CHANGE for any pin where
* the current configuration should not be changed.
*
* @note if *pin is set as NULL, this function will initialize both of the built-in DAC channels by default.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,7 @@
#pragma once
#include "hal/i2s_types.h"
#include "driver/i2s_types.h"
#ifdef __cplusplus
extern "C" {
@ -141,6 +142,25 @@ typedef enum {
} i2s_dac_mode_t;
#endif //SOC_I2S_SUPPORTS_DAC
/**
* @brief I2S event queue types
*/
typedef enum {
I2S_EVENT_DMA_ERROR, /*!< I2S DMA has no next descriptor for sending or receiving */
I2S_EVENT_TX_DONE, /*!< I2S DMA finished sending one DMA buffer */
I2S_EVENT_RX_DONE, /*!< I2S DMA finished receiving one DMA buffer */
I2S_EVENT_TX_Q_OVF, /*!< I2S DMA sending queue overflowed, the oldest data has been overwritten by the new data in the DMA buffer */
I2S_EVENT_RX_Q_OVF, /*!< I2S DMA receive queue overflowed, the oldest data has been overwritten by the new data in the DMA buffer */
} i2s_event_type_t;
/**
* @brief Event structure used in I2S event queue
*/
typedef struct {
i2s_event_type_t type; /*!< I2S event type */
size_t size; /*!< I2S data size for I2S_DATA event*/
} i2s_event_t;
/**
* @brief I2S GPIO pins configuration
*/

View File

@ -12,11 +12,19 @@
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#if CONFIG_I2S_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "soc/lldesc.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "driver/i2s_types_legacy.h"
#include "hal/i2s_hal.h"
#include "driver/i2s.h"
#if SOC_I2S_SUPPORTS_DAC
#include "driver/dac.h"
#include "driver/adc.h"
@ -36,12 +44,9 @@
#include "esp_pm.h"
#include "esp_efuse.h"
#include "esp_rom_gpio.h"
#include "esp_private/i2s_platform.h"
#include "esp_private/periph_ctrl.h"
#include "sdkconfig.h"
static const char *TAG = "I2S";
static const char *TAG = "i2s(legacy)";
#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])
@ -124,13 +129,24 @@ typedef struct {
uint32_t total_slot; /*!< Total slot number */
} i2s_obj_t;
// Record the component name that using I2S peripheral
static const char *comp_using_i2s[SOC_I2S_NUM] = {[0 ... SOC_I2S_NUM - 1] = NULL};
// Global I2S object pointer
static i2s_obj_t *p_i2s[SOC_I2S_NUM] = {
[0 ... SOC_I2S_NUM - 1] = NULL,
};
// Global spin lock for all i2s controllers
static portMUX_TYPE i2s_spinlock[SOC_I2S_NUM] = {
[0 ... SOC_I2S_NUM - 1] = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED,
};
__attribute__((weak)) esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name);
__attribute__((weak)) esp_err_t i2s_platform_release_occupation(int id);
/*-------------------------------------------------------------
I2S DMA operation
-------------------------------------------------------------*/
@ -412,7 +428,7 @@ static void i2s_rx_stop(i2s_port_t i2s_num)
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");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
//start DMA link
I2S_ENTER_CRITICAL(i2s_num);
@ -433,7 +449,7 @@ esp_err_t i2s_start(i2s_port_t i2s_num)
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");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), 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);
@ -496,7 +512,6 @@ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj)
ESP_GOTO_ON_FALSE(dma_obj->buf[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma buffer");
/* Initialize DMA buffer to 0 */
memset(dma_obj->buf[cnt], 0, dma_obj->buf_size);
ESP_LOGD(TAG, "Addr[%d] = %d", cnt, (int)dma_obj->buf[cnt]);
/* Allocate DMA descpriptor */
dma_obj->desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), MALLOC_CAP_DMA);
@ -518,7 +533,7 @@ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj)
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_ll_rx_set_eof_num(p_i2s[i2s_num]->hal.dev, dma_obj->buf_size);
}
ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_desc_num=%d", dma_obj->buf_size, buf_cnt);
ESP_LOGD(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_desc_num=%d", dma_obj->buf_size, buf_cnt);
return ESP_OK;
err:
/* Delete DMA buffer if failed to allocate memory */
@ -564,7 +579,7 @@ static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma)
/* Free DMA structure */
free(*dma);
*dma = NULL;
ESP_LOGI(TAG, "DMA queue destroyed");
ESP_LOGD(TAG, "DMA queue destroyed");
return ESP_OK;
}
@ -629,14 +644,14 @@ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint3
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq);
}
ESP_LOGI(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
/* In APLL mode, there is no sclk but only mclk, so return 0 here to indicate APLL mode */
return real_freq;
}
return I2S_LL_BASE_CLK;
#else
if (use_apll) {
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_PLL_160M as default clock source");
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_SRC_DEFAULT as default clock source");
}
return I2S_LL_BASE_CLK;
#endif
@ -869,7 +884,7 @@ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel)
esp_err_t i2s_adc_enable(i2s_port_t i2s_num)
{
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), 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(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX),
ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled");
@ -882,7 +897,7 @@ esp_err_t i2s_adc_enable(i2s_port_t i2s_num)
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((i2s_num < SOC_I2S_NUM), 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(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX),
ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled");
@ -897,7 +912,7 @@ static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, const i2s_config_t *
{
/* Step 1: Check the validity of input parameters */
/* Check the validity of i2s device number */
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE(p_i2s[i2s_num] == NULL, ESP_ERR_INVALID_STATE, TAG, "this i2s port is in use");
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL");
/* Check the size of DMA buffer */
@ -957,16 +972,16 @@ static void i2s_set_slot_legacy(i2s_port_t i2s_num)
}
#if SOC_I2S_SUPPORTS_PDM
else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
#if SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_SUPPORTS_PDM_TX
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
i2s_hal_pdm_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_hal_pdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
#endif
#endif
}
#endif
#if SOC_I2S_SUPPORTS_TDM
@ -1006,14 +1021,14 @@ static void i2s_set_clock_legacy(i2s_port_t i2s_num)
float i2s_get_clk(i2s_port_t i2s_num)
{
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
return (float)clk_cfg->sample_rate_hz;
}
esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch)
{
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S%d has not installed yet", i2s_num);
/* Acquire the lock before stop i2s, otherwise reading/writing operation will stuck on receiving the message queue from interrupt */
@ -1038,6 +1053,19 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
ESP_RETURN_ON_FALSE((slot_cfg->slot_bit_width % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per channel");
ESP_RETURN_ON_FALSE(((int)slot_cfg->slot_bit_width <= (int)I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample");
slot_cfg->slot_mode = ((ch & 0xFFFF) == I2S_CHANNEL_MONO) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) {
if (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) {
if (slot_cfg->std.slot_mask == I2S_STD_SLOT_LEFT_RIGHT) {
slot_cfg->std.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
#if SOC_I2S_HW_VERSION_1
// Enable right first to get correct data sequence
slot_cfg->std.ws_pol = !slot_cfg->std.ws_pol;
#endif
}
} else {
slot_cfg->std.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
}
}
#if SOC_I2S_SUPPORTS_TDM
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
uint32_t slot_mask = ch >> 16;
@ -1092,7 +1120,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate)
{
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
uint32_t mask = 0;
#if SOC_I2S_SUPPORTS_TDM
@ -1224,22 +1252,22 @@ static void i2s_mode_identify(i2s_port_t i2s_num, const i2s_config_t *i2s_config
static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s_config)
{
#define SLOT_CFG(m) p_i2s[i2s_num]->slot_cfg.m
#define CLK_CFG() p_i2s[i2s_num]->clk_cfg
#define SLOT_CFG(m) p_i2s[i2s_num]->slot_cfg.m
#define CLK_CFG() p_i2s[i2s_num]->clk_cfg
/* Convert legacy configuration into general part of slot and clock configuration */
p_i2s[i2s_num]->slot_cfg.data_bit_width = i2s_config->bits_per_sample;
p_i2s[i2s_num]->slot_cfg.slot_bit_width = (int)i2s_config->bits_per_chan < (int)i2s_config->bits_per_sample ?
i2s_config->bits_per_sample : i2s_config->bits_per_chan;
slot_cfg.slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ?
p_i2s[i2s_num]->slot_cfg.slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ?
I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO;
CLK_CFG().sample_rate_hz = i2s_config->sample_rate;
CLK_CFG().mclk_multiple = i2s_config->mclk_multiple == 0 ? I2S_MCLK_MULTIPLE_256 : i2s_config->mclk_multiple;
CLK_CFG().clk_src = I2S_CLK_PLL_160M;
CLK_CFG().clk_src = I2S_CLK_SRC_DEFAULT;
p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk;
p_i2s[i2s_num]->use_apll = false;
#if SOC_I2S_SUPPORTS_APLL
CLK_CFG().clk_src = i2s_config->use_apll ? I2S_CLK_APLL : I2S_CLK_PLL_160M;
CLK_CFG().clk_src = i2s_config->use_apll ? I2S_CLK_SRC_APLL : I2S_CLK_SRC_DEFAULT;
p_i2s[i2s_num]->use_apll = i2s_config->use_apll;
#endif // SOC_I2S_SUPPORTS_APLL
@ -1249,12 +1277,12 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
SLOT_CFG(std).ws_width = i2s_config->bits_per_sample;
SLOT_CFG(std).ws_pol = false;
if (i2s_config->channel_format == I2S_CHANNEL_FMT_RIGHT_LEFT) {
std_slot->slot_sel = I2S_STD_SLOT_LEFT_RIGHT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
} else if (i2s_config->channel_format == I2S_CHANNEL_FMT_ALL_LEFT ||
i2s_config->channel_format == I2S_CHANNEL_FMT_ONLY_LEFT) {
std_slot->slot_sel = I2S_STD_SLOT_ONLY_LEFT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_LEFT;
} else {
std_slot->slot_sel = I2S_STD_SLOT_ONLY_RIGHT;
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
}
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
SLOT_CFG(std).bit_shift = true;
@ -1264,13 +1292,13 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
SLOT_CFG(std).ws_width = 1;
SLOT_CFG(std).ws_pol = true;
}
#if SOC_I2S_HW_VERSION_1
SLOT_CFG(std).msb_right = false;
#elif SOC_I2S_HW_VERSION_2
#if SOC_I2S_HW_VERSION_1
SLOT_CFG(std).msb_right = true;
#elif SOC_I2S_HW_VERSION_2
SLOT_CFG(std).left_align = i2s_config->left_align;
SLOT_CFG(std).big_endian = i2s_config->big_edin;
SLOT_CFG(std).bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect
#endif // SOC_I2S_HW_VERSION_1
#endif // SOC_I2S_HW_VERSION_1
p_i2s[i2s_num]->active_slot = (int)p_i2s[i2s_num]->slot_cfg.slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2;
p_i2s[i2s_num]->total_slot = 2;
@ -1284,13 +1312,13 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
SLOT_CFG(pdm_tx).hp_scale = I2S_PDM_SIG_SCALING_MUL_1;
SLOT_CFG(pdm_tx).lp_scale = I2S_PDM_SIG_SCALING_MUL_1;
SLOT_CFG(pdm_tx).sinc_scale = I2S_PDM_SIG_SCALING_MUL_1;
#if SOC_I2S_HW_VERSION_2
#if SOC_I2S_HW_VERSION_2
SLOT_CFG(pdm_tx).sd_en = true;
SLOT_CFG(pdm_tx).hp_en = true;
SLOT_CFG(pdm_tx).hp_cut_off_freq_hz = 49;
SLOT_CFG(pdm_tx).sd_dither = 0;
SLOT_CFG(pdm_tx).sd_dither2 = 0;
#endif // SOC_I2S_HW_VERSION_2
#endif // SOC_I2S_HW_VERSION_2
/* Generate PDM TX clock configuration */
CLK_CFG().up_sample_fp = 960;
@ -1321,13 +1349,11 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
SLOT_CFG(tdm).ws_pol = false;
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
SLOT_CFG(tdm).bit_shift = true;
}
else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_SHORT) {
} else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_SHORT) {
SLOT_CFG(tdm).bit_shift = true;
SLOT_CFG(tdm).ws_width = 1;
SLOT_CFG(tdm).ws_pol = true;
}
else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_LONG) {
} else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_LONG) {
SLOT_CFG(tdm).bit_shift = true;
SLOT_CFG(tdm).ws_width = p_i2s[i2s_num]->slot_cfg.slot_bit_width;
SLOT_CFG(tdm).ws_pol = true;
@ -1355,6 +1381,9 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
}
#endif // SOC_I2S_SUPPORTS_ADC_DAC
#undef SLOT_CFG
#undef CLK_CFG
finish:
return ESP_OK;
}
@ -1389,16 +1418,16 @@ static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag)
}
#if SOC_I2S_SUPPORTS_PDM
else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
#if SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_SUPPORTS_PDM_TX
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
i2s_hal_pdm_enable_tx_channel(&(p_i2s[i2s_num]->hal));
}
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_hal_pdm_enable_rx_channel(&(p_i2s[i2s_num]->hal));
}
#endif
#endif
}
#endif
#if SOC_I2S_SUPPORTS_TDM
@ -1455,7 +1484,7 @@ static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag)
esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
{
ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE(i2s_num < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num);
i2s_obj_t *obj = p_i2s[i2s_num];
i2s_stop(i2s_num);
@ -1500,10 +1529,10 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
if (obj->use_apll) {
// switch back to PLL clock source
if (obj->dir & I2S_DIR_TX) {
i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_PLL_160M);
i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_SRC_DEFAULT);
}
if (obj->dir & I2S_DIR_RX) {
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_PLL_160M);
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_SRC_DEFAULT);
}
periph_rtc_apll_release();
}
@ -1533,6 +1562,9 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue)
{
#if CONFIG_I2S_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
/* Step 1: Check the validity of input parameters */
@ -1541,12 +1573,12 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config,
/* Step 2: Allocate driver object and register to platform */
i2s_obj_t *i2s_obj = calloc(1, sizeof(i2s_obj_t));
ESP_RETURN_ON_FALSE(i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver");
p_i2s[i2s_num] = i2s_obj;
if (i2s_platform_acquire_occupation(i2s_num, "i2s_legacy") != ESP_OK) {
free(i2s_obj);
ESP_LOGE(TAG, "register I2S object to platform failed");
return ESP_ERR_INVALID_STATE;
}
p_i2s[i2s_num] = i2s_obj;
i2s_hal_init(&i2s_obj->hal, i2s_num);
/* Step 3: Store and assign configarations */
@ -1564,7 +1596,7 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config,
i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t));
ESP_GOTO_ON_FALSE(i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed");
*((QueueHandle_t *) i2s_queue) = i2s_obj->i2s_queue;
ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(i2s_obj->i2s_queue));
ESP_LOGD(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(i2s_obj->i2s_queue));
} else {
i2s_obj->i2s_queue = NULL;
}
@ -1586,7 +1618,7 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by
char *src_byte;
size_t bytes_can_write;
*bytes_written = 0;
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled");
xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
@ -1629,7 +1661,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz
int aim_bytes;
int zero_bytes;
*bytes_written = 0;
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), 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 mode is not enabled");
@ -1688,11 +1720,11 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz
esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait)
{
char *data_ptr;;
char*dest_byte;
char *dest_byte;
int bytes_can_read;
*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((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "RX mode is not enabled");
xSemaphoreTake(p_i2s[i2s_num]->rx->mux, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
@ -1771,13 +1803,13 @@ static esp_err_t i2s_check_set_mclk(i2s_port_t i2s_num, gpio_num_t gpio_num)
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);
ESP_LOGD(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num);
return ESP_OK;
}
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");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
uint32_t buf_cnt = p_i2s[i2s_num]->dma_desc_num;
/* Clear I2S RX DMA buffer */
@ -1805,7 +1837,7 @@ esp_err_t i2s_zero_dma_buffer(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");
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
if (pin == NULL) {
#if SOC_I2S_SUPPORTS_DAC
return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
@ -1852,3 +1884,48 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)
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_platform_acquire_occupation(int id, const char *comp_name)
{
esp_err_t ret = ESP_ERR_NOT_FOUND;
ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
portENTER_CRITICAL(&i2s_spinlock[id]);
if (!comp_using_i2s[id]) {
ret = ESP_OK;
comp_using_i2s[id] = comp_name;
periph_module_enable(i2s_periph_signal[id].module);
i2s_ll_enable_clock(I2S_LL_GET_HW(id));
}
portEXIT_CRITICAL(&i2s_spinlock[id]);
return ret;
}
esp_err_t i2s_platform_release_occupation(int id)
{
esp_err_t ret = ESP_ERR_INVALID_STATE;
ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
portENTER_CRITICAL(&i2s_spinlock[id]);
if (comp_using_i2s[id]) {
ret = ESP_OK;
comp_using_i2s[id] = NULL;
/* Disable module clock */
periph_module_disable(i2s_periph_signal[id].module);
i2s_ll_disable_clock(I2S_LL_GET_HW(id));
}
portEXIT_CRITICAL(&i2s_spinlock[id]);
return ret;
}
/**
* @brief This function will be called during start up, to check that pulse_cnt driver is not running along with the legacy i2s driver
*/
static __attribute__((constructor)) void check_i2s_driver_conflict(void)
{
extern __attribute__((weak)) esp_err_t i2s_del_channel(void *handle);
/* If the new I2S driver is linked, the weak function will point to the actual function in the new driver, otherwise it is NULL*/
if ((void *)i2s_del_channel != NULL) {
ESP_EARLY_LOGE(TAG, "CONFLICT! The new i2s driver can't work along with the legacy i2s driver");
abort();
}
ESP_EARLY_LOGW(TAG, "legacy i2s driver is deprecated, please migrate to use driver/i2s_std.h, driver/i2s_pdm.h or driver/i2s_tdm.h");
}

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#if CONFIG_I2S_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_pdm.h"
@ -14,7 +23,7 @@
#include "esp_intr_alloc.h"
#include "esp_check.h"
static const char *TAG = "I2S_PDM";
static const char *TAG = "i2s_pdm";
/*---------------------------------------------------------------
PDM TX
@ -30,7 +39,7 @@ static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_
clk_info->bclk_div = 8;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_PLL_160M ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
clk_info->sclk = (clk_cfg->clk_src == I2S_CLK_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
@ -39,7 +48,7 @@ static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_
/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
/* Set upsampling configuration */
i2s_ll_tx_set_pdm_fpfs(handle->parent->hal.dev, pdm_tx_clk->up_sample_fp, pdm_tx_clk->up_sample_fs);
i2s_ll_tx_set_pdm_fpfs(handle->controller->hal.dev, pdm_tx_clk->up_sample_fp, pdm_tx_clk->up_sample_fs);
return ESP_OK;
}
@ -49,33 +58,16 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx
esp_err_t ret = ESP_OK;
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)(handle->mode_info);
/* If Power Management enabled, a PM lock will be created when there is no PM lock or clock source changed */
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (handle->pm_lock == NULL || pdm_tx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), TAG, "I2S delete old pm lock failed");
}
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
ESP_RETURN_ON_ERROR(i2s_pdm_tx_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed");
ESP_LOGD(TAG, "Clock division info: [sclk] %d Hz [mdiv] %d [mclk] %d Hz [bdiv] %d [bclk] %d Hz",
clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Set clock configurations in HAL*/
i2s_hal_set_tx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&s_i2s.spinlock);
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: clock configuration */
memcpy(&(pdm_tx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_tx_clk_config_t));
@ -98,13 +90,13 @@ static esp_err_t i2s_pdm_tx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_
TAG, "allocate memory for dma descriptor failed");
}
/* Share bck and ws signal in full-duplex mode */
i2s_ll_share_bck_ws(handle->parent->hal.dev, handle->parent->full_duplex);
i2s_ll_share_bck_ws(handle->controller->hal.dev, handle->controller->full_duplex);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Configure the hardware to apply PDM format */
bool is_slave = handle->role == I2S_ROLE_SLAVE;
i2s_hal_pdm_set_tx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
portEXIT_CRITICAL(&s_i2s.spinlock);
i2s_hal_pdm_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: slot configuration */
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
@ -115,7 +107,7 @@ static esp_err_t i2s_pdm_tx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_
static esp_err_t i2s_pdm_tx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg)
{
int id = handle->parent->id;
int id = handle->controller->id;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->clk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->clk)),
@ -123,21 +115,21 @@ static esp_err_t i2s_pdm_tx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_
ESP_RETURN_ON_FALSE((gpio_cfg->dout == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->dout)),
ESP_ERR_INVALID_ARG, TAG, "dout gpio is invalid");
/* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false);
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */
if (!handle->parent->full_duplex) {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_tx_ws_sig, true);
if (!handle->controller->full_duplex) {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
} else {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
}
} else {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
}
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->hal.dev);
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
#endif
/* Update the mode info: gpio configuration */
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
@ -146,11 +138,14 @@ static esp_err_t i2s_pdm_tx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_
return ESP_OK;
}
esp_err_t i2s_init_pdm_tx_channel(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg)
esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg)
{
#if CONFIG_I2S_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a TX handle");
ESP_RETURN_ON_FALSE(handle->parent->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
ESP_RETURN_ON_FALSE(handle->controller->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
esp_err_t ret = ESP_OK;
@ -164,11 +159,11 @@ esp_err_t i2s_init_pdm_tx_channel(i2s_chan_handle_t handle, const i2s_pdm_tx_con
handle->mode_info = calloc(1, sizeof(i2s_pdm_tx_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, &pdm_tx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_slot should be called before i2s_set_clock while initializing, because clock is relay on the slot */
/* i2s_set_pdm_tx_slot should be called before i2s_set_pdm_tx_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_slot(handle, &pdm_tx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when the clock source is APLL */
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
@ -176,10 +171,19 @@ esp_err_t i2s_init_pdm_tx_channel(i2s_chan_handle_t handle, const i2s_pdm_tx_con
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_clock(handle, &pdm_tx_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
i2s_ll_tx_enable_pdm(handle->parent->hal.dev);
i2s_ll_tx_enable_pdm(handle->controller->hal.dev);
#if SOC_I2S_HW_VERSION_2
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
i2s_ll_tx_enable_clock(handle->parent->hal.dev);
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
#endif
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif
/* Initialization finished, mark state as ready */
@ -192,7 +196,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg)
esp_err_t i2s_channel_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
@ -202,24 +206,37 @@ esp_err_t i2s_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_c
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the clock");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the clock");
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_tx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
#if SOC_I2S_SUPPORTS_APLL
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t*)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_tx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_PLL_160M) {
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && pdm_tx_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
if (clk_cfg->clk_src == I2S_CLK_PLL_160M && pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (pdm_tx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
xSemaphoreGive(handle->mutex);
@ -229,7 +246,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg)
esp_err_t i2s_channel_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
@ -239,9 +256,9 @@ esp_err_t i2s_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_sl
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the slot");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the slot");
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t*)handle->mode_info;
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_tx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_slot(handle, slot_cfg), err, TAG, "set i2s standard slot failed");
@ -260,7 +277,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg)
esp_err_t i2s_channel_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
@ -270,7 +287,7 @@ esp_err_t i2s_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gp
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "This handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be stopped before reconfiguring the gpio");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be disabled before reconfiguring the gpio");
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
xSemaphoreGive(handle->mutex);
@ -298,7 +315,7 @@ static esp_err_t i2s_pdm_rx_calculate_clock(i2s_chan_handle_t handle, const i2s_
clk_info->bclk_div = 8;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_PLL_160M ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
clk_info->sclk = (clk_cfg->clk_src == I2S_CLK_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
@ -307,7 +324,7 @@ static esp_err_t i2s_pdm_rx_calculate_clock(i2s_chan_handle_t handle, const i2s_
/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
/* Set down-sampling configuration */
i2s_ll_rx_set_pdm_dsr(handle->parent->hal.dev, pdm_rx_clk->dn_sample_mode);
i2s_ll_rx_set_pdm_dsr(handle->controller->hal.dev, pdm_rx_clk->dn_sample_mode);
return ESP_OK;
}
@ -316,33 +333,16 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx
esp_err_t ret = ESP_OK;
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)(handle->mode_info);
/* If Power Management enabled, a PM lock will be created when there is no PM lock or clock source changed */
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (handle->pm_lock == NULL || pdm_rx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), TAG, "I2S delete old pm lock failed");
}
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
ESP_RETURN_ON_ERROR(i2s_pdm_rx_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed");
ESP_LOGD(TAG, "Clock division info: [sclk] %d Hz [mdiv] %d [mclk] %d Hz [bdiv] %d [bclk] %d Hz",
clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Set clock configurations in HAL*/
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&s_i2s.spinlock);
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: clock configuration */
memcpy(&(pdm_rx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_rx_clk_config_t));
@ -365,13 +365,13 @@ static esp_err_t i2s_pdm_rx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_
TAG, "allocate memory for dma descriptor failed");
}
/* Share bck and ws signal in full-duplex mode */
i2s_ll_share_bck_ws(handle->parent->hal.dev, handle->parent->full_duplex);
i2s_ll_share_bck_ws(handle->controller->hal.dev, handle->controller->full_duplex);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Configure the hardware to apply PDM format */
bool is_slave = (handle->role == I2S_ROLE_SLAVE) | handle->parent->full_duplex;
i2s_hal_pdm_set_rx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
portEXIT_CRITICAL(&s_i2s.spinlock);
bool is_slave = (handle->role == I2S_ROLE_SLAVE) | handle->controller->full_duplex;
i2s_hal_pdm_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: slot configuration */
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
@ -382,7 +382,7 @@ static esp_err_t i2s_pdm_rx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_
static esp_err_t i2s_pdm_rx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg)
{
int id = handle->parent->id;
int id = handle->controller->id;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->clk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->clk)),
@ -390,20 +390,20 @@ static esp_err_t i2s_pdm_rx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_
ESP_RETURN_ON_FALSE((gpio_cfg->din == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->din)),
ESP_ERR_INVALID_ARG, TAG, "dout gpio is invalid");
/* Set data input GPIO */
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true);
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
} else {
if (!handle->parent->full_duplex) {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_rx_ws_sig, false);
if (!handle->controller->full_duplex) {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
} else {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
}
}
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_rx_clk(handle->parent->hal.dev);
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
#endif
/* Update the mode info: gpio configuration */
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
@ -412,11 +412,11 @@ static esp_err_t i2s_pdm_rx_set_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_
return ESP_OK;
}
esp_err_t i2s_init_pdm_rx_channel(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg)
esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_RX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a RX handle");
ESP_RETURN_ON_FALSE(handle->parent->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
ESP_RETURN_ON_FALSE(handle->controller->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
esp_err_t ret = ESP_OK;
@ -430,11 +430,11 @@ esp_err_t i2s_init_pdm_rx_channel(i2s_chan_handle_t handle, const i2s_pdm_rx_con
handle->mode_info = calloc(1, sizeof(i2s_pdm_rx_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_gpio(handle, &pdm_rx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_slot should be called before i2s_set_clock while initializing, because clock is relay on the slot */
/* i2s_set_pdm_rx_slot should be called before i2s_set_pdm_rx_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_slot(handle, &pdm_rx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when the clock source is APLL */
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
@ -442,10 +442,19 @@ esp_err_t i2s_init_pdm_rx_channel(i2s_chan_handle_t handle, const i2s_pdm_rx_con
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_clock(handle, &pdm_rx_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
i2s_ll_rx_enable_pdm(handle->parent->hal.dev);
i2s_ll_rx_enable_pdm(handle->controller->hal.dev);
#if SOC_I2S_HW_VERSION_2
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
i2s_ll_rx_enable_clock(handle->parent->hal.dev);
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
#endif
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif
/* Initialization finished, mark state as ready */
@ -458,7 +467,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg)
esp_err_t i2s_channel_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
@ -468,24 +477,37 @@ esp_err_t i2s_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_c
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the clock");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the clock");
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_rx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
#if SOC_I2S_SUPPORTS_APLL
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t*)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_rx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_PLL_160M) {
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && pdm_rx_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
if (clk_cfg->clk_src == I2S_CLK_PLL_160M && pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (pdm_rx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
xSemaphoreGive(handle->mutex);
@ -495,7 +517,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg)
esp_err_t i2s_channel_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
@ -505,9 +527,9 @@ esp_err_t i2s_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_sl
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the slot");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the slot");
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t*)handle->mode_info;
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(pdm_rx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_slot(handle, slot_cfg), err, TAG, "set i2s standard slot failed");
@ -526,7 +548,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg)
esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
@ -535,7 +557,7 @@ esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gp
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_PDM, ESP_ERR_INVALID_ARG, err, TAG, "This handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be stopped before reconfiguring the gpio");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be disabled before reconfiguring the gpio");
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
xSemaphoreGive(handle->mutex);

View File

@ -10,6 +10,7 @@
#include "soc/lldesc.h"
#include "soc/soc_caps.h"
#include "hal/i2s_types.h"
#include "driver/i2s_types.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#if SOC_GDMA_SUPPORTED
@ -22,17 +23,15 @@
extern "C" {
#endif
#define I2S_NULL_POINTER_CHECK(tag, p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, tag, "[%s:%d] input parameter '"#p"' is NULL",__FUNCTION__,__LINE__)
#define I2S_NULL_POINTER_CHECK(tag, p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, tag, "input parameter '"#p"' is NULL")
/**
* @brief i2s channel state for checking if the operation in under right driver state
*/
typedef enum {
I2S_CHAN_STATE_REGISTER, /*!< i2s channel is registered (not initialized) */
I2S_CHAN_STATE_READY, /*!< i2s channel is stopped (initialized) */
I2S_CHAN_STATE_IDLE, /*!< i2s channel is idling (initialized and started) */
I2S_CHAN_STATE_WRITING, /*!< i2s channel is writing (initialized and started) */
I2S_CHAN_STATE_READING /*!< i2s channel is reading (initialized and started) */
I2S_CHAN_STATE_READY, /*!< i2s channel is disabled (initialized) */
I2S_CHAN_STATE_RUNNING, /*!< i2s channel is idling (initialized and enabled) */
} i2s_state_t;
/**
@ -41,7 +40,9 @@ typedef enum {
*/
typedef struct {
#if SOC_GDMA_SUPPORTED
gdma_channel_handle_t dma_chan; /*!< gdma channel handler */
gdma_channel_handle_t dma_chan; /*!< gdma channel handle */
#else
intr_handle_t dma_chan; /*!< interrupt channel handle */
#endif
uint32_t desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */
uint32_t frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */
@ -65,15 +66,11 @@ typedef struct {
i2s_chan_handle_t tx_chan; /*!< tx channel handler */
i2s_chan_handle_t rx_chan; /*!< rx channel handler */
int mclk; /*!< MCK out pin, shared by tx/rx*/
#if !SOC_GDMA_SUPPORTED
intr_handle_t i2s_isr_handle; /*!< i2s interrupt handler */
#endif
} i2s_controller_t;
struct i2s_channel_t {
/* Channel basic information */
i2s_controller_t *parent; /*!< Parent pointer to controller object */
i2s_controller_t *controller; /*!< Parent pointer to controller object */
i2s_comm_mode_t mode; /*!< i2s channel communication mode */
i2s_role_t role; /*!< i2s role */
i2s_dir_t dir; /*!< i2s channel direction */
@ -87,43 +84,125 @@ struct i2s_channel_t {
uint32_t active_slot; /*!< Active slot number */
uint32_t total_slot; /*!< Total slot number */
/* Locks and queues */
SemaphoreHandle_t mutex; /*!< Mutex for DMA buffer related operations like reading/writing */
SemaphoreHandle_t mutex; /*!< Mutex semaphore for the channel operations */
SemaphoreHandle_t binary; /*!< Binary semaphore for writing / reading / enabling / disabling */
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; /*!< Power management lock, to avoid apb clock frequency changes while i2s is working */
#endif
#if CONFIG_I2S_ISR_IRAM_SAFE
StaticSemaphore_t *mutex_struct; /*!< Static mutex struct */
StaticSemaphore_t *binary_struct; /*!< Static binary struct */
StaticQueue_t *msg_que_struct; /*!< Static message queue struct */
void *msg_que_storage; /*!< Static message queue storage */
#endif
QueueHandle_t msg_queue; /*!< Message queue handler, used for transporting data between interrupt and read/write task */
QueueHandle_t event_queue; /*!< Event queue handler used for transporting interrupt event to user */
/* Function pointers of the corresponding mode */
i2s_event_callbacks_t callbacks; /*!< Callback functions */
void *user_data; /*!< User data for callback functions */
void (*start)(i2s_chan_handle_t); /*!< start tx/rx channel */
void (*stop)(i2s_chan_handle_t); /*!< stop tx/rx channel */
};
/**
* @brief i2s platform level configurations
* @note All i2s controllers are under its control
* @note All i2s controllers' resources are involved
*/
typedef struct {
portMUX_TYPE spinlock; /*!< Platform level lock */
i2s_controller_t *controller[I2S_NUM_MAX]; /*!< Controller object */
const char *comp_name[I2S_NUM_MAX]; /*!< The component name that occupied i2s controller */
i2s_controller_t *controller[SOC_I2S_NUM]; /*!< Controller object */
const char *comp_name[SOC_I2S_NUM]; /*!< The component name that occupied i2s controller */
} i2s_platform_t;
extern i2s_platform_t s_i2s;
extern i2s_platform_t g_i2s;
/**
* @brief Initialize I2S DMA interrupt
*
* @param handle I2S channel handle
* @param intr_flag I2S interrupt flags, `ESP_INTR_FLAG_XXX` defined in `esp_intr_alloc.h`
* @return
* - ESP_OK Initialize interrupt success
* - ESP_ERR_INVALID_ARG Wrong port id or NULL pointer
*/
esp_err_t i2s_init_dma_intr(i2s_chan_handle_t handle, int intr_flag);
/**
* @brief Free I2S DMA descriptor and DMA buffer
*
* @param handle I2S channel handle
* @return
* - ESP_OK Free success
* - ESP_ERR_INVALID_ARG NULL pointer
*/
esp_err_t i2s_free_dma_desc(i2s_chan_handle_t handle);
/**
* @brief Allocate memory for I2S DMA descriptor and DMA buffer
*
* @param handle I2S channel handle
* @param num Number of DMA descriptors
* @param bufsize The DMA buffer size
*
* @return
* - ESP_OK Allocate memory success
* - ESP_ERR_INVALID_ARG NULL pointer or bufsize is too big
* - ESP_ERR_NO_MEM No memmory for DMA descriptor and DMA buffer
*/
esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bufsize);
/**
* @brief Get DMA buffer size
*
* @param handle I2S channel handle
* @param data_bit_width Data bit width in one slot
* @param dma_frame_num Frame number in one DMA buffer
*
* @return
* - DMA buffer size
*/
uint32_t i2s_get_buf_size(i2s_chan_handle_t handle, uint32_t data_bit_width, uint32_t dma_frame_num);
#if SOC_I2S_SUPPORTS_APLL
uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq);
/**
* @brief Set mclk frequency and get the actuall APLL frequency
*
* @param mclk_freq_hz Expected mclk frequenct in Hz
* @return
* - Actuall APLL frequency
*/
uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq_hz);
#endif
void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input);
/**
* @brief Check gpio validity and attach to corresponding signal
*
* @param gpio GPIO number
* @param signal_idx Signal index
* @param is_input Is input gpio
* @param is_invert Is invert gpio
*/
void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input, bool is_invert);
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num);
/**
* @brief Check gpio validity and output mclk signal
*
* @param id I2S port id
* @param gpio_num GPIO number
* @param is_apll Is using APLL as clock source
* @param is_invert Is invert the GPIO
* @return
* - ESP_OK Set mclk output gpio success
* - ESP_ERR_INVALID_ARG Invalid GPIO number
*/
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert);
/**
* @brief Attach data out signal and data in signal to a same gpio
*
* @param gpio GPIO number
* @param out_sig_idx Data out signal index
* @param in_sig_idx Data in signal index
*/
void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_sig_idx);
#ifdef __cplusplus
}

View File

@ -5,8 +5,17 @@
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#if CONFIG_I2S_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_std.h"
@ -15,7 +24,7 @@
#include "esp_intr_alloc.h"
#include "esp_check.h"
const static char *TAG = "I2S_STD";
const static char *TAG = "i2s_std";
static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
{
@ -37,7 +46,7 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_PLL_160M ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
clk_info->sclk = (clk_cfg->clk_src == I2S_CLK_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
@ -54,37 +63,20 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c
esp_err_t ret = ESP_OK;
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
/* If Power Management enabled, a PM lock will be created when there is no PM lock or clock source changed */
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (handle->pm_lock == NULL || std_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), TAG, "I2S delete old pm lock failed");
}
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
ESP_RETURN_ON_ERROR(i2s_std_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed");
ESP_LOGD(TAG, "Clock division info: [sclk] %d Hz [mdiv] %d [mclk] %d Hz [bdiv] %d [bclk] %d Hz",
clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Set clock configurations in HAL*/
if (handle->dir == I2S_DIR_TX) {
i2s_hal_set_tx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
} else {
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: clock configuration */
memcpy(&(std_cfg->clk_cfg), clk_cfg, sizeof(i2s_std_clk_config_t));
@ -94,6 +86,10 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c
static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
{
/* Check configuration validity */
ESP_RETURN_ON_FALSE((slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO) || (slot_cfg->slot_mask != I2S_STD_SLOT_LEFT_RIGHT),
ESP_ERR_INVALID_ARG, TAG, "Can't select both left and right slot in mono mode");
/* Update the total slot num and active slot num */
handle->total_slot = 2;
handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2;
@ -108,28 +104,28 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c
}
bool is_slave = handle->role == I2S_ROLE_SLAVE;
/* Share bck and ws signal in full-duplex mode */
if (handle->parent->full_duplex) {
i2s_ll_share_bck_ws(handle->parent->hal.dev, true);
if (handle->controller->full_duplex) {
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
/* Since bck and ws are shared, only tx or rx can be master
Force to set rx as slave to avoid conflict of clock signal */
if (handle->dir == I2S_DIR_RX) {
is_slave = true;
}
} else {
i2s_ll_share_bck_ws(handle->parent->hal.dev, false);
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
}
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Configure the hardware to apply STD format */
if (handle->dir == I2S_DIR_TX) {
i2s_hal_std_set_tx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
i2s_hal_std_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
} else {
i2s_hal_std_set_rx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
i2s_hal_std_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: slot configuration */
i2s_std_config_t *std_cfg = (i2s_std_config_t*)(handle->mode_info);
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
memcpy(&(std_cfg->slot_cfg), slot_cfg, sizeof(i2s_std_slot_config_t));
return ESP_OK;
@ -137,64 +133,72 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c
static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
{
int id = handle->parent->id;
int id = handle->controller->id;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->bclk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->bclk)),
ESP_ERR_INVALID_ARG, TAG, "bclk invalid");
ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)),
ESP_ERR_INVALID_ARG, TAG, "ws invalid");
if (handle->dir == I2S_DIR_TX) {
/* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false);
} else {
/* Set data input GPIO */
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true);
}
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
/* Loopback if dout = din */
if (gpio_cfg->dout != -1 &&
gpio_cfg->dout == gpio_cfg->din) {
gpio_set_direction(gpio_cfg->dout, GPIO_MODE_INPUT_OUTPUT);
i2s_gpio_loopback_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, i2s_periph_signal[id].data_in_sig);
} else if (handle->dir == I2S_DIR_TX) {
/* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
} else {
/* Set data input GPIO */
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
}
if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */
if (handle->dir == I2S_DIR_TX && !handle->parent->full_duplex) {
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->hal.dev);
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
#endif
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
} else {
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
}
} else {
/* mclk only available in master mode */
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, handle->parent->mclk), TAG, "mclk config failed");
/* For "rx + master" mode, select RX signal index for ws and bck */
if (handle->dir == I2S_DIR_RX && !handle->parent->full_duplex) {
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_rx_clk(handle->parent->hal.dev);
#if SOC_I2S_SUPPORTS_APLL
bool is_apll = std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL;
#else
bool is_apll = false;
#endif
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false);
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, is_apll, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
/* For "rx + master" mode, select RX signal index for ws and bck */
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
#endif
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
} else {
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
}
}
/* Update the mode info: gpio configuration */
i2s_std_config_t *std_cfg = (i2s_std_config_t*)(handle->mode_info);
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
return ESP_OK;
}
esp_err_t i2s_init_std_channel(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
{
#if CONFIG_I2S_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
@ -208,11 +212,11 @@ esp_err_t i2s_init_std_channel(i2s_chan_handle_t handle, const i2s_std_config_t
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_slot should be called before i2s_set_clock while initializing, because clock is relay on the slot */
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when the clock source is APLL */
if (std_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
@ -222,14 +226,24 @@ esp_err_t i2s_init_std_channel(i2s_chan_handle_t handle, const i2s_std_config_t
#if SOC_I2S_HW_VERSION_2
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
if (handle->dir == I2S_DIR_TX) {
i2s_ll_tx_enable_std(handle->parent->hal.dev);
i2s_ll_tx_enable_clock(handle->parent->hal.dev);
i2s_ll_tx_enable_std(handle->controller->hal.dev);
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
} else {
i2s_ll_rx_enable_std(handle->parent->hal.dev);
i2s_ll_rx_enable_clock(handle->parent->hal.dev);
i2s_ll_rx_enable_std(handle->controller->hal.dev);
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
}
#endif
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
@ -240,7 +254,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg)
esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
@ -249,24 +263,39 @@ esp_err_t i2s_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_STD, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the clock");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the clock");
i2s_std_config_t *std_cfg = (i2s_std_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(std_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
#if SOC_I2S_SUPPORTS_APLL
i2s_std_config_t *std_cfg = (i2s_std_config_t*)handle->mode_info;
ESP_GOTO_ON_FALSE(std_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && std_cfg->clk_cfg.clk_src == I2S_CLK_PLL_160M) {
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && std_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
if (clk_cfg->clk_src == I2S_CLK_PLL_160M && std_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (std_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
xSemaphoreGive(handle->mutex);
return ESP_OK;
@ -275,7 +304,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
esp_err_t i2s_channel_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
@ -284,9 +313,9 @@ esp_err_t i2s_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_STD, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the slot");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the slot");
i2s_std_config_t *std_cfg = (i2s_std_config_t*)handle->mode_info;
i2s_std_config_t *std_cfg = (i2s_std_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(std_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, slot_cfg), err, TAG, "set i2s standard slot failed");
@ -305,7 +334,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
esp_err_t i2s_channel_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
@ -314,7 +343,7 @@ esp_err_t i2s_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_STD, ESP_ERR_INVALID_ARG, err, TAG, "This handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be stopped before reconfiguring the gpio");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be disabled before reconfiguring the gpio");
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
xSemaphoreGive(handle->mutex);

View File

@ -5,8 +5,17 @@
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#if CONFIG_I2S_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_tdm.h"
@ -15,7 +24,7 @@
#include "esp_intr_alloc.h"
#include "esp_check.h"
const static char *TAG = "I2S_TDM";
const static char *TAG = "i2s_tdm";
// Same with standard mode except total slot number
static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
@ -38,7 +47,7 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_PLL_160M ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_APLL ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
@ -55,37 +64,20 @@ static esp_err_t i2s_tdm_set_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_c
esp_err_t ret = ESP_OK;
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
/* If Power Management enabled, a PM lock will be created when there is no PM lock or clock source changed */
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (handle->pm_lock == NULL || tdm_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), TAG, "I2S delete old pm lock failed");
}
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
ESP_RETURN_ON_ERROR(i2s_tdm_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed");
ESP_LOGD(TAG, "Clock division info: [sclk] %d Hz [mdiv] %d [mclk] %d Hz [bdiv] %d [bclk] %d Hz",
clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk);
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Set clock configurations in HAL*/
if (handle->dir == I2S_DIR_TX) {
i2s_hal_set_tx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
} else {
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: clock configuration */
memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_config_t));
@ -111,28 +103,28 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c
}
bool is_slave = handle->role == I2S_ROLE_SLAVE;
/* Share bck and ws signal in full-duplex mode */
if (handle->parent->full_duplex) {
i2s_ll_share_bck_ws(handle->parent->hal.dev, true);
if (handle->controller->full_duplex) {
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
/* Since bck and ws are shared, only tx or rx can be master
Force to set rx as slave to avoid conflict of clock signal */
if (handle->dir == I2S_DIR_RX) {
is_slave = true;
}
} else {
i2s_ll_share_bck_ws(handle->parent->hal.dev, false);
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
}
portENTER_CRITICAL(&s_i2s.spinlock);
portENTER_CRITICAL(&g_i2s.spinlock);
/* Configure the hardware to apply TDM format */
if (handle->dir == I2S_DIR_TX) {
i2s_hal_tdm_set_tx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
i2s_hal_tdm_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
} else {
i2s_hal_tdm_set_rx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
i2s_hal_tdm_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
portEXIT_CRITICAL(&g_i2s.spinlock);
/* Update the mode info: slot configuration */
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t*)(handle->mode_info);
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
memcpy(&(tdm_cfg->slot_cfg), slot_cfg, sizeof(i2s_tdm_slot_config_t));
return ESP_OK;
@ -140,65 +132,67 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c
static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg)
{
int id = handle->parent->id;
int id = handle->controller->id;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->bclk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->bclk)),
ESP_ERR_INVALID_ARG, TAG, "bclk invalid");
ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)),
ESP_ERR_INVALID_ARG, TAG, "ws invalid");
if (handle->dir == I2S_DIR_TX) {
/* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false);
} else {
/* Set data input GPIO */
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true);
}
/* Loopback if dout = din */
if (gpio_cfg->dout != -1 &&
gpio_cfg->dout == gpio_cfg->din) {
gpio_set_direction(gpio_cfg->dout, GPIO_MODE_INPUT_OUTPUT);
i2s_gpio_loopback_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, i2s_periph_signal[id].data_in_sig);
} else if (handle->dir == I2S_DIR_TX) {
/* Set data output GPIO */
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
} else {
/* Set data input GPIO */
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
}
if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */
if (handle->dir == I2S_DIR_TX && !handle->parent->full_duplex) {
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->hal.dev);
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
#endif
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
} else {
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
}
} else {
/* mclk only available in master mode */
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, handle->parent->mclk), TAG, "mclk config failed");
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, false, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
/* For "rx + master" mode, select RX signal index for ws and bck */
if (handle->dir == I2S_DIR_RX && !handle->parent->full_duplex) {
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_rx_clk(handle->parent->hal.dev);
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
#endif
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
} else {
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false);
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
}
}
/* Update the mode info: gpio configuration */
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t*)(handle->mode_info);
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
return ESP_OK;
}
esp_err_t i2s_init_tdm_channel(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
{
#if CONFIG_I2S_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
@ -212,28 +206,37 @@ esp_err_t i2s_init_tdm_channel(i2s_chan_handle_t handle, const i2s_tdm_config_t
handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_slot should be called before i2s_set_clock while initializing, because clock is relay on the slot */
/* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when the clock source is APLL */
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
#endif
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
#endif
#if SOC_I2S_HW_VERSION_2
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
if (handle->dir == I2S_DIR_TX) {
i2s_ll_tx_enable_tdm(handle->parent->hal.dev);
i2s_ll_tx_enable_clock(handle->parent->hal.dev);
i2s_ll_tx_enable_tdm(handle->controller->hal.dev);
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
} else {
i2s_ll_rx_enable_tdm(handle->parent->hal.dev);
i2s_ll_rx_enable_clock(handle->parent->hal.dev);
i2s_ll_rx_enable_tdm(handle->controller->hal.dev);
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
}
#endif
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
@ -245,7 +248,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg)
esp_err_t i2s_channel_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
@ -254,24 +257,39 @@ esp_err_t i2s_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_TDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the clock");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the clock");
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(tdm_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
#if SOC_I2S_SUPPORTS_APLL
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t*)handle->mode_info;
ESP_GOTO_ON_FALSE(tdm_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && clk_cfg->clk_cfg.clk_src == I2S_CLK_PLL_160M) {
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && clk_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
if (clk_cfg->clk_src == I2S_CLK_PLL_160M && clk_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && clk_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
#ifdef CONFIG_PM_ENABLE
// Create/Re-create power management lock
if (tdm_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
}
#endif //CONFIG_PM_ENABLE
xSemaphoreGive(handle->mutex);
return ESP_OK;
@ -280,7 +298,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg)
esp_err_t i2s_channel_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
@ -289,9 +307,9 @@ esp_err_t i2s_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_TDM, ESP_ERR_INVALID_ARG, err, TAG, "this handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be stopped before reconfiguring the slot");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S should be disabled before reconfiguring the slot");
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t*)handle->mode_info;
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)handle->mode_info;
ESP_GOTO_ON_FALSE(tdm_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, slot_cfg), err, TAG, "set i2s standard slot failed");
@ -301,6 +319,10 @@ esp_err_t i2s_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_con
if (tdm_cfg->slot_cfg.slot_bit_width == slot_bits) {
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "update clock failed");
}
/* Reset queue */
xQueueReset(handle->msg_queue);
xSemaphoreGive(handle->mutex);
return ESP_OK;
@ -309,7 +331,7 @@ err:
return ret;
}
esp_err_t i2s_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg)
esp_err_t i2s_channel_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
@ -318,7 +340,7 @@ esp_err_t i2s_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_con
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->mode == I2S_COMM_MODE_TDM, ESP_ERR_INVALID_ARG, err, TAG, "This handle is not working in standard moded");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be stopped before reconfiguring the gpio");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "Invalid state, I2S should be disabled before reconfiguring the gpio");
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
xSemaphoreGive(handle->mutex);

View File

@ -6,9 +6,7 @@
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "driver/i2s_types.h"
#include "hal/i2s_types.h"
#include "esp_types.h"
@ -18,12 +16,6 @@
extern "C" {
#endif
#ifdef I2S_DRIVER_SELECTED
#error "Only one set of APIs is allowed to use"
#else
#define I2S_DRIVER_SELECTED
#endif
/**
* @brief get default I2S property
*/
@ -37,6 +29,27 @@ extern "C" {
#define I2S_GPIO_UNUSED GPIO_NUM_NC /*!< Used in i2s_gpio_config_t for signals which are not used */
/**
* @brief Group of I2S callbacks
* @note The callbacks are all running under ISR environment
* @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in the function should be in the SRAM as well.
*/
typedef struct {
i2s_isr_callback_t on_recv; /**< Callback of data received event, only for rx channel
* The event data includes DMA buffer address and size that just finished receiving data
*/
i2s_isr_callback_t on_recv_q_ovf; /**< Callback of receiving queue overflowed event, only for rx channel
* The event data includes buffer size that has been overwritten
*/
i2s_isr_callback_t on_sent; /**< Callback of data sent event, only for tx channel
* The event data includes DMA buffer address and size that just finished sending data
*/
i2s_isr_callback_t on_send_q_ovf; /**< Callback of sending queue overflowed evnet, only for tx channel
* The event data includes buffer size that has been overwritten
*/
} i2s_event_callbacks_t;
/**
* @brief I2S controller channel configuration
*/
@ -47,37 +60,51 @@ typedef struct {
/* DMA configurations */
uint32_t dma_desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */
uint32_t dma_frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */
bool auto_clear; /*!< Set to auto clear DMA TX descriptor, i2s will always send zero automatically if no data to send */
bool auto_clear; /*!< Set to auto clear DMA TX buffer, i2s will always send zero automatically if no data to send */
} i2s_chan_config_t;
/**
* @brief Allocate a new i2s channel
* @note The new i2s channel handle will at REGISTERED state after it is allocated successfully.
* @note When the port id in channel configuration is I2S_NUM_AUTO, driver will allocate i2s port automatically
* Otherwise driver will try to allocate the new channel on the selected port.
* @note If both tx_handle and rx_handle are not NULL, it means this I2S port will work at duplex mode,
* and these rx and tx channel will be allocated on a same I2S port as well.
* Note that tx/rx channel will be affected by each other on ESP32 and ESP32S2,
* so please make sure them are working in same condition and have same status(start/stop).
* @note If tx_handle or rx_handle is NULL, it means this I2S port will work at simplex mode,
* For ESP32 and ESP32S2, the whole I2S port will be occupied as well even if only simplex is working.
* For the other targets, another channel on this port will still available.
* @brief I2S channel information
*/
typedef struct {
i2s_port_t id; /*!< I2S port id */
i2s_role_t role; /*!< I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE */
i2s_dir_t dir; /*!< I2S channel direction */
i2s_comm_mode_t mode; /*!< I2S channel communication mode */
i2s_chan_handle_t pair_chan; /*!< I2S pair channel handle in duplex mode, always NULL in simplex mode */
} i2s_chan_info_t;
/**
* @brief Allocate new I2S channel(s)
* @note The new created I2S channel handle will be REGISTERED state after it is allocated successfully.
* @note When the port id in channel configuration is I2S_NUM_AUTO, driver will allocate I2S port automatically
* on one of the i2s controller, otherwise driver will try to allocate the new channel on the selected port.
* @note If both tx_handle and rx_handle are not NULL, it means this I2S controller will work at full-duplex mode,
* the rx and tx channels will be allocated on a same I2S port in this case.
* Note that some configurations of tx/rx channel are shared on ESP32 and ESP32S2,
* so please make sure they are working at same condition and under same status(start/stop).
* Currently, full-duplex mode can't guarantee tx/rx channels write/read synchronously,
* they can only share the clock signals for now.
* @note If tx_handle OR rx_handle is NULL, it means this I2S controller will work at simplex mode.
* For ESP32 and ESP32S2, the whole I2S controller (i.e. both rx and tx channel) will be occupied,
* even if only one of rx or tx channel is registered.
* For the other targets, another channel on this controller will still available.
*
* @param[in] chan_cfg I2S controller channel configurations
* @param[out] tx_handle I2S channel handler used for managing the sending channel(optional)
* @param[out] rx_handle I2S channel handler used for managing the receiving channel(optional)
* @param[out] ret_tx_handle I2S channel handler used for managing the sending channel(optional)
* @param[out] ret_rx_handle I2S channel handler used for managing the receiving channel(optional)
* @return
* - ESP_OK Allocate a new channel success
* - ESP_OK Allocate new channel(s) success
* - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
* - ESP_ERR_INVALID_ARG NULL pointer or illegal parameter in i2s_chan_config_t
* - ESP_ERR_NOT_FOUND No available I2S channel found
*/
esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *tx_handle, i2s_chan_handle_t *rx_handle);
esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *ret_tx_handle, i2s_chan_handle_t *ret_rx_handle);
/**
* @brief Delete the i2s channel
* @note The i2s channel will be stopped to ensure the i2s channel is not at READING or WRITING state.
* @note Only allowed to be called when the i2s channel is at REGISTERED or READY state (i.e., it should stop before deleting it).
* @note Resource will be free automatically if all channels in one port are deleted
*
* @param[in] handle I2S channel handler
@ -87,99 +114,36 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *
esp_err_t i2s_del_channel(i2s_chan_handle_t handle);
/**
* @brief Initialize i2s channel
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
* and the state will be updated to INITIALIZING while during the initialization,
* finally it will be READY if initialization is success, otherwise the state will return to REGISTERED.
* @note Will initialize i2s channel according to the i2s mode
* For different modes, we should input corresponding configurations.
* @brief Get I2S channel information
*
* @param[in] handle I2S channel handler
* @param[in] clk_config Clock configuration, should input correct type of clock configuration according to i2s communication mode that set in 'i2s_chan_config_t'.
* For I2S_COMM_MODE_STD mode, please input 'i2s_std_clk_config_t' type.
* For I2S_COMM_MODE_PDM mode, please input 'i2s_pdm_tx_clk_config_t' type for tx channel and 'i2s_pdm_rx_clk_config_t' type for rx channel.
* For I2S_COMM_MODE_TDM mode, please input 'i2s_tdm_clk_config_t' type.
* @param[in] slot_config Slot configuration, should input correct type of slot configuration according to i2s communication mode that set in 'i2s_chan_config_t'.
* For I2S_COMM_MODE_STD mode, please input 'i2s_std_slot_config_t' type.
* For I2S_COMM_MODE_PDM mode, please input 'i2s_pdm_tx_slot_config_t' type for tx channel and 'i2s_pdm_rx_slot_config_t' type for rx channel.
* For I2S_COMM_MODE_TDM mode, please input 'i2s_tdm_slot_config_t' type.
* @param[out] chan_info I2S channel basic information
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE This channel is not registered
* - ESP_OK Get i2s channel information success
* - ESP_ERR_NOT_FOUND The input handle doesn't match any registered I2S channels, it may not an i2s channel handle or not available any more
* - ESP_ERR_INVALID_ARG The input handle or chan_info pointer is NULL
*/
// esp_err_t i2s_init_channel(i2s_chan_handle_t handle, const void *clk_config, const void *slot_config);
esp_err_t i2s_channel_get_info(i2s_chan_handle_t handle, i2s_chan_info_t *chan_info);
/**
* @brief Reconfigure the I2S clock
* @brief Enable the i2s channel
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
* this function won't change the state.
* @note Normally the clock has been configured after 'i2s_init_channel' is called
* This function is for re-configuring the clock.
* 'i2s_stop_channel' should be called before calling this function if i2s has started.
*
* @param[in] handle I2S channel handler
* @param[in] clk_config Clock configuration, should input correct type of clock configuration according to i2s communication mode
* For I2S_COMM_MODE_STD mode, please input 'i2s_std_clk_config_t' type.
* For I2S_COMM_MODE_PDM mode, please input 'i2s_pdm_tx_clk_config_t' type for tx channel and 'i2s_pdm_rx_clk_config_t' type for rx channel.
* For I2S_COMM_MODE_TDM mode, please input 'i2s_tdm_clk_config_t' type.
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_NOT_SUPPORTED The input communication mode is not supported
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE This channel is not initialized
*/
// esp_err_t i2s_set_clock(i2s_chan_handle_t handle, const void *clk_config);
/**
* @brief Reconfigure the I2S slot
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
* this function won't change the state.
* @note Normally the slot has been configured after 'i2s_init_channel' is called
* This function is for re-configuring the slot
* 'i2s_stop_channel' should be called before calling this function if i2s has started.
*
* @param[in] handle I2S channel handler
* @param[in] slot_config Slot configuration, should input correct type of clock configuration according to i2s communication mode
* For I2S_COMM_MODE_STD mode, please input 'i2s_std_slot_config_t' type.
* For I2S_COMM_MODE_PDM mode, please input 'i2s_pdm_tx_slot_config_t' type for tx channel and 'i2s_pdm_rx_slot_config_t' type for rx channel.
* For I2S_COMM_MODE_TDM mode, please input 'i2s_tdm_slot_config_t' type.
* @return
* - ESP_OK Set slot successfully
* - ESP_ERR_INVALID_ARG NULL pointer or unmatched slot configuration type
* - ESP_ERR_INVALID_STATE This channel is not initialized
*/
// esp_err_t i2s_set_slot(i2s_chan_handle_t handle, const void *slot_config);
/**
* @brief Get I2S event queue handler
* @note Can be called at any time
*
* @param[in] handle I2S channel handler
* @param[in] que_len Queue length (if the queue has not been created yet)
* @return
* - NULL Failed to create the event queue
* - else Event queue handler
*/
QueueHandle_t i2s_get_event_queue(i2s_chan_handle_t handle, uint32_t que_len);
/**
* @brief Start i2s channel
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
* the channel will enter IDLE state once it is started successfully.
* the channel will enter RUNNING state once it is enabled successfully.
* @note Enbale the channel can start the I2S communication on hardware. It will start outputting bclk and ws signal.
* For mclk signal, it will start to output when initialization is finished
*
* @param[in] handle I2S channel handler
* - ESP_OK Start successfully
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE This channel has not initialized or already started
*/
esp_err_t i2s_start_channel(i2s_chan_handle_t handle);
esp_err_t i2s_channel_enable(i2s_chan_handle_t handle);
/**
* @brief Stop i2s channel
* @note Only allowed to be called when the channel state is READY / IDLE / WRITING / READING, (i.e., channel has been initialized)
* the channel will enter READY state once it is stopped successfully.
* @note It will stop bclk and ws signal but not mclk signal
* @brief Disable the i2s channel
* @note Only allowed to be called when the channel state is READY / RUNNING, (i.e., channel has been initialized)
* the channel will enter READY state once it is disabled successfully.
* @note Disable the channel can stop the I2S communication on hardware. It will stop bclk and ws signal but not mclk signal
*
* @param[in] handle I2S channel handler
* @return
@ -187,80 +151,61 @@ esp_err_t i2s_start_channel(i2s_chan_handle_t handle);
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE This channel has not stated
*/
esp_err_t i2s_stop_channel(i2s_chan_handle_t handle);
esp_err_t i2s_channel_disable(i2s_chan_handle_t handle);
/**
* @brief I2S write data
* @note Only allowed to be called when the channel state is IDLE, (i.e., tx channel has been started and is not writing now)
* the channel will enter WRITING state once start to write,
* and it will switch back to IDLE when quit the writing,
* but the IDLE only stands for the software state, it doesn't mean there is no the signal transporting on line.
* @note Only allowed to be called when the channel state is RUNNING, (i.e., tx channel has been started and is not writing now)
* but the RUNNING only stands for the software state, it doesn't mean there is no the signal transporting on line.
*
* @param[in] handle I2S channel handler
* @param[in] src The pointer of sent data buffer
* @param[in] size Max data buffer length
* @param[out] bytes_written Byte number that actually be sent
* @param[in] ticks_to_wait Max block time
* @param[in] timeout_ms Max block time
* @return
* - ESP_OK Write successfully
* - ESP_ERR_INVALID_ARG NULL pointer or this handle is not tx handle
* - ESP_ERR_TIMEOUT Writing timeout, no writing event received from ISR within ticks_to_wait
* - ESP_ERR_INVALID_STATE I2S is not ready to write
*/
esp_err_t i2s_write_channel(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait);
esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, uint32_t timeout_ms);
/**
* @brief I2S read data
* @note Only allowed to be called when the channel state is IDLE
* the channel will enter READING state once start to read,
* and it will switch back to IDLE when quit the reading,
* but the IDLE only stands for the software state, it doesn't mean there is no the signal transporting on line.
* @note Only allowed to be called when the channel state is RUNNING
* but the RUNNING only stands for the software state, it doesn't mean there is no the signal transporting on line.
*
* @param[in] handle I2S channel handler
* @param[in] dest The pointer of receiving data buffer
* @param[in] size Max data buffer length
* @param[out] bytes_read Byte number that actually be read
* @param[in] ticks_to_wait Max block time
* @param[in] timeout_ms Max block time
* @return
* - ESP_OK Read successfully
* - ESP_ERR_INVALID_ARG NULL pointer or this handle is not rx handle
* - ESP_ERR_TIMEOUT Reading timeout, no reading event received from ISR within ticks_to_wait
* - ESP_ERR_INVALID_STATE I2S is not ready to read
*/
esp_err_t i2s_read_channel(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait);
esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms);
/**
* @brief Clear the DMA buffer
* @note Only allowed to be called when the channel state is READY / IDLE / WRITING / READING, (i.e., channel has been initialized)
* it won't change the current channel state.
* @note The legacy data in DMA buffer will be cleared immediately once it is called
* That means i2s will keep send zero if no other data to send
* It has same effect with 'auto_clear' field in slot configuration struct
* but it should be called manually and won't lower the interrupt performance
* @brief Set event callbacks for I2S channel
*
* @note Only allowed to be called when the channel state is REGISTARED / READY, (i.e., before channel starts)
* @note User can deregister a previously registered callback by calling this function and setting the callback member in the `callbacks` structure to NULL.
* @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in the function should be in the SRAM as well. The `user_data` should also reside in SRAM or internal RAM as well.
*
* @param[in] handle I2S channel handler
* @param[in] callbacks Group of callback functions
* @param[in] user_data User data, which will be passed to callback functions directly
* @return
* - ESP_OK Clear successfully
* - ESP_ERR_INVALID_STATE I2S is not initialized
* - ESP_OK Set event callbacks successfully
* - ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument
* - ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTARED or READY
*/
esp_err_t i2s_clear_dma_buffer(i2s_chan_handle_t handle);
/**
* @brief Abort i2s reading or writing function
* @note Only allowed to be called when the channel state is IDLE / WRITING / READING, (i.e., channel has been started)
* it will change to IDLE after aborting the current reading or writing.
* @note Since reading or writing will be blocked for a long time while transporting a large quantity of data
* This function can help to terminate reading/writing in its next reading/writing loop,
* but if reading/writing is bolcked on receiving dma queue(i.e. dma can't send or receive data), this function won't take effect
* And it will only abort for one time, so reading/writing thread won't stop though it is called
*
* @param[in] handle I2S channel handler
* @return
* - ESP_OK Abort successfully
* - ESP_ERR_INVALID_STATE I2S is stopped or not initialized
*/
esp_err_t i2s_abort_reading_writing(i2s_chan_handle_t handle);
esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data);
#ifdef __cplusplus
}

View File

@ -29,7 +29,6 @@ extern "C" {
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_PDM_RX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.mode = I2S_COMM_MODE_PDM, \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
@ -41,32 +40,29 @@ extern "C" {
*/
#define I2S_PDM_RX_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_PLL_160M, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.dn_sample_mode = I2S_PDM_DSR_8S \
}
/**
* @breif I2S slot configuration for pdm rx mode
* @brief I2S slot configuration for pdm rx mode
*/
typedef struct {
/* General fields */
i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */
i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */
i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) , only support 16 bits for PDM mode */
i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
} i2s_pdm_rx_slot_config_t;
/**
* @breif I2S clock configuration for pdm rx mode
* @brief I2S clock configuration for pdm rx mode
*/
typedef struct {
/* General fields */
uint32_t sample_rate_hz; /*!< I2S sample rate */
i2s_clock_src_t clk_src; /*!< Choose clock source */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
/* Particular fields */
i2s_pdm_dsr_t dn_sample_mode; /*!< Down-sampling rate mode */
} i2s_pdm_rx_clk_config_t;
@ -77,23 +73,83 @@ typedef struct {
typedef struct {
gpio_num_t clk; /*!< PDM clk pin, output */
gpio_num_t din; /*!< DATA pin, input */
struct {
uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */
} invert_flags; /*!< GPIO pin invert flags */
} i2s_pdm_rx_gpio_config_t;
typedef struct
{
i2s_pdm_rx_clk_config_t clk_cfg;
i2s_pdm_rx_slot_config_t slot_cfg;
i2s_pdm_rx_gpio_config_t gpio_cfg;
/**
* @brief I2S PDM RX mode major configuration that including clock/slot/gpio configuration
*/
typedef struct {
i2s_pdm_rx_clk_config_t clk_cfg; /*!< PDM RX clock configurations, can be genertated by macro I2S_PDM_RX_CLK_DEFAULT_CONFIG */
i2s_pdm_rx_slot_config_t slot_cfg; /*!< PDM RX slot configurations, can be genertated by macro I2S_PDM_RX_SLOT_DEFAULT_CONFIG */
i2s_pdm_rx_gpio_config_t gpio_cfg; /*!< PDM RX slot configurations, specified by user */
} i2s_pdm_rx_config_t;
/**
* @brief Initialize i2s channel to PDM RX mode
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
*
* @param[in] handle I2S rx channel handler
* @param[in] pdm_rx_cfg Configurations for PDM RX mode, including clock, slot and gpio
* The clock configuration can be generated by the helper macro `I2S_PDM_RX_CLK_DEFAULT_CONFIG`
* The slot configuration can be generated by the helper macro `I2S_PDM_RX_SLOT_DEFAULT_CONFIG`
*
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_NO_MEM No memory for storing the channel information
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
* - ESP_ERR_INVALID_STATE This channel is not registered
*/
esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg);
esp_err_t i2s_init_pdm_rx_channel(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg);
/**
* @brief Reconfigure the I2S clock for PDM RX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
*
* @param[in] handle I2S rx channel handler
* @param[in] clk_cfg PDM RX mode clock configuration, can be generated by `I2S_PDM_RX_CLK_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg);
esp_err_t i2s_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg);
/**
* @brief Reconfigure the I2S slot for PDM RX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
*
* @param[in] handle I2S rx channel handler
* @param[in] slot_cfg PDM RX mode slot configuration, can be generated by `I2S_PDM_RX_SLOT_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_NO_MEM No memory for DMA buffer
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg);
esp_err_t i2s_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg);
esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg);
/**
* @brief Reconfigure the I2S gpio for PDM RX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
*
* @param[in] handle I2S rx channel handler
* @param[in] gpio_cfg PDM RX mode gpio configuration, specified by user
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg);
#endif // SOC_I2S_SUPPORTS_PDM_RX
@ -107,7 +163,6 @@ esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gp
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_PDM_TX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.mode = I2S_COMM_MODE_PDM, \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
@ -129,12 +184,8 @@ esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gp
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_PDM_TX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.mode = I2S_COMM_MODE_PDM, \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.dma_desc_num = 8, \
.dma_frame_num = 200, \
.auto_clear = false, \
.slot_mode = mono_or_stereo, \
.sd_prescale = 0, \
.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
@ -150,12 +201,12 @@ esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gp
* 1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000
* 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz
* 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.
* Otherwise, the second configuration should be adopted.
* @param rate sample rate
*/
#define I2S_PDM_TX_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_PLL_160M, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.up_sample_fp = 960, \
.up_sample_fs = ((rate) / 100), \
@ -181,11 +232,9 @@ esp_err_t i2s_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gp
*/
typedef struct {
/* General fields */
i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */
i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */
i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot), only support 16 bits for PDM mode */
i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
/* Particular fields */
uint32_t sd_prescale; /*!< Sigma-delta filter prescale */
i2s_pdm_sig_scale_t sd_scale; /*!< Sigma-delta filter scaling value */
@ -202,14 +251,13 @@ typedef struct {
} i2s_pdm_tx_slot_config_t;
/**
* @breif I2S clock configuration for pdm tx mode
* @brief I2S clock configuration for pdm tx mode
*/
typedef struct {
/* General fields */
uint32_t sample_rate_hz; /*!< I2S sample rate */
i2s_clock_src_t clk_src; /*!< Choose clock source */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
/* Particular fields */
uint32_t up_sample_fp; /*!< Up-sampling param fp */
uint32_t up_sample_fs; /*!< Up-sampling param fs */
@ -221,22 +269,83 @@ typedef struct {
typedef struct {
gpio_num_t clk; /*!< PDM clk pin, output */
gpio_num_t dout; /*!< DATA pin, output */
struct {
uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */
} invert_flags; /*!< GPIO pin invert flags */
} i2s_pdm_tx_gpio_config_t;
typedef struct
{
i2s_pdm_tx_clk_config_t clk_cfg;
i2s_pdm_tx_slot_config_t slot_cfg;
i2s_pdm_tx_gpio_config_t gpio_cfg;
/**
* @brief I2S PDM TX mode major configuration that including clock/slot/gpio configuration
*/
typedef struct {
i2s_pdm_tx_clk_config_t clk_cfg; /*!< PDM TX clock configurations, can be genertated by macro I2S_PDM_TX_CLK_DEFAULT_CONFIG */
i2s_pdm_tx_slot_config_t slot_cfg; /*!< PDM TX slot configurations, can be genertated by macro I2S_PDM_TX_SLOT_DEFAULT_CONFIG */
i2s_pdm_tx_gpio_config_t gpio_cfg; /*!< PDM TX gpio configurations, specified by user */
} i2s_pdm_tx_config_t;
esp_err_t i2s_init_pdm_tx_channel(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg);
/**
* @brief Initialize i2s channel to PDM TX mode
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
*
* @param[in] handle I2S tx channel handler
* @param[in] pdm_tx_cfg Configurations for PDM TX mode, including clock, slot and gpio
* The clock configuration can be generated by the helper macro `I2S_PDM_TX_CLK_DEFAULT_CONFIG`
* The slot configuration can be generated by the helper macro `I2S_PDM_TX_SLOT_DEFAULT_CONFIG`
*
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_NO_MEM No memory for storing the channel information
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
* - ESP_ERR_INVALID_STATE This channel is not registered
*/
esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg);
esp_err_t i2s_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg);
/**
* @brief Reconfigure the I2S clock for PDM TX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
*
* @param[in] handle I2S tx channel handler
* @param[in] clk_cfg PDM TX mode clock configuration, can be generated by `I2S_PDM_TX_CLK_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg);
esp_err_t i2s_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg);
/**
* @brief Reconfigure the I2S slot for PDM TX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
*
* @param[in] handle I2S tx channel handler
* @param[in] slot_cfg PDM TX mode slot configuration, can be generated by `I2S_PDM_TX_SLOT_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_NO_MEM No memory for DMA buffer
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg);
esp_err_t i2s_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg);
/**
* @brief Reconfigure the I2S gpio for PDM TX mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
*
* @param[in] handle I2S tx channel handler
* @param[in] gpio_cfg PDM TX mode gpio configuration, specified by user
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg);
#endif // SOC_I2S_SUPPORTS_PDM_TX

View File

@ -30,12 +30,12 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.msb_right = false, \
.msb_right = true, \
}
/**
@ -48,12 +48,12 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.msb_right = false, \
.msb_right = true, \
}
/**
@ -65,12 +65,12 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.msb_right = false, \
.msb_right = true, \
}
#else
@ -83,11 +83,11 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false \
@ -103,11 +103,11 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false \
@ -122,11 +122,11 @@ extern "C" {
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
.slot_sel = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false \
@ -141,12 +141,12 @@ extern "C" {
*/
#define I2S_STD_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_PLL_160M, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
}
/**
* @breif I2S slot configuration for standard mode
* @brief I2S slot configuration for standard mode
*/
typedef struct {
/* General fields */
@ -155,10 +155,10 @@ typedef struct {
i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
/* Particular fields */
i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
bool bit_shift; /*!< Set to enbale bit shift in Philip mode */
i2s_std_slot_sel_t slot_sel; /*!< Select the left, right or both slot */
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
bool msb_right; /*!< Set to place right channel data at the MSB in the FIFO */
#else
@ -169,7 +169,7 @@ typedef struct {
} i2s_std_slot_config_t;
/**
* @breif I2S clock configuration for standard mode
* @brief I2S clock configuration for standard mode
*/
typedef struct {
/* General fields */
@ -187,21 +187,87 @@ typedef struct {
gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */
gpio_num_t dout; /*!< DATA pin, output */
gpio_num_t din; /*!< DATA pin, input */
struct {
uint32_t mclk_inv: 1; /*!< Set 1 to invert the mclk output */
uint32_t bclk_inv: 1; /*!< Set 1 to invert the bclk input/output */
uint32_t ws_inv: 1; /*!< Set 1 to invert the ws input/output */
} invert_flags; /*!< GPIO pin invert flags */
} i2s_std_gpio_config_t;
/**
* @brief I2S standard mode major configuration that including clock/slot/gpio configuration
*/
typedef struct {
i2s_std_clk_config_t clk_cfg; /*!< Standard mode clock configuration */
i2s_std_slot_config_t slot_cfg; /*!< Standard mode slot configuration */
i2s_std_gpio_config_t gpio_cfg; /*!< Standard mode gpio configuration */
i2s_std_clk_config_t clk_cfg; /*!< Standard mode clock configuration, can be generated by macro I2S_STD_CLK_DEFAULT_CONFIG */
i2s_std_slot_config_t slot_cfg; /*!< Standard mode slot configuration, can be generated by macros I2S_STD_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIP/MSB/PCM */
i2s_std_gpio_config_t gpio_cfg; /*!< Standard mode gpio configuration, specified by user */
} i2s_std_config_t;
esp_err_t i2s_init_std_channel(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg);
/**
* @brief Initialize i2s channel to standard mode
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
*
* @param[in] handle I2S channel handler
* @param[in] std_cfg Configurations for standard mode, including clock, slot and gpio
* The clock configuration can be generated by the helper macro `I2S_STD_CLK_DEFAULT_CONFIG`
* The slot configuration can be generated by the helper macro `I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG`,
* `I2S_STD_PCM_SLOT_DEFAULT_CONFIG` or `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`
*
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_NO_MEM No memory for storing the channel information
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
* - ESP_ERR_INVALID_STATE This channel is not registered
*/
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg);
esp_err_t i2s_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg);
/**
* @brief Reconfigure the I2S clock for standard mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] clk_cfg Standard mode clock configuration, can be generated by `I2S_STD_CLK_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg);
esp_err_t i2s_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg);
/**
* @brief Reconfigure the I2S slot for standard mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] slot_cfg Standard mode slot configuration, can be generated by `I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG`,
* `I2S_STD_PCM_SLOT_DEFAULT_CONFIG` and `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`.
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_NO_MEM No memory for DMA buffer
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg);
esp_err_t i2s_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg);
/**
* @brief Reconfigure the I2S gpio for standard mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] gpio_cfg Standard mode gpio configuration, specified by user
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg);
#ifdef __cplusplus
}

View File

@ -31,10 +31,10 @@ extern "C" {
* @param mask active slot mask
*/
#define I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \
.mode = I2S_COMM_MODE_TDM, \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mask), \
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
.ws_pol = false, \
.bit_shift = true, \
@ -42,7 +42,6 @@ extern "C" {
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
@ -53,10 +52,10 @@ extern "C" {
* @param mask active slot mask
*/
#define I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \
.mode = I2S_COMM_MODE_TDM, \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mask), \
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
.ws_pol = false, \
.bit_shift = false, \
@ -64,7 +63,6 @@ extern "C" {
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false ,\
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
@ -75,10 +73,10 @@ extern "C" {
* @param mask active slot mask
*/
#define I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \
.mode = I2S_COMM_MODE_TDM, \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mask), \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
@ -86,7 +84,6 @@ extern "C" {
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
@ -97,10 +94,10 @@ extern "C" {
* @param mask active slot mask
*/
#define I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \
.mode = I2S_COMM_MODE_TDM, \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mask), \
.ws_width = (bits_per_sample), \
.ws_pol = true, \
.bit_shift = true, \
@ -108,7 +105,6 @@ extern "C" {
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
@ -120,22 +116,22 @@ extern "C" {
*/
#define I2S_TDM_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_PLL_160M, \
.clk_src = I2S_CLK_SRC_DEFAULT, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
}
/**
* @breif I2S slot configuration for tdm mode
* @brief I2S slot configuration for tdm mode
*/
typedef struct {
/* General fields */
i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */
i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample) */
i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) */
i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */
/* Particular fields */
uint32_t ws_width; /*!< WS signal width ((i.e. the number of bclk ticks that ws signal is high)) */
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
bool bit_shift; /*!< Set true to enable bit shift in Philip mode */
@ -144,12 +140,11 @@ typedef struct {
bool bit_order_lsb; /*!< Set true to enable lsb first */
bool skip_mask; /*!< Set true 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 */
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
uint32_t total_slot; /*!< I2S total number of slots. If it is smaller than the biggest activated channel number, it will be set to this number automatically. */
} i2s_tdm_slot_config_t;
/**
* @breif I2S clock configuration for tdm mode
* @brief I2S clock configuration for tdm mode
*/
typedef struct {
/* General fields */
@ -167,21 +162,87 @@ typedef struct {
gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */
gpio_num_t dout; /*!< DATA pin, output */
gpio_num_t din; /*!< DATA pin, input */
struct {
uint32_t mclk_inv: 1; /*!< Set 1 to invert the mclk output */
uint32_t bclk_inv: 1; /*!< Set 1 to invert the bclk input/output */
uint32_t ws_inv: 1; /*!< Set 1 to invert the ws input/output */
} invert_flags; /*!< GPIO pin invert flags */
} i2s_tdm_gpio_config_t;
/**
* @brief I2S TDM mode major configuration that including clock/slot/gpio configuration
*/
typedef struct {
i2s_tdm_clk_config_t clk_cfg; /*!< TDM mode clock configuration */
i2s_tdm_slot_config_t slot_cfg; /*!< TDM mode slot configuration */
i2s_tdm_gpio_config_t gpio_cfg; /*!< TDM mode gpio configuration */
i2s_tdm_clk_config_t clk_cfg; /*!< TDM mode clock configuration, can be generated by macro I2S_TDM_CLK_DEFAULT_CONFIG */
i2s_tdm_slot_config_t slot_cfg; /*!< TDM mode slot configuration, can be generated by macros I2S_TDM_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIP/MSB/PCM_SHORT/PCM_LONG */
i2s_tdm_gpio_config_t gpio_cfg; /*!< TDM mode gpio configuration, specified by user */
} i2s_tdm_config_t;
esp_err_t i2s_init_tdm_channel(i2s_chan_handle_t handle, const i2s_tdm_config_t *std_cfg);
/**
* @brief Initialize i2s channel to TDM mode
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
*
* @param[in] handle I2S channel handler
* @param[in] tdm_cfg Configurations for TDM mode, including clock, slot and gpio
* The clock configuration can be generated by the helper macro `I2S_TDM_CLK_DEFAULT_CONFIG`
* The slot configuration can be generated by the helper macro `I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG`,
* `I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG`, `I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG` or `I2S_TDM_MSB_SLOT_DEFAULT_CONFIG`
*
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_NO_MEM No memory for storing the channel information
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
* - ESP_ERR_INVALID_STATE This channel is not registered
*/
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg);
esp_err_t i2s_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg);
/**
* @brief Reconfigure the I2S clock for TDM mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] clk_cfg Standard mode clock configuration, can be generated by `I2S_TDM_CLK_DEFAULT_CONFIG`
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg);
esp_err_t i2s_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg);
/**
* @brief Reconfigure the I2S slot for TDM mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] slot_cfg Standard mode slot configuration, can be generated by `I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG`,
* `I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG`, `I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG` or `I2S_TDM_MSB_SLOT_DEFAULT_CONFIG`.
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_NO_MEM No memory for DMA buffer
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg);
esp_err_t i2s_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg);
/**
* @brief Reconfigure the I2S gpio for TDM mode
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
*
* @param[in] handle I2S channel handler
* @param[in] gpio_cfg Standard mode gpio configuration, specified by user
* @return
* - ESP_OK Set clock successfully
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
*/
esp_err_t i2s_channel_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg);
#ifdef __cplusplus

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2S controller port number, the max port number is (SOC_I2S_NUM -1).
*/
typedef enum {
I2S_NUM_0 = 0, /*!< I2S controller port 0 */
#if SOC_I2S_NUM > 1
I2S_NUM_1 = 1, /*!< I2S controller port 1 */
#endif
I2S_NUM_AUTO, /*!< Select whichever port is available */
} i2s_port_t;
/**
* @brief I2S controller communication mode
*/
typedef enum {
I2S_COMM_MODE_STD, /*!< I2S controller using standard communication mode, support philip/MSB/PCM format */
#if SOC_I2S_SUPPORTS_PDM
I2S_COMM_MODE_PDM, /*!< I2S controller using PDM communication mode, support PDM output or input */
#endif
#if SOC_I2S_SUPPORTS_TDM
I2S_COMM_MODE_TDM, /*!< I2S controller using TDM communication mode, support up to 16 slots per frame */
#endif
I2S_COMM_MODE_NONE, /*!< Unspecified I2S controller mode */
} i2s_comm_mode_t;
/**
* @brief The multiple of mclk to sample rate
*/
typedef enum {
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;
/**
* @brief Event structure used in I2S event queue
*/
typedef struct {
void *data; /**< The pointer of DMA buffer that just finished sending or receiving for `on_recv` and `on_sent` callback
* NULL for `on_recv_q_ovf` and `on_send_q_ovf` callback
*/
size_t size; /**< The buffer size of DMA buffer when success to send or receive,
* also the buffer size that dropped when queue overflow.
* It is related to the dma_frame_num and data_bit_width, typically it is fixed when data_bit_width is not changed.
*/
} i2s_event_data_t;
typedef struct i2s_channel_t *i2s_chan_handle_t; /*!< i2s channel handle, the control unit of the i2s driver*/
/**
* @brief I2S event callback
* @param[in] handle I2S channel handle, created from `i2s_new_channel()`
* @param[in] event I2S event data
* @param[in] user_ctx User registered context, passed from `i2s_channel_register_event_callback()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
typedef bool (*i2s_isr_callback_t)(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
#ifdef __cplusplus
}
#endif

View File

@ -10,7 +10,6 @@
#pragma once
#include "esp_err.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
@ -44,6 +43,14 @@ esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name);
*/
esp_err_t i2s_platform_release_occupation(int id);
/**
* @brief This function is only used for getting DMA buffer offset in `test_i2s_iram.c`
*
* @return
* - The offset of DMA buffers in the `i2s_chan_handle_t` struct (unit: bytes)
*/
size_t i2s_platform_get_dma_buffer_offset(void);
#ifdef __cplusplus
}
#endif

View File

@ -257,7 +257,7 @@ static void adc_dma_init(void)
static void continuous_adc_test(void)
{
uint16_t *adcReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
size_t bytesRead;
uint32_t bytesRead;
for (int loop = 0; loop < 10; loop++) {
for (int level = 0; level <= 1; level++) {
if (level == 0) {

View File

@ -1,30 +0,0 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import glob
import os
import ttfw_idf
from tiny_test_fw import Utility
@ttfw_idf.idf_component_unit_test(env_tag='COMPONENT_UT_GENERIC', target=['esp32', 'esp32s2', 'esp32s3', 'esp32c3'])
def test_component_ut_i2s(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
# Get the names of all configs (sdkconfig.ci.* files)
config_files = glob.glob(os.path.join(os.path.dirname(__file__), 'sdkconfig.ci.*'))
config_names = [os.path.basename(s).replace('sdkconfig.ci.', '') for s in config_files]
# Run test once with binaries built for each config
for name in config_names:
Utility.console_log(f'Checking config "{name}"... ', end='')
dut = env.get_dut('i2s', 'components/driver/test_apps/i2s', app_config_name=name)
dut.start_app()
stdout = dut.expect('Press ENTER to see the list of tests', full_stdout=True)
dut.write('*')
stdout = dut.expect("Enter next test, or 'enter' to see menu", full_stdout=True, timeout=30)
ttfw_idf.ComponentUTResult.parse_result(stdout,ttfw_idf.TestFormat.UNITY_BASIC)
env.close_dut(dut.name)
Utility.console_log(f'Test config "{name}" done')
if __name__ == '__main__':
test_component_ut_i2s()

View File

@ -1,16 +0,0 @@
set(srcs "test_app_main.c")
if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "test_i2s.c"
"test_i2s_legacy.c")
endif()
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES driver hal soc unity)
if(CONFIG_SOC_I2S_SUPPORTED)
target_link_libraries(${COMPONENT_LIB} INTERFACE
"-u test_app_include_i2s"
"-u test_app_include_i2s_legacy")
endif()

View File

@ -1,538 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* I2S test environment UT_T1_I2S:
* We use internal signals instead of external wiring, but please keep the following IO connections, or connect nothing to prevent the signal from being disturbed.
* connect GPIO15 and GPIO19, GPIO25(ESP32)/GPIO17(ESP32-S2) and GPIO26, GPIO21 and GPIO22(ESP32)/GPIO20(ESP32-S2)
* Please do not connect GPIO32(ESP32) any pull-up resistors externally, it will be used to test i2s adc function.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "esp_err.h"
#include "unity.h"
#include "math.h"
#include "esp_rom_gpio.h"
#include "soc/i2s_periph.h"
#include "driver/i2s_std.h"
#if SOC_I2S_SUPPORTS_PDM
#include "driver/i2s_pdm.h"
#endif
#if SOC_I2S_SUPPORTS_TDM
#include "driver/i2s_tdm.h"
#endif
#include "hal/i2s_hal.h"
#include "esp_private/i2s_platform.h"
#if SOC_PCNT_SUPPORTED
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#endif
#define SAMPLE_RATE (48000)
#define SAMPLE_BITS (16)
#if CONFIG_IDF_TARGET_ESP32
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 21
#define SLAVE_WS_IO 22
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#define ADC1_CHANNEL_4_IO 32
#elif CONFIG_IDF_TARGET_ESP32S2
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#elif CONFIG_IDF_TARGET_ESP32C3
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#elif CONFIG_IDF_TARGET_ESP32S3
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#endif
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
#define I2S_TEST_MASTER_DEFAULT_PIN { \
.mclk = MASTER_MCK_IO, \
.bclk = MASTER_BCK_IO, \
.ws = MASTER_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO \
}
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
.mclk = -1, \
.bclk = SLAVE_BCK_IO, \
.ws = SLAVE_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO \
}
// This empty function is used to force the compiler link this file
void test_app_include_i2s(void)
{
}
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
static void i2s_test_io_config(int mode)
{
// Connect internal signals using IO matrix.
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_BCK_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[DATA_OUT_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_BCK_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(DATA_OUT_IO, GPIO_MODE_INPUT_OUTPUT);
switch (mode) {
#if SOC_I2S_NUM > 1
case I2S_TEST_MODE_SLAVE_TO_MASTER: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
case I2S_TEST_MODE_MASTER_TO_SLAVE: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
}
break;
#endif
case I2S_TEST_MODE_LOOPBACK: {
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
default: {
TEST_FAIL_MESSAGE("error: mode not supported");
}
break;
}
}
static void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan)
{
#define I2S_SEND_BUF_LEN 100
#define I2S_RECV_BUF_LEN 10000
size_t bytes_write = 0;
size_t bytes_read = 0;
bool is_success = false;
uint8_t *send_buf = (uint8_t *)calloc(I2S_SEND_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(send_buf);
uint8_t *recv_buf = (uint8_t *)calloc(I2S_RECV_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(recv_buf);
for (int i = 0; i < I2S_SEND_BUF_LEN; i++) {
send_buf[i] = i + 1;
}
// write data to slave
TEST_ESP_OK(i2s_write_channel(tx_chan, send_buf, I2S_SEND_BUF_LEN, &bytes_write, 1000 / portTICK_PERIOD_MS));
TEST_ESP_OK(i2s_read_channel(rx_chan, recv_buf, I2S_RECV_BUF_LEN, &bytes_read, 1000 / portTICK_PERIOD_MS));
TEST_ASSERT_EQUAL_INT32(I2S_SEND_BUF_LEN, bytes_write);
TEST_ASSERT_EQUAL_INT32(I2S_RECV_BUF_LEN, bytes_read);
// test the read data right or not
for (int i = 0, j = 0; i < (I2S_RECV_BUF_LEN - I2S_SEND_BUF_LEN); i++) {
if (recv_buf[i] == 1) {
for (j = 1; (j < I2S_SEND_BUF_LEN) && (recv_buf[i+j] == j + 1); j++) {}
if (j == I2S_SEND_BUF_LEN) {
is_success = true;
goto finish;
}
i += j;
}
}
finish:
free(send_buf);
free(recv_buf);
TEST_ASSERT(is_success);
}
// To check if the software logic of I2S driver is correct
TEST_CASE("I2S basic driver apply, delete test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_cfg));
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_start_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
/* Duplex channel basic test */
chan_cfg.id = I2S_NUM_0; // Specify port id to I2S port 0
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Repeat to check if a same port can be allocated again */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Hold the occupation */
TEST_ESP_OK(i2s_platform_acquire_occupation(I2S_NUM_0, "test_i2s"));
TEST_ASSERT(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle) == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(i2s_platform_release_occupation(I2S_NUM_0));
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
TEST_CASE("I2S memory leak test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
/* The first operation will always take some memory */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
int memory_left = esp_get_free_heap_size();
printf("\r\nHeap size before: %d\n", memory_left);
for (int i = 0; i < 100; i++) {
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
TEST_ASSERT(memory_left == esp_get_free_heap_size());
}
printf("\r\nHeap size after: %d\n", esp_get_free_heap_size());
}
TEST_CASE("I2S loopback test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
i2s_test_io_config(I2S_TEST_MODE_LOOPBACK);
TEST_ESP_OK(i2s_start_channel(tx_handle));
TEST_ESP_OK(i2s_start_channel(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_NUM > 1
TEST_CASE("I2S master write slave read test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_mst_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_slv_cfg));
i2s_test_io_config(I2S_TEST_MODE_MASTER_TO_SLAVE);
TEST_ESP_OK(i2s_start_channel(tx_handle));
TEST_ESP_OK(i2s_start_channel(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
TEST_CASE("I2S master read slave write test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_init_std_channel(tx_handle, &std_slv_cfg));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_mst_cfg));
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
TEST_ESP_OK(i2s_start_channel(tx_handle));
TEST_ESP_OK(i2s_start_channel(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif
/*------------------------------ Clock Test --------------------------------*/
#if SOC_PCNT_SUPPORTED
#define TEST_I2S_PERIOD_MS 100
static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_config_t* clk_cfg)
{
TEST_ASSERT_NOT_NULL(rx_chan);
TEST_ASSERT_NOT_NULL(clk_cfg);
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = MASTER_WS_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
// Reconfig GPIO signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
// Test common sample rate
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000,
128000, 144000, 196000};
int real_pulse = 0;
for (int i = 0; i < 15; i++) {
int expt_pulse = (int)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
clk_cfg->sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_reconfig_std_clock(rx_chan, clk_cfg));
TEST_ESP_OK(i2s_start_channel(rx_chan));
vTaskDelay(1); // Waiting for hardware totally started
// pcnt will count the pulse number on WS signal in 100ms
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
printf("[%d Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ESP_OK(i2s_stop_channel(rx_chan));
// Check if the error between real pulse number and expected pulse number is within 1%
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
}
}
TEST_CASE("I2S D2CLK clock test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_SUPPORTS_APLL
TEST_CASE("I2S APLL clock test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
std_cfg.clk_cfg.clk_src = I2S_CLK_APLL;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif // SOC_I2S_SUPPORTS_APLL
#endif // SOC_PCNT_SUPPORTED
static void i2s_event_monitor(void *args)
{
i2s_chan_handle_t rx_handle = *((i2s_chan_handle_t *)args);
QueueHandle_t evt_que = i2s_get_event_queue(rx_handle, 16);
TEST_ASSERT_NOT_NULL(evt_que);
i2s_event_t evnet;
while (1) {
xQueueReceive(evt_que, &evnet, portMAX_DELAY);
if (evnet.type == I2S_EVENT_RX_Q_OVF) {
break;
}
}
vTaskDelete(NULL);
}
TEST_CASE("I2S package lost test", "[i2s]")
{
/* Steps of calculate appropriate parameters of I2S buffer:
* Known by user: sample_rate = 144k, data_bit_width = 32, slot_num = 2, polling_cycle = 10 ms
* 1. dma_buffer_size = dma_frame_num * slot_num * data_bit_width / 8 <= 4092
* dma_frame_num <= 511, dma_frame_num is as big as possible.
* interrupt_interval = dma_frame_num / sample_rate = 3.549 ms
* 2. dma_desc_num > polling_cycle / interrupt_interval = cell(2.818) = 3
* 3. recv_buffer_size > dma_desc_num * dma_buffer_size = 3 * 4092 = 12276 bytes */
#define TEST_RECV_BUF_LEN 12276
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 3;
chan_cfg.dma_frame_num = 511;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_std_channel(rx_handle, &std_cfg));
TaskHandle_t h_monitor_task;
xTaskCreate(i2s_event_monitor, "event monitor task", 4096, &rx_handle, 5, &h_monitor_task);
uint32_t test_freq[] = {16000, 32000, 48000, 64000, 96000, 128000, 144000};
uint32_t test_num = sizeof(test_freq) / sizeof(uint32_t);
uint8_t *data = (uint8_t *)calloc(TEST_RECV_BUF_LEN, sizeof(uint8_t));
size_t bytes_read = 0;
int i;
for (i = 0; i < test_num; i++) {
printf("Testing %d Hz sample rate\n", test_freq[i]);
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_reconfig_std_clock(rx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_start_channel(rx_handle));
for (int j = 0; j < 10; j++) {
TEST_ESP_OK(i2s_read_channel(rx_handle, (void *)data, TEST_RECV_BUF_LEN, &bytes_read, portMAX_DELAY));
// To simulate 10ms delay caused by other statements like data process
vTaskDelay(1);
}
TEST_ESP_OK(i2s_stop_channel(rx_handle));
if (eTaskGetState(h_monitor_task) == eDeleted) {
printf("package lost detected at %d Hz\n", test_freq[i]);
goto finish;
}
}
vTaskDelete(h_monitor_task);
finish:
TEST_ESP_OK(i2s_del_channel(rx_handle));
free(data);
// Test failed if package lost within 96000
TEST_ASSERT(i == test_num);
}

View File

@ -0,0 +1,18 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
--elf-file ${CMAKE_BINARY_DIR}/i2s_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |

View File

@ -0,0 +1,7 @@
set(srcs "test_app_main.c"
"test_i2s.c"
"test_i2s_iram.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "../../"
WHOLE_ARCHIVE)

View File

@ -0,0 +1,855 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "unity.h"
#include "math.h"
#include "esp_rom_gpio.h"
#include "soc/i2s_periph.h"
#include "driver/i2s_std.h"
#if SOC_I2S_SUPPORTS_PDM
#include "driver/i2s_pdm.h"
#endif
#if SOC_I2S_SUPPORTS_TDM
#include "driver/i2s_tdm.h"
#endif
#include "hal/i2s_hal.h"
#include "esp_private/i2s_platform.h"
#if SOC_PCNT_SUPPORTED
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#endif
#include "test_inc/test_i2s.h"
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
#define I2S_TEST_MASTER_DEFAULT_PIN { \
.mclk = MASTER_MCK_IO, \
.bclk = MASTER_BCK_IO, \
.ws = MASTER_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
.mclk = -1, \
.bclk = SLAVE_BCK_IO, \
.ws = SLAVE_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
static void i2s_test_io_config(int mode)
{
// Connect internal signals using IO matrix.
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_BCK_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[DATA_OUT_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_BCK_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(DATA_OUT_IO, GPIO_MODE_INPUT_OUTPUT);
switch (mode) {
#if SOC_I2S_NUM > 1
case I2S_TEST_MODE_SLAVE_TO_MASTER: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
case I2S_TEST_MODE_MASTER_TO_SLAVE: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
}
break;
#endif
case I2S_TEST_MODE_LOOPBACK: {
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
default: {
TEST_FAIL_MESSAGE("error: mode not supported");
}
break;
}
}
static void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan)
{
#define I2S_SEND_BUF_LEN 100
#define I2S_RECV_BUF_LEN 10000
size_t bytes_write = 0;
size_t bytes_read = 0;
bool is_success = false;
uint8_t *send_buf = (uint8_t *)calloc(I2S_SEND_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(send_buf);
uint8_t *recv_buf = (uint8_t *)calloc(I2S_RECV_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(recv_buf);
for (int i = 0; i < I2S_SEND_BUF_LEN; i++) {
send_buf[i] = i + 1;
}
// write data to slave
TEST_ESP_OK(i2s_channel_write(tx_chan, send_buf, I2S_SEND_BUF_LEN, &bytes_write, 1000));
TEST_ESP_OK(i2s_channel_read(rx_chan, recv_buf, I2S_RECV_BUF_LEN, &bytes_read, 1000));
TEST_ASSERT_EQUAL_INT32(I2S_SEND_BUF_LEN, bytes_write);
TEST_ASSERT_EQUAL_INT32(I2S_RECV_BUF_LEN, bytes_read);
// test the read data right or not
for (int i = 0, j = 0; i < (I2S_RECV_BUF_LEN - I2S_SEND_BUF_LEN); i++) {
if (recv_buf[i] == 1) {
for (j = 1; (j < I2S_SEND_BUF_LEN) && (recv_buf[i+j] == j + 1); j++) {}
if (j == I2S_SEND_BUF_LEN) {
is_success = true;
goto finish;
}
i += j;
}
}
finish:
free(send_buf);
free(recv_buf);
TEST_ASSERT(is_success);
}
// To check if the software logic of I2S driver is correct
TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_chan_info_t chan_info;
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_NONE);
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_STD);
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ASSERT(i2s_channel_get_info(tx_handle, &chan_info) == ESP_ERR_NOT_FOUND);
/* Duplex channel basic test */
chan_cfg.id = I2S_NUM_0; // Specify port id to I2S port 0
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.pair_chan == rx_handle);
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Repeat to check if a same port can be allocated again */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Hold the occupation */
TEST_ESP_OK(i2s_platform_acquire_occupation(I2S_NUM_0, "test_i2s"));
TEST_ASSERT(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle) == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(i2s_platform_release_occupation(I2S_NUM_0));
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
static volatile bool task_run_flag;
static void i2s_read_task(void *args) {
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)args;
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(recv_buf);
size_t recv_size = 0;
esp_err_t ret = ESP_OK;
uint32_t cnt = 1;
while (task_run_flag) {
ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300);
if (ret == ESP_ERR_TIMEOUT) {
printf("Read timeout count: %d\n", cnt++);
}
}
free(recv_buf);
vTaskDelete(NULL);
}
static void i2s_write_task(void *args) {
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(send_buf);
size_t send_size = 0;
esp_err_t ret = ESP_OK;
uint32_t cnt = 1;
while (task_run_flag) {
ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300);
if (ret == ESP_ERR_TIMEOUT) {
printf("Write timeout count: %d\n", cnt++);
}
}
free(send_buf);
vTaskDelete(NULL);
}
static void i2s_reconfig_task(void *args) {
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
i2s_chan_info_t chan_info;
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
i2s_chan_handle_t rx_handle = chan_info.pair_chan;
int cnt = 1;
while (task_run_flag) {
/* Reconfig the slot while reading / writing */
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the slot...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &slot_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
/* Reconfig the clock while reading / writing */
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE / 2);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the clock...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &clk_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &clk_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
/* Reconfig the gpio while reading / writing */
i2s_std_gpio_config_t gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_WS_IO,
.ws = MASTER_BCK_IO,
.dout = DATA_IN_IO,
.din = DATA_IN_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
};
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the gpio...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(tx_handle, &gpio_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(rx_handle, &gpio_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
cnt++;
}
vTaskDelete(NULL);
}
TEST_CASE("I2S_thread_concurrent_safety_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
/* Enable the channels before creating reading/writing task*/
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
task_run_flag = true;
/* reading task to keep reading */
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL);
/* writing task to keep writing */
xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL);
/* reconfig taks to reconfigurate the settings every 200 ms */
xTaskCreate(i2s_reconfig_task, "i2s_reconfig_task", 4096, tx_handle, 6, NULL);
/* Wait 3 seconds to see if any failures occur */
vTaskDelay(pdMS_TO_TICKS(4000));
/* Stop those three tasks */
task_run_flag = false;
/* Wait for the three thread deleted */
vTaskDelay(pdMS_TO_TICKS(1000));
/* Disable the channels, they will keep waiting until the current reading / writing finished */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
/* Delete the channels */
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
static uint32_t get_start_index(uint16_t *buf, uint32_t len, uint32_t start_val)
{
uint32_t i = 0;
for (i = 0; i < len; i++) {
if (buf[i] == start_val) {
printf("%d %d %d %d %d %d %d %d\n",
buf[i], buf[i+1], buf[i+2], buf[i+3],
buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
break;
}
}
return i;
}
/**
* @brief Test mono and stereo mode of I2S by loopback
* @note Only rx channel distinguish left mono and right mono, tx channel does not
* @note 1. Check switch mono/stereo by 'i2s_set_clk'
* 2. Check rx right mono and left mono (requiring tx works in stereo mode)
* 3. Check tx mono (requiring rx works in stereo mode)
*/
TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
{
#define WRITE_BUF_LEN 2000
#define READ_BUF_LEN 4000
#define RETEY_TIMES 3
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 8;
chan_cfg.dma_frame_num = 128;
i2s_std_config_t tx_std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
// In stereo mode
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_std_config_t rx_std_cfg = tx_std_cfg;
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &rx_std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &tx_std_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
uint16_t *w_buf = calloc(1, WRITE_BUF_LEN);
uint16_t *r_buf = calloc(1, READ_BUF_LEN);
size_t w_bytes = 0;
size_t r_bytes = 0;
uint32_t index = 0;
uint32_t retry = 0;
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
w_buf[n] = n%100;
}
/* rx right mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x01[R] 0x03[R] ... */
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 50); retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
#endif
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
}
printf("Data start index: %d\n", index);
TEST_ASSERT(index < READ_BUF_LEN / 2 - 50);
for (int16_t j = 1; j < 100; j += 2) {
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j);
}
printf("rx right mono test passed\n");
/* rx left mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[R] 0x02[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 50); retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
#endif
index = get_start_index(r_buf, READ_BUF_LEN / 2, 2);
}
printf("Data start index: %d\n", index);
TEST_ASSERT(index < READ_BUF_LEN / 2 - 50);
for (int16_t j = 2; j < 100; j += 2) {
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j);
}
printf("rx left mono test passed\n");
/* tx/rx stereo test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 100); retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
}
printf("Data start index: %d\n", index);
TEST_ASSERT(index < READ_BUF_LEN / 2 - 100);
for (int16_t j = 1; j < 100; j ++) {
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j); // receive all number
}
printf("tx/rx stereo test passed\n");
#if !CONFIG_IDF_TARGET_ESP32 // the 16 bit channel sequence on ESP32 is incorrect
/* tx mono rx stereo test
* tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ...
* rx receive: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
i2s_std_slot_config_t std_slot = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_slot));
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 100); retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
}
printf("Data start index: %d\n", index);
TEST_ASSERT(index < READ_BUF_LEN / 2 - 200);
for (int16_t j = 1; j < 100; j ++) {
TEST_ASSERT_EQUAL_INT16(r_buf[index], j);
index += 2;
}
printf("tx mono rx stereo test passed\n");
#endif
free(w_buf);
free(r_buf);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
TEST_CASE("I2S_memory_leak_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
/* The first operation will always take some memory */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
int memory_left = esp_get_free_heap_size();
printf("\r\nHeap size before: %d\n", memory_left);
for (int i = 0; i < 30; i++) {
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
TEST_ASSERT(memory_left == esp_get_free_heap_size());
}
printf("\r\nHeap size after: %d\n", esp_get_free_heap_size());
}
TEST_CASE("I2S_loopback_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_test_io_config(I2S_TEST_MODE_LOOPBACK);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_NUM > 1
TEST_CASE("I2S_master_write_slave_read_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_mst_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_slv_cfg));
i2s_test_io_config(I2S_TEST_MODE_MASTER_TO_SLAVE);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
TEST_CASE("I2S_master_read_slave_write_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_slv_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_mst_cfg));
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif
/*------------------------------ Clock Test --------------------------------*/
#if SOC_PCNT_SUPPORTED
#define TEST_I2S_PERIOD_MS 100
static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_config_t* clk_cfg)
{
TEST_ASSERT_NOT_NULL(rx_chan);
TEST_ASSERT_NOT_NULL(clk_cfg);
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = MASTER_WS_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
// Reconfig GPIO signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
// Test common sample rate
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000,
128000, 144000, 196000};
int real_pulse = 0;
for (int i = 0; i < 15; i++) {
int expt_pulse = (int)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
clk_cfg->sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_chan, clk_cfg));
TEST_ESP_OK(i2s_channel_enable(rx_chan));
vTaskDelay(1); // Waiting for hardware totally started
// pcnt will count the pulse number on WS signal in 100ms
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
printf("[%d Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ESP_OK(i2s_channel_disable(rx_chan));
// Check if the error between real pulse number and expected pulse number is within 1%
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
}
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
TEST_CASE("I2S_default_PLL_clock_test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_SUPPORTS_APLL
TEST_CASE("I2S_APLL_clock_test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif // SOC_I2S_SUPPORTS_APLL
#endif // SOC_PCNT_SUPPORTED
static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
{
int *cnt = (int *)user_ctx;
(*cnt)++;
return false;
}
TEST_CASE("I2S_package_lost_test", "[i2s]")
{
/* Steps of calculate appropriate parameters of I2S buffer:
* Known by user: sample_rate = 144k, data_bit_width = 32, slot_num = 2, polling_cycle = 10 ms
* 1. dma_buffer_size = dma_frame_num * slot_num * data_bit_width / 8 <= 4092
* dma_frame_num <= 511, dma_frame_num is as big as possible.
* interrupt_interval = dma_frame_num / sample_rate = 3.549 ms
* 2. dma_desc_num > polling_cycle / interrupt_interval = cell(2.818) = 3
* 3. recv_buffer_size > dma_desc_num * dma_buffer_size = 3 * 4092 = 12276 bytes */
#define TEST_RECV_BUF_LEN 12276
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 3;
chan_cfg.dma_frame_num = 511;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_event_callbacks_t cbs = {
.on_recv = NULL,
.on_recv_q_ovf = i2s_rx_queue_overflow_callback,
.on_sent = NULL,
.on_send_q_ovf = NULL,
};
int count = 0;
TEST_ESP_OK(i2s_channel_register_event_callback(rx_handle, &cbs, &count));
uint32_t test_freq[] = {16000, 32000, 48000, 64000, 96000, 128000, 144000};
uint32_t test_num = sizeof(test_freq) / sizeof(uint32_t);
uint8_t *data = (uint8_t *)calloc(TEST_RECV_BUF_LEN, sizeof(uint8_t));
size_t bytes_read = 0;
int i;
for (i = 0; i < test_num; i++) {
printf("Testing %d Hz sample rate\n", test_freq[i]);
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
for (int j = 0; j < 10; j++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, (void *)data, TEST_RECV_BUF_LEN, &bytes_read, portMAX_DELAY));
// To simulate 10ms delay caused by other statements like data process
vTaskDelay(1);
}
TEST_ESP_OK(i2s_channel_disable(rx_handle));
if (count > 0) {
printf("package lost detected at %d Hz\n", test_freq[i]);
goto finish;
}
}
finish:
TEST_ESP_OK(i2s_del_channel(rx_handle));
free(data);
// Test failed if package lost within 96000
TEST_ASSERT(i == test_num);
}

View File

@ -0,0 +1,114 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include "driver/i2s_std.h"
#include "esp_spi_flash.h"
#include "esp_attr.h"
#include "soc/soc_caps.h"
#include "esp_private/i2s_platform.h"
#include "test_inc/test_i2s.h"
#if CONFIG_I2S_ISR_IRAM_SAFE
#define GET_DMA_BUFFERS_BY_OFFSET(base_addr, offset) (uint8_t **)(*((uint32_t *)base_addr + offset))
static bool IRAM_ATTR test_i2s_tx_done_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
{
int *is_triggered = (int *)user_ctx;
if (*(uint8_t *)(event->data) != 0) {
*is_triggered = 1;
}
return false;
}
static void IRAM_ATTR test_i2s_iram_write(i2s_chan_handle_t tx_handle)
{
// Get the DMA buf pointer via the offset of 'bufs' field in i2s_channel_t struct
size_t offset = i2s_platform_get_dma_buffer_offset() / sizeof(uint32_t); // Get the offset and transfer to unit 'uint32_t' (i.e. 4 bytes)
uint8_t **dma_bufs = GET_DMA_BUFFERS_BY_OFFSET(tx_handle, offset);
// disable cache and non-iram ISR handlers
spi_flash_guard_get()->start();
// write data into dma buffer directly, the data in dma buffer will be sent automatically
for (int i=0; i < 100; i++) {
dma_bufs[0][i] = i + 1;
}
// enable cache and non-iram ISR handlers
spi_flash_guard_get()->end();
}
TEST_CASE("i2s_iram_interrupt_safe", "[i2s]")
{
i2s_chan_handle_t tx_chan = NULL;
i2s_chan_handle_t rx_chan = NULL;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 6;
chan_cfg.dma_frame_num = 200;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO, // same gpio to loopback
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
TEST_ESP_OK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
int is_triggerred = 0;
i2s_event_callbacks_t cbs = {
.on_recv = NULL,
.on_recv_q_ovf = NULL,
.on_sent = test_i2s_tx_done_callback,
.on_send_q_ovf = NULL,
};
TEST_ESP_OK(i2s_channel_register_event_callback(tx_chan, &cbs, &is_triggerred));
TEST_ESP_OK(i2s_channel_enable(tx_chan));
TEST_ESP_OK(i2s_channel_enable(rx_chan));
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(recv_buf != NULL);
size_t r_bytes;
int i = 0;
test_i2s_iram_write(tx_chan);
for (int retry = 0; retry < 3; retry++) {
i2s_channel_read(rx_chan, recv_buf, 2000, &r_bytes, pdMS_TO_TICKS(1000));
for (i = 0; i < 2000 - 100; i++) {
if (recv_buf[i] != 0) {
goto finish;
}
}
}
finish:
TEST_ESP_OK(i2s_channel_disable(tx_chan));
TEST_ESP_OK(i2s_channel_disable(rx_chan));
TEST_ESP_OK(i2s_del_channel(tx_chan));
TEST_ESP_OK(i2s_del_channel(rx_chan));
TEST_ASSERT(i < (2000 - 100));
for (int j = 1; j <= 100; j++) {
TEST_ASSERT_EQUAL_UINT8(recv_buf[i++], j);
}
TEST_ASSERT(is_triggerred);
free(recv_buf);
}
#endif // CONFIG_PCNT_ISR_IRAM_SAFE

View File

@ -0,0 +1,24 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32c3
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'iram_safe',
'release',
],
indirect=True,
)
def test_i2s(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,5 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_I2S_ISR_IRAM_SAFE=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

View File

@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +1,2 @@
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -0,0 +1,5 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_adc_dac_test)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |

View File

@ -0,0 +1,6 @@
set(srcs "test_app_main.c"
"test_i2s_adc.c"
"test_i2s_dac.c")
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in I2S driver, the threadhold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ___ ____ ____ _____ _
// |_ _|___ \/ ___| |_ _|__ ___| |_
// | | __) \___ \ | |/ _ \/ __| __|
// | | / __/ ___) | | | __/\__ \ |_
// |___|_____|____/ |_|\___||___/\__|
printf(" ___ ____ ____ _____ _ \r\n");
printf(" |_ _|___ \\/ ___| |_ _|__ ___| |_ \r\n");
printf(" | | __) \\___ \\ | |/ _ \\/ __| __|\r\n");
printf(" | | / __/ ___) | | | __/\\__ \\ |_ \r\n");
printf(" |___|_____|____/ |_|\\___||___/\\__|(ADC/DAC)\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,252 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the adc device driver on ESP32 only
*/
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp_system.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_rom_sys.h"
#include "driver/dac.h"
#include "soc/adc_periph.h"
/*
* ADC DMA testcase
*/
#include "driver/i2s.h"
//i2s number
#define EXAMPLE_I2S_NUM (0)
//i2s sample rate
#define EXAMPLE_I2S_SAMPLE_RATE (150000)
//i2s data bits
#define EXAMPLE_I2S_SAMPLE_BITS (16)
//enable display buffer for debug
#define EXAMPLE_I2S_BUF_DEBUG (0)
//I2S read buffer length
#define EXAMPLE_I2S_READ_LEN (16 * 1024)
//I2S data format, ADC-I2S only support mono.
#define EXAMPLE_I2S_FORMAT I2S_CHANNEL_FMT_ONLY_RIGHT
//I2S built-in ADC unit
#define I2S_ADC_UNIT ADC_UNIT_1
//I2S built-in ADC channel
#define I2S_ADC_CHANNEL ADC1_CHANNEL_4
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
static void adc_fake_tie_middle(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
static void adc_fake_tie_high(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
static void adc_fake_tie_low(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
static void adc_io_normal(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
/**
* @brief I2S ADC/DAC mode init.
*/
static void example_i2s_init(void)
{
int i2s_num = EXAMPLE_I2S_NUM;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
.sample_rate = EXAMPLE_I2S_SAMPLE_RATE,
.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
.channel_format = EXAMPLE_I2S_FORMAT,
.intr_alloc_flags = 0,
.dma_desc_num = 2,
.dma_frame_num = 1024,
.use_apll = 0,
};
//install and start i2s driver
TEST_ESP_OK( i2s_driver_install(i2s_num, &i2s_config, 0, NULL) );
//init ADC pad
TEST_ESP_OK( i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL) );
}
static void example_i2s_deinit(void)
{
TEST_ESP_OK( i2s_driver_uninstall(EXAMPLE_I2S_NUM) );
}
/**
* @brief debug buffer data
*/
static void example_disp_buf(uint8_t *buf, int length)
{
printf("\n======");
for (int i = 0; i < length; i += 2) {
uint16_t data = ((uint16_t)buf[i+1] << 8) | (uint16_t)buf[i];
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&data;
if ((i) % 16 == 0) printf("\n");
printf("[%d_%d] ", p->type1.channel, p->type1.data);
}
printf("\n======\n");
}
static esp_err_t adc_dma_data_check(uint8_t *buf, int length, int ideal_level)
{
for (int i = 0; i < length; i += 2) {
uint16_t data = ((uint16_t)buf[i+1] << 8) | (uint16_t)buf[i];
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&data;
if (p->type1.channel != I2S_ADC_CHANNEL) {
TEST_FAIL_MESSAGE("I2S-DMA data channel error!");
}
if (ideal_level == 1) { // high level 3.3v
TEST_ASSERT_EQUAL( 0xFFF, p->type1.data );
} else if (ideal_level == 0) { // low level 0v
TEST_ASSERT_LESS_THAN( 10, p->type1.data );
} else if (ideal_level == 2) { // middle level 1.4v
TEST_ASSERT_INT_WITHIN( 128, 1586, p->type1.data );
} else if (ideal_level == 3) { // normal level
} else { // no check
}
}
return ESP_OK;
}
static void adc_dma_read(uint8_t *buf, int length)
{
size_t bytes_read = 0;
int flash_wr_size = 0;
vTaskDelay(pdTICKS_TO_MS(100));
while (flash_wr_size < length) {
//read data from I2S bus, in this case, from ADC.
TEST_ESP_OK( i2s_read(EXAMPLE_I2S_NUM, (void *) buf + flash_wr_size, length - flash_wr_size, &bytes_read, portMAX_DELAY) );
flash_wr_size += bytes_read;
example_disp_buf((uint8_t *) buf, 128);
}
}
TEST_CASE("ADC_DMA_read", "[adc dma]")
{
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
char *i2s_read_buff = (char *) calloc(i2s_read_len, sizeof(char));
example_i2s_init();
TEST_ESP_OK( i2s_adc_enable(EXAMPLE_I2S_NUM) );
adc_fake_tie_low(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
adc_dma_read((uint8_t *)i2s_read_buff, i2s_read_len);
adc_dma_data_check((uint8_t *)i2s_read_buff, i2s_read_len, 0);
adc_fake_tie_middle(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
adc_dma_read((uint8_t *)i2s_read_buff, i2s_read_len);
adc_dma_data_check((uint8_t *)i2s_read_buff, i2s_read_len, 2);
adc_fake_tie_high(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
adc_dma_read((uint8_t *)i2s_read_buff, i2s_read_len);
adc_dma_data_check((uint8_t *)i2s_read_buff, i2s_read_len, 1);
adc_io_normal(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
TEST_ESP_OK( i2s_adc_disable(EXAMPLE_I2S_NUM) );
if (i2s_read_buff) {
free(i2s_read_buff);
i2s_read_buff = NULL;
}
example_i2s_deinit();
}
#endif // CONFIG_IDF_TARGET_ESP32

View File

@ -0,0 +1,160 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the dac device driver on ESP32 only
Hardware connection:
- ESP32: GPIO25 <---> GPIO26
*/
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp_system.h"
#include "driver/adc.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_dac_audio_file.h"
#include "driver/i2s.h"
#include "driver/dac.h"
/*
* DAC DMA config.
*/
//enable record sound and save in flash
#define RECORD_IN_FLASH_EN (1)
//enable replay recorded sound in flash
#define REPLAY_FROM_FLASH_EN (1)
//i2s number
#define EXAMPLE_I2S_NUM (0)
//i2s sample rate
#define EXAMPLE_I2S_SAMPLE_RATE (16000)
//i2s data bits
#define EXAMPLE_I2S_SAMPLE_BITS (16)
//enable display buffer for debug
#define EXAMPLE_I2S_BUF_DEBUG (0)
//I2S read buffer length
#define EXAMPLE_I2S_READ_LEN (16 * 1024)
//I2S data format
#define EXAMPLE_I2S_FORMAT (I2S_CHANNEL_FMT_RIGHT_LEFT)
//I2S channel number
#define EXAMPLE_I2S_CHANNEL_NUM ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
/**
* @brief I2S ADC/DAC mode init.
*/
static void example_i2s_init(void)
{
int i2s_num = EXAMPLE_I2S_NUM;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
.sample_rate = EXAMPLE_I2S_SAMPLE_RATE,
.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
.channel_format = EXAMPLE_I2S_FORMAT,
.intr_alloc_flags = 0,
.dma_desc_num = 2,
.dma_frame_num = 1024,
.use_apll = 0,
};
//install and start i2s driver
TEST_ESP_OK( i2s_driver_install(i2s_num, &i2s_config, 0, NULL) );
//init DAC pad
TEST_ESP_OK( i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN) );
}
static void example_i2s_deinit(void)
{
TEST_ESP_OK( i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE) );
TEST_ESP_OK( i2s_driver_uninstall(EXAMPLE_I2S_NUM) );
}
/**
* @brief Set i2s clock for example audio file
*/
static void example_set_file_play_mode(void)
{
TEST_ESP_OK( i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1) );
}
/**
* @brief Scale data to 16bit/32bit for I2S DMA output.
* DAC can only output 8bit data value.
* I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data.
*/
static int example_i2s_dac_data_scale(uint8_t *d_buff, uint8_t *s_buff, uint32_t len)
{
uint32_t j = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
for (int i = 0; i < len; i++) {
d_buff[j++] = 0;
d_buff[j++] = s_buff[i];
}
return (len * 2);
#else
for (int i = 0; i < len; i++) {
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = s_buff[i];
}
return (len * 4);
#endif
}
/**
* @brief debug buffer data
*/
static void example_disp_buf(uint8_t *buf, int length)
{
printf("======\n");
for (int i = 0; i < length; i++) {
printf("%02x ", buf[i]);
if ((i + 1) % 8 == 0) {
printf("\n");
}
}
printf("======\n");
}
/**
* @brief Reset i2s clock and mode
*/
static void example_reset_play_mode(void)
{
TEST_ESP_OK( i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM) );
}
TEST_CASE("DAC_DMA_output", "[dac]")
{
size_t bytes_written;
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
uint8_t *i2s_write_buff = (uint8_t *) calloc(i2s_read_len, sizeof(char));
int offset = 0;
int tot_size = sizeof(audio_table);
printf("Playing file example: \n");
example_i2s_init();
example_set_file_play_mode();
while (offset < tot_size) {
int play_len = ((tot_size - offset) > (4 * 1024)) ? (4 * 1024) : (tot_size - offset);
int i2s_wr_len = example_i2s_dac_data_scale(i2s_write_buff, (uint8_t *)(audio_table + offset), play_len);
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, i2s_wr_len, &bytes_written, portMAX_DELAY);
offset += play_len;
example_disp_buf((uint8_t *) i2s_write_buff, 32);
}
example_reset_play_mode();
free(i2s_write_buff);
example_i2s_deinit();
}
#endif // CONFIG_IDF_TARGET_ESP32

View File

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_i2s_adc_dac(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,9 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_I2S_ISR_IRAM_SAFE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

View File

@ -1,3 +1,3 @@
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -1,5 +1,5 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_test)
project(i2s_legacy_test)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |

View File

@ -0,0 +1,6 @@
set(srcs "test_app_main.c"
"test_legacy_i2s.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "../../"
WHOLE_ARCHIVE)

View File

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in I2S driver, the threadhold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ___ ____ ____ _____ _
// |_ _|___ \/ ___| |_ _|__ ___| |_
// | | __) \___ \ | |/ _ \/ __| __|
// | | / __/ ___) | | | __/\__ \ |_
// |___|_____|____/ |_|\___||___/\__|
printf(" ___ ____ ____ _____ _ \r\n");
printf(" |_ _|___ \\/ ___| |_ _|__ ___| |_ \r\n");
printf(" | | __) \\___ \\ | |/ _ \\/ __| __|\r\n");
printf(" | | / __/ ___) | | | __/\\__ \\ |_ \r\n");
printf(" |___|_____|____/ |_|\\___||___/\\__| (legacy)\r\n");
unity_run_menu();
}

View File

@ -17,70 +17,27 @@
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/i2s.h"
#include "hal/i2s_hal.h"
#include "soc/i2s_periph.h"
#include "soc/soc_caps.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "unity.h"
#include "math.h"
#include "esp_rom_gpio.h"
#define SAMPLE_RATE (36000)
#define SAMPLE_BITS (16)
#if CONFIG_IDF_TARGET_ESP32
#define MASTER_BCK_IO 15
#define MASTER_WS_IO 25
#define SLAVE_BCK_IO 19
#define SLAVE_WS_IO 26
#define DATA_IN_IO 21
#define DATA_OUT_IO 22
#define ADC1_CHANNEL_4_IO 32
#define I2S0_DATA_OUT_IDX I2S0O_DATA_OUT23_IDX
#define I2S0_DATA_IN_IDX I2S0I_DATA_IN15_IDX
#define I2S1_DATA_OUT_IDX I2S1O_DATA_OUT23_IDX
#define I2S1_DATA_IN_IDX I2S1I_DATA_IN15_IDX
#elif CONFIG_IDF_TARGET_ESP32S2
#define MASTER_BCK_IO 15
#define MASTER_WS_IO 28
#define SLAVE_BCK_IO 19
#define SLAVE_WS_IO 26
#define DATA_IN_IO 21
#define DATA_OUT_IO 20
#define I2S0_DATA_OUT_IDX I2S0O_DATA_OUT23_IDX
#define I2S0_DATA_IN_IDX I2S0I_DATA_IN15_IDX
#elif CONFIG_IDF_TARGET_ESP32C3
// TODO: change pins
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#define I2S0_DATA_OUT_IDX I2SO_SD_OUT_IDX
#define I2S0_DATA_IN_IDX I2SI_SD_IN_IDX
#elif CONFIG_IDF_TARGET_ESP32S3
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#define I2S0_DATA_OUT_IDX I2S0O_SD_OUT_IDX
#define I2S0_DATA_IN_IDX I2S0I_SD_IN_IDX
#define I2S1_DATA_OUT_IDX I2S1O_SD_OUT_IDX
#define I2S1_DATA_IN_IDX I2S1I_SD_IN_IDX
#if SOC_PCNT_SUPPORTED
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#endif
#include "test_inc/test_i2s.h"
#define PERCENT_DIFF 0.0001
#define I2S_TEST_MODE_SLAVE_TO_MAXTER 0
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
// This empty function is used to force the compiler link this file
void test_app_include_i2s_legacy(void)
{
}
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
static void i2s_test_io_config(int mode)
@ -96,33 +53,33 @@ static void i2s_test_io_config(int mode)
switch (mode) {
#if SOC_I2S_NUM > 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_MASTER: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 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, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
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);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 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);
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 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, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
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);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
}
break;
#endif
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);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
@ -133,13 +90,81 @@ static void i2s_test_io_config(int mode)
}
}
#if SOC_I2S_SUPPORTS_ADC
#define ADC1_CHANNEL_4_IO 32
/* Only ESP32 need I2S adc/dac test */
TEST_CASE("I2S_adc_test", "[i2s_legacy]")
{
// 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_desc_num = 2,
.dma_frame_num = 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(pdMS_TO_TICKS(200));
// 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);
}
#endif
/**
* i2s initialize test
* 1. i2s_driver_install
* 2. i2s_set_pin
*/
TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s_legacy]")
TEST_CASE("I2S_basic_driver_installation_uninstallation_and_settings_test", "[i2s_legacy]")
{
// dac, adc i2s
i2s_config_t i2s_config = {
@ -177,7 +202,7 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s_legacy]")
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
//error param test
TEST_ASSERT(i2s_driver_install(I2S_NUM_MAX, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
TEST_ASSERT(i2s_driver_install(SOC_I2S_NUM, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
TEST_ASSERT(i2s_driver_install(I2S_NUM_0, NULL, 0, NULL) == ESP_ERR_INVALID_ARG);
i2s_config.dma_desc_num = 1;
TEST_ASSERT(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
@ -186,15 +211,24 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s_legacy]")
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_driver_uninstall(I2S_NUM_0));
}
TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s_legacy]")
/**
* @brief Test mono and stereo mode of I2S by loopback
* @note Only rx channel distinguish left mono and right mono, tx channel does not
* @note 1. Check switch mono/stereo by 'i2s_set_clk'
* 2. Check rx right mono and left mono (requiring tx works in stereo mode)
* 3. Check tx mono (requiring rx works in stereo mode)
*/
TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s_legacy]")
{
#define WRITE_BUF_LEN 2000
#define READ_BUF_LEN 4000
// master driver installed and send data
i2s_config_t master_i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = SAMPLE_BITS,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.dma_desc_num = 6,
.dma_frame_num = 100,
.use_apll = 0,
@ -215,51 +249,152 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s_legacy]")
.data_out_num = DATA_OUT_IO,
.data_in_num = DATA_IN_IO
};
/* Install I2S in duplex mode */
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
/* Config TX as stereo channel directly, because legacy driver can't support config tx&rx separately */
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
#else
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
#endif
i2s_ll_tx_enable_mono_mode(&I2S0, false);
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
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);
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);
TEST_ESP_OK(i2s_start(I2S_NUM_0));
for (int i = 0; i < 100; i++) {
data_wr[i] = i + 1;
uint16_t *w_buf = calloc(1, WRITE_BUF_LEN);
uint16_t *r_buf = calloc(1, READ_BUF_LEN);
size_t w_bytes = 0;
size_t r_bytes = 0;
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
w_buf[n] = n%100;
}
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) {
if (length >= 10000 - 500) {
break;
/* rx right mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x01[R] 0x03[R] ... */
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
i2s_read(I2S_NUM_0, i2s_read_buff + length, sizeof(uint8_t) * 500, &bytes_read, 1000 / portMAX_DELAY);
if (bytes_read > 0) {
for (int i = length; i < length + bytes_read; i++) {
if (i2s_read_buff[i] == 100) {
flag = 1;
end_position = i;
#endif
int i = 0;
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
if (r_buf[i] == 1) {
printf("%d %d %d %d\n%d %d %d %d\n",
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
break;
}
}
printf("Data start index: %d\n", i);
TEST_ASSERT(i < READ_BUF_LEN / 2 - 50);
for (int16_t j = 1; j < 100; j += 2) {
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
}
length = length + bytes_read;
printf("rx right mono test passed\n");
/* tx/rx stereo test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */
TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, SAMPLE_BITS, I2S_CHANNEL_STEREO));
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
if (r_buf[i] == 1) {
printf("%d %d %d %d\n%d %d %d %d\n",
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
break;
}
// 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));
}
free(data_wr);
free(i2s_read_buff);
i2s_driver_uninstall(I2S_NUM_0);
printf("Data start index: %d\n", i);
TEST_ASSERT(i < READ_BUF_LEN / 2 - 100);
TEST_ASSERT(i % 2);
for (int16_t j = 1; j < 100; j ++) {
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); // receive all number
}
printf("tx/rx stereo test passed\n");
/* tx mono rx right mono test
* tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ...
* rx receive: 0x01[R] 0x02[R] ... */
TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO));
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
for (i = 0; i < READ_BUF_LEN / 2; i++) {
if (r_buf[i] == 1) {
printf("%d %d %d %d\n%d %d %d %d\n",
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
break;
}
}
printf("Data start index: %d\n", i);
TEST_ASSERT(i < READ_BUF_LEN / 2 - 100);
for (int16_t j = 1; j < 100; j ++) {
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
}
printf("tx/rx mono test passed\n");
/* Reinstalling I2S to test rx left mono */
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
master_i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
#else
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
#endif
i2s_ll_tx_enable_mono_mode(&I2S0, false);
TEST_ESP_OK(i2s_start(I2S_NUM_0));
/* rx left mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[R] 0x02[R] ... */
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
#endif
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
if (r_buf[i] == 2) {
printf("%d %d %d %d\n%d %d %d %d\n",
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
break;
}
}
printf("Data start index: %d\n", i);
TEST_ASSERT(i < READ_BUF_LEN / 2 - 50);
for (int16_t j = 2; j < 100; j += 2) {
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
}
printf("rx left mono test passed\n");
free(w_buf);
free(r_buf);
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
}
#if SOC_I2S_SUPPORTS_TDM
TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s_legacy]")
TEST_CASE("I2S_TDM_loopback_test_with_master_tx_and_rx", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@ -328,7 +463,7 @@ TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s_legacy]")
#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_legacy]")
TEST_CASE("I2S_write_and_read_test_with_master_tx_and_slave_rx", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@ -432,7 +567,7 @@ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s_legacy]")
i2s_driver_uninstall(I2S_NUM_1);
}
TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s_legacy]")
TEST_CASE("I2S_write_and_read_test_master_rx_and_slave_tx", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@ -463,7 +598,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s_legacy]")
};
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_test_io_config(I2S_TEST_MODE_SLAVE_TO_MAXTER);
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
printf("\r\nheap size: %d\n", esp_get_free_heap_size());
i2s_config_t slave_i2s_config = {
@ -495,7 +630,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s_legacy]")
// slave driver installed and receive data
TEST_ESP_OK(i2s_driver_install(I2S_NUM_1, &slave_i2s_config, 0, NULL));
TEST_ESP_OK(i2s_set_pin(I2S_NUM_1, &slave_pin_config));
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MAXTER);
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
printf("\r\nheap size: %d\n", esp_get_free_heap_size());
uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400);
@ -538,7 +673,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s_legacy]")
}
#endif
TEST_CASE("I2S memory leaking test", "[i2s_legacy]")
TEST_CASE("I2S_memory_leaking_test", "[i2s_legacy]")
{
i2s_config_t master_i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX,
@ -588,7 +723,7 @@ TEST_CASE("I2S memory leaking test", "[i2s_legacy]")
* and the APLL clock generate for it. The TEST_CASE passes PERCENT_DIFF variation from the provided sample rate in APLL generated clock
* The percentage difference calculated as (mod((obtained clock rate - desired clock rate)/(desired clock rate))) * 100.
*/
TEST_CASE("I2S APLL clock variation test", "[i2s_legacy]")
TEST_CASE("I2S_APLL_clock_variation_test", "[i2s_legacy]")
{
i2s_pin_config_t pin_config = {
.mck_io_num = -1,
@ -644,76 +779,8 @@ TEST_CASE("I2S APLL clock variation test", "[i2s_legacy]")
}
#endif
#if SOC_I2S_SUPPORTS_ADC
/* Only ESP32 need I2S adc/dac test */
TEST_CASE("I2S adc test", "[i2s_legacy]")
{
// 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_desc_num = 2,
.dma_frame_num = 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(pdMS_TO_TICKS(200));
// 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);
}
#endif
#if SOC_I2S_SUPPORTS_DAC
TEST_CASE("I2S dac test", "[i2s_legacy]")
TEST_CASE("I2S_dac_test", "[i2s_legacy]")
{
// dac, adc i2s
i2s_config_t i2s_config = {
@ -736,3 +803,112 @@ TEST_CASE("I2S dac test", "[i2s_legacy]")
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
}
#endif
/*------------------------------ Clock Test --------------------------------*/
#if SOC_PCNT_SUPPORTED
#define TEST_I2S_PERIOD_MS 100
static void i2s_test_common_sample_rate(i2s_port_t id)
{
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = MASTER_WS_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
// Reconfig GPIO signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
// Test common sample rate
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000,
128000, 144000, 196000};
int real_pulse = 0;
for (int i = 0; i < 15; i++) {
int expt_pulse = (int16_t)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
TEST_ESP_OK(i2s_set_clk(id, test_freq[i], SAMPLE_BITS, I2S_CHANNEL_STEREO));
vTaskDelay(1); // Waiting for hardware totally started
// pcnt will count the pulse number on WS signal in 100ms
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
printf("[%d Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
// Check if the error between real pulse number and expected pulse number is within 1%
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
}
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
TEST_CASE("I2S clock freqency test", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_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_desc_num = 6,
.dma_frame_num = 100,
.use_apll = 0,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
#if SOC_I2S_SUPPORTS_TDM
.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1,
.total_chan = 2,
.left_align = false,
.big_edin = false,
.bit_order_msb = false,
.skip_msk = false
#endif
};
i2s_pin_config_t master_pin_config = {
.mck_io_num = -1,
.bck_io_num = MASTER_BCK_IO,
.ws_io_num = MASTER_WS_IO,
.data_out_num = DATA_OUT_IO,
.data_in_num = -1
};
/* Non-APLL test */
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_test_common_sample_rate(I2S_NUM_0);
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
/* APLL test */
#if SOC_I2S_SUPPORTS_APLL
master_i2s_config.use_apll = true;
master_i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_256;
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_test_common_sample_rate(I2S_NUM_0);
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
#endif
}
#endif // SOC_PCNT_SUPPORTED

View File

@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32c3
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_legacy_i2s(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_I2S_ISR_IRAM_SAFE=y

View File

@ -0,0 +1,3 @@
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SAMPLE_RATE (48000)
#define SAMPLE_BITS (16)
#if CONFIG_IDF_TARGET_ESP32
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 21
#define SLAVE_WS_IO 22
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#elif CONFIG_IDF_TARGET_ESP32S2
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#elif CONFIG_IDF_TARGET_ESP32C3
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#elif CONFIG_IDF_TARGET_ESP32S3
#define MASTER_MCK_IO 0
#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define SLAVE_BCK_IO 14
#define SLAVE_WS_IO 15
#define DATA_IN_IO 19
#define DATA_OUT_IO 18
#endif
#ifdef __cplusplus
}
#endif

View File

@ -152,7 +152,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
int bus_id = -1;
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
if (i2s_platform_acquire_occupation(0, "esp_lcd_panel_io_i2s") == ESP_OK) {
if (i2s_platform_acquire_occupation(i, "esp_lcd_panel_io_i2s") == ESP_OK) {
bus_id = i;
break;
}
@ -185,7 +185,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
i2s_ll_tx_bypass_pcm(bus->hal.dev, true);
i2s_ll_tx_set_slave_mod(bus->hal.dev, false);
i2s_ll_tx_set_bits_mod(bus->hal.dev, bus_config->bus_width);
i2s_ll_tx_select_slot(bus->hal.dev, I2S_STD_SLOT_ONLY_LEFT); // mono
i2s_ll_tx_select_slot(bus->hal.dev, I2S_STD_SLOT_ONLY_LEFT, false); // mono
bus->bus_width = bus_config->bus_width;
i2s_ll_tx_enable_right_first(bus->hal.dev, true);
#if SOC_I2S_SUPPORTS_DMA_EQUAL
@ -593,7 +593,7 @@ static esp_err_t i2s_lcd_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
switch (src) {
case LCD_CLK_SRC_PLL160M:
bus->resolution_hz = 160000000 / LCD_PERIPH_CLOCK_PRE_SCALE;
i2s_ll_tx_clk_set_src(bus->hal.dev, I2S_CLK_PLL_160M);
i2s_ll_tx_clk_set_src(bus->hal.dev, I2S_CLK_SRC_PLL_160M);
#if CONFIG_PM_ENABLE
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_controller_lcd", &bus->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");

View File

@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
@ -453,35 +453,3 @@ TEST_CASE("lcd_panel_with_i80_interface_(st7789, 8bits)", "[lcd]")
free(img);
#undef TEST_IMG_SIZE
}
#if SOC_I2S_LCD_I80_VARIANT
#include "driver/i2s_std.h"
TEST_CASE("i80 and i2s driver coexistance", "[lcd][i2s]")
{
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO,
.wr_gpio_num = TEST_LCD_PCLK_GPIO,
.data_gpio_nums = {
TEST_LCD_DATA0_GPIO,
TEST_LCD_DATA1_GPIO,
TEST_LCD_DATA2_GPIO,
TEST_LCD_DATA3_GPIO,
TEST_LCD_DATA4_GPIO,
TEST_LCD_DATA5_GPIO,
TEST_LCD_DATA6_GPIO,
TEST_LCD_DATA7_GPIO,
},
.bus_width = 8,
.max_transfer_bytes = 20,
};
TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_handle_t tx_handle;
// I2S driver won't be installed as the same I2S port has been used by LCD
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
}
#endif // SOC_I2S_LCD_I80_VARIANT

View File

@ -189,7 +189,7 @@ static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t fre
adc_ll_digi_controller_clk_div(ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT);
adc_ll_digi_clk_sel(0); //use APB
#else
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_PLL_160M); /*!< Clock from PLL_D2_CLK(160M)*/
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_SRC_DEFAULT); /*!< Clock from PLL_D2_CLK(160M)*/
uint32_t bck = I2S_BASE_CLK / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_B_DEFAULT / ADC_LL_CLKM_DIV_A_DEFAULT) / 2 / freq;
i2s_ll_set_raw_mclk_div(hal->dev, ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT);
i2s_ll_rx_set_bck_div_num(hal->dev, bck);

View File

@ -570,6 +570,16 @@ static inline __attribute__((always_inline)) void gpio_ll_iomux_func_sel(uint32_
PIN_FUNC_SELECT(pin_name, func);
}
/**
* @brief Control the pin in the IOMUX
*
* @param val Control value
*/
static inline __attribute__((always_inline)) void gpio_ll_iomux_pin_ctrl(uint32_t val)
{
WRITE_PERI_REG(PIN_CTRL, val);
}
/**
* @brief Set peripheral output to an GPIO pad through the IOMUX.
*

View File

@ -42,6 +42,9 @@ extern "C" {
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
#define I2S_INTR_MAX (UINT32_MAX)
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF
#define I2S_LL_RX_EVENT_MASK I2S_LL_EVENT_RX_EOF
/* I2S clock configuration structure */
typedef struct {
uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a)
@ -203,6 +206,8 @@ static inline void i2s_ll_tx_reset(i2s_dev_t *hw)
{
hw->conf.tx_reset = 1;
hw->conf.tx_reset = 0;
hw->lc_conf.out_rst = 1;
hw->lc_conf.out_rst = 0;
}
/**
@ -214,6 +219,8 @@ static inline void i2s_ll_rx_reset(i2s_dev_t *hw)
{
hw->conf.rx_reset = 1;
hw->conf.rx_reset = 0;
hw->lc_conf.in_rst = 1;
hw->lc_conf.in_rst = 0;
}
/**
@ -248,7 +255,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 == I2S_CLK_APLL) ? 1 : 0;
hw->clkm_conf.clka_en = (src == I2S_CLK_SRC_APLL) ? 1 : 0;
}
/**
@ -261,7 +268,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 == I2S_CLK_APLL) ? 1 : 0;
hw->clkm_conf.clka_en = (src == I2S_CLK_SRC_APLL) ? 1 : 0;
}
/**
@ -444,6 +451,14 @@ static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw)
return hw->int_st.val;
}
/**
* @brief Get channel interrupt status register address
*/
static inline volatile void *i2s_ll_get_interrupt_status_reg(i2s_dev_t *hw)
{
return (volatile void *)(&hw->int_st);
}
/**
* @brief Clear I2S interrupt status
*
@ -734,17 +749,18 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to send data
* @param slot_mask select slot to send data
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.tx_chan_mod = 2;
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.tx_chan_mod = 0;
@ -758,17 +774,18 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to receive data
* @param slot_mask select slot to receive data
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.rx_chan_mod = 1;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.rx_chan_mod = 2;
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.rx_chan_mod = 0;
@ -788,7 +805,6 @@ 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_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf_chan.tx_chan_mod = mono_ena;
}
/**
@ -801,7 +817,6 @@ 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_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf_chan.rx_chan_mod = mono_ena;
}
/**

View File

@ -17,7 +17,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/i2s_types_priv.h"
#ifdef __cplusplus
extern "C" {
@ -200,7 +200,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-C3 only support `I2S_CLK_PLL_160M`
* @param src I2S source clock, ESP32-C3 only support `I2S_CLK_SRC_PLL_160M`
*/
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
{
@ -555,21 +555,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to send data
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->tx_tdm_ctrl.val |= 0x02;
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->tx_tdm_ctrl.val |= 0x01;
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x03;
@ -583,21 +583,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to receive data
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->rx_tdm_ctrl.val |= 0x02;
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->rx_tdm_ctrl.val |= 0x01;
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x03;

View File

@ -18,7 +18,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/i2s_types_priv.h"
#ifdef __cplusplus
extern "C" {
@ -28,7 +28,8 @@ extern "C" {
#define I2S_LL_TDM_CH_MASK (0xffff)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ)
// [clk_tree] TODO: replace the following switch table by clk_tree API
#define I2S_LL_BASE_CLK (96*1000000)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
@ -201,7 +202,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-H2 only support `I2S_CLK_PLL_160M`
* @param src I2S source clock, ESP32-H2 only support `I2S_CLK_SRC_PLL_96M` for now
*/
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
{
@ -556,21 +557,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to send data
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->tx_tdm_ctrl.val |= 0x02;
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->tx_tdm_ctrl.val |= 0x01;
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x03;
@ -584,21 +585,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to receive data
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->rx_tdm_ctrl.val |= 0x02;
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->rx_tdm_ctrl.val |= 0x01;
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x03;

View File

@ -19,7 +19,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/i2s_types_priv.h"
#ifdef __cplusplus
extern "C" {
@ -41,6 +41,9 @@ extern "C" {
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
#define I2S_INTR_MAX (UINT32_MAX)
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF
#define I2S_LL_RX_EVENT_MASK I2S_LL_EVENT_RX_EOF
/* I2S clock configuration structure */
typedef struct {
uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a)
@ -247,7 +250,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 == I2S_CLK_APLL) ? 1 : 2;
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
}
/**
@ -258,7 +261,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 == I2S_CLK_APLL) ? 1 : 2;
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
}
/**
@ -441,6 +444,14 @@ static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw)
return hw->int_st.val;
}
/**
* @brief Get DMA interrupt status register address
*/
static inline volatile void *i2s_ll_get_interrupt_status_reg(i2s_dev_t *hw)
{
return (volatile void *)(&hw->int_st);
}
/**
* @brief Clear I2S interrupt status
*
@ -829,17 +840,17 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to send data
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = 1;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.tx_chan_mod = 2;
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.tx_chan_mod = 0;
@ -853,17 +864,17 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to receive data
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
{
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.rx_chan_mod = 1;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->conf_chan.rx_chan_mod = 2;
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
break;
case I2S_STD_SLOT_ONLY_LEFT:
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->conf_chan.rx_chan_mod = 0;
@ -906,7 +917,6 @@ 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_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf.tx_dma_equal = mono_ena;
hw->conf_chan.tx_chan_mod = mono_ena;
}
/**
@ -920,7 +930,6 @@ 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_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
hw->conf.rx_dma_equal = mono_ena;
hw->conf_chan.rx_chan_mod = mono_ena;
}
/**

View File

@ -17,7 +17,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/i2s_types_priv.h"
#ifdef __cplusplus
extern "C" {
@ -190,7 +190,7 @@ static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw)
* @brief Set TX source clock
*
* @param hw Peripheral I2S hardware instance address.
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_PLL_160M`
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_SRC_PLL_160M`
* TX and RX share the same clock setting
*/
static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
@ -202,7 +202,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_PLL_160M`
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_SRC_PLL_160M`
* TX and RX share the same clock setting
*/
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
@ -558,21 +558,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
* @brief Set I2S tx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to send data
* @param slot_mask select slot to send data
*/
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->tx_tdm_ctrl.val |= 0x02;
hw->tx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->tx_tdm_ctrl.val |= 0x01;
hw->tx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->tx_tdm_ctrl.val |= 0x03;
@ -586,21 +586,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
* @brief Set I2S rx chan mode
*
* @param hw Peripheral I2S hardware instance address.
* @param slot_sel select slot to receive data
* @param slot_mask select slot to receive data
*/
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
{
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
* Otherwise always enable the first two slots */
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
switch (slot_sel)
switch (slot_mask)
{
case I2S_STD_SLOT_ONLY_LEFT:
hw->rx_tdm_ctrl.val |= 0x02;
hw->rx_tdm_ctrl.val |= 0x01;
break;
case I2S_STD_SLOT_ONLY_RIGHT:
hw->rx_tdm_ctrl.val |= 0x01;
hw->rx_tdm_ctrl.val |= 0x02;
break;
case I2S_STD_SLOT_LEFT_RIGHT:
hw->rx_tdm_ctrl.val |= 0x03;

View File

@ -20,7 +20,8 @@ static const float cut_off_coef[21][3] = {
{104, 2, 4}, {92, 4, 4}, {91.5, 2, 7},
{81, 4, 5}, {77.2, 3, 7}, {69, 5, 5},
{63, 4, 7}, {58, 5, 6}, {49, 5, 7},
{46, 6, 6}, {35.5, 6, 7}, {23.3, 7, 7}};
{46, 6, 6}, {35.5, 6, 7}, {23.3, 7, 7}
};
#endif
void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
@ -65,11 +66,14 @@ void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
#if SOC_I2S_HW_VERSION_1
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
// According to the test, the behavior of tx_msb_right is opposite with TRM, TRM is wrong?
i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask);
i2s_ll_tx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
@ -88,13 +92,14 @@ void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_sel);
#if SOC_I2S_HW_VERSION_1
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
/* Should always enable fifo */
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
#elif SOC_I2S_HW_VERSION_2
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask);
i2s_ll_rx_set_half_sample_bit(hal->dev, slot_bit_width);
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);

View File

@ -34,21 +34,23 @@ typedef struct {
union {
/* STD configurations */
struct {
i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
bool bit_shift; /*!< Set to enbale bit shift in Philip mode */
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
bool msb_right; /*!< Set to place right channel data at the MSB in the FIFO */
#else
#else
bool left_align; /*!< Set to enable left alignment */
bool big_endian; /*!< Set to enable big endian */
bool bit_order_lsb; /*!< Set to enable lsb first */
#endif
} std;
#endif
} std; /*!< Specific configurations for standard mode */
#if SOC_I2S_SUPPORTS_TDM
#if SOC_I2S_SUPPORTS_TDM
/* TDM configurations */
struct {
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
uint32_t ws_width; /*!< WS signal width ((i.e. the number of bclk ticks that ws signal is high)) */
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
bool bit_shift; /*!< Set true to enable bit shift in Philip mode */
@ -58,12 +60,11 @@ typedef struct {
bool bit_order_lsb; /*!< Set true to enable lsb first */
bool skip_mask; /*!< Set true 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 */
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
uint32_t total_slot; /*!< I2S total number of slots. If it is smaller than the biggest activated channel number, it will be set to this number automatically. */
} tdm;
#endif
} tdm; /*!< Specific configurations for TDM mode */
#endif
#if SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_SUPPORTS_PDM_TX
/* PDM TX configurations */
struct {
uint32_t sd_prescale; /*!< Sigma-delta filter prescale */
@ -71,15 +72,15 @@ typedef struct {
i2s_pdm_sig_scale_t hp_scale; /*!< High pass filter scaling value */
i2s_pdm_sig_scale_t lp_scale; /*!< Low pass filter scaling value */
i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */
#if SOC_I2S_HW_VERSION_2
#if SOC_I2S_HW_VERSION_2
bool sd_en; /*!< Sigma-delta filter enable */
bool hp_en; /*!< High pass filter enable */
float hp_cut_off_freq_hz; /*!< High pass filter cut-off frequency, range 23.3Hz ~ 185Hz, see cut-off frequency sheet above */
uint32_t sd_dither; /*!< Sigma-delta filter dither */
uint32_t sd_dither2; /*!< Sigma-delta filter dither2 */
#endif // SOC_I2S_HW_VERSION_2
} pdm_tx;
#endif
#endif // SOC_I2S_HW_VERSION_2
} pdm_tx; /*!< Specific configurations for PDM TX mode */
#endif
};
} i2s_hal_slot_config_t;
@ -106,7 +107,7 @@ typedef struct {
* @brief Init I2S hal context
*
* @param hal Context of the HAL layer
* @param port_id The I2S port number, the max port number is (I2S_NUM_MAX -1)
* @param port_id The I2S port number, the max port number is (SOC_I2S_NUM -1)
*/
void i2s_hal_init(i2s_hal_context_t *hal, int port_id);
@ -139,7 +140,7 @@ void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *cl
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s standard mode
*/
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Set rx slot to standard mode
@ -148,7 +149,7 @@ void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s standard mode
*/
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Enable tx channel as standard mode
@ -177,7 +178,7 @@ void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal);
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s pdm tx mode
*/
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Enable tx channel as pdm mode
@ -195,7 +196,7 @@ void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal);
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s pdm rx mode
*/
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Enable rx channel as pdm mode
@ -217,7 +218,7 @@ void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal);
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s tdm mode
*/
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Set rx slot to tdm mode
@ -226,7 +227,7 @@ void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_ha
* @param is_slave If is slave role
* @param slot_config General slot configuration pointer, but will specified to i2s tdm mode
*/
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_config);
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
/**
* @brief Enable tx channel as tdm mode

View File

@ -12,37 +12,12 @@
#include <stddef.h>
#include "esp_bit_defs.h"
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2S controller port number, the max port number is (I2S_NUM_MAX -1).
*/
typedef enum {
I2S_NUM_0 = 0, /*!< I2S controller port 0 */
#if SOC_I2S_NUM > 1
I2S_NUM_1 = 1, /*!< I2S controller port 1 */
#endif
I2S_NUM_MAX, /*!< I2S controller port max */
I2S_NUM_AUTO, /*!< Select whichever port is available */
} i2s_port_t;
/**
* @brief I2S controller communication mode
*/
typedef enum {
I2S_COMM_MODE_STD, /*!< I2S controller using standard communication mode, support philip/MSB/PCM format */
#if SOC_I2S_SUPPORTS_PDM
I2S_COMM_MODE_PDM, /*!< I2S controller using PDM communication mode, support PDM output or input */
#endif
#if SOC_I2S_SUPPORTS_TDM
I2S_COMM_MODE_TDM, /*!< I2S controller using TDM communication mode, support up to 16 slots per frame */
#endif
I2S_COMM_MODE_NONE, /*!< Unspecific I2S controller mode */
} i2s_comm_mode_t;
/**
* @brief I2S channel slot mode
*/
@ -51,15 +26,6 @@ typedef enum {
I2S_SLOT_MODE_STEREO = 2, /*!< I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode. */
} i2s_slot_mode_t;
/**
* @brief I2S slot select in standard mode
*/
typedef enum {
I2S_STD_SLOT_ONLY_LEFT = 0x01, /*!< I2S only transmits or receives left slot */
I2S_STD_SLOT_ONLY_RIGHT = 0x02, /*!< I2S only transmits or receives right slot */
I2S_STD_SLOT_LEFT_RIGHT = 0x03, /*!< I2S only transmits or receives both left and right slot */
} i2s_std_slot_sel_t;
/**
* @brief I2S channel direction
*/
@ -98,33 +64,7 @@ typedef enum {
I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */
} i2s_slot_bit_width_t;
/**
* @brief The multiple of mclk to sample rate
*/
typedef enum {
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;
typedef enum {
I2S_CLK_PLL_160M = 0, /*!< I2S controller clock source PLL_D2_CLK(160M)*/
#if SOC_I2S_SUPPORTS_APLL
I2S_CLK_APLL, /*!< I2S controller clock source APLL*/
#endif
} i2s_clock_src_t;
/**
* @brief I2S event queue types
*/
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_TX_Q_OVF, /*!< I2S DMA sent queue overflow*/
I2S_EVENT_RX_Q_OVF, /*!< I2S DMA receive queue overflow*/
I2S_EVENT_MAX, /*!< I2S event max index*/
} i2s_event_type_t;
typedef soc_periph_i2s_clk_src_t i2s_clock_src_t; /*!< I2S clock source */
#if SOC_I2S_SUPPORTS_PCM
/**
@ -163,10 +103,19 @@ typedef enum {
} i2s_pdm_sig_scale_t;
#endif // SOC_I2S_SUPPORTS_PDM_TX
/**
* @brief I2S slot select in standard mode
*/
typedef enum {
I2S_STD_SLOT_ONLY_LEFT = BIT(0), /*!< I2S only transmits or receives left slot */
I2S_STD_SLOT_ONLY_RIGHT = BIT(1), /*!< I2S only transmits or receives right slot */
I2S_STD_SLOT_LEFT_RIGHT = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
} i2s_std_slot_mask_t;
#if SOC_I2S_SUPPORTS_TDM
/**
* @brief tdm slot number
* @note There are 16 slots in TDM mode.
* @note Multiple slots in TDM mode.
* For TX module, only the active slot send the audio data, the inactive slot send a constant or will be skipped if 'skip_msk' is set.
* For RX module, only receive the audio data in active slots, the data in inactive slots will be ignored.
* the bit map of active slot can not exceed (0x1<<total_slot_num).
@ -192,15 +141,6 @@ typedef enum {
} i2s_tdm_slot_mask_t;
#endif // SOC_I2S_SUPPORTS_TDM
/**
* @brief Event structure used in I2S event queue
*/
typedef struct {
i2s_event_type_t type; /*!< I2S event type */
size_t size; /*!< I2S data size for I2S_DATA event*/
} i2s_event_t;
typedef struct i2s_channel_t *i2s_chan_handle_t; /*!< i2s channel handle, the control unit of the i2s driver*/
#ifdef __cplusplus
}

View File

@ -241,6 +241,22 @@ typedef enum {
MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< SElect APB as the default clock choice */
} soc_periph_mcpwm_capture_clk_src_t;
///////////////////////////////////////////////////I2S//////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of
*/
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_APLL}
/**
* @brief I2S clock source enum
*
*/
typedef enum {
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the default source clock */
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */
I2S_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
} soc_periph_i2s_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -202,6 +202,21 @@ typedef enum {
UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */
} soc_periph_uart_clk_src_legacy_t;
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of
*/
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M}
/**
* @brief I2S clock source enum
*/
typedef enum {
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
} soc_periph_i2s_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -110,10 +110,11 @@ typedef enum {
SOC_MOD_CLK_RTC_FAST, /*!< RTC_FAST_CLK can be sourced from XTAL_D2 or RC_FAST by configuring soc_rtc_fast_clk_src_t */
SOC_MOD_CLK_RTC_SLOW, /*!< RTC_SLOW_CLK can be sourced from RC_SLOW, XTAL32K, or RC32K by configuring soc_rtc_slow_clk_src_t */
// For digital domain: peripherals, WIFI, BLE
SOC_MOD_CLK_AHB, /*!< AHB_CLK sources from CPU with a configurable divider */
SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 8MHz rc oscillator, passing a clock gating to the peripherals */
SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 32MHz crystal */
SOC_MOD_CLK_AHB, /*< AHB_CLK sources from CPU with a configurable divider */
SOC_MOD_CLK_XTAL32K, /*< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
SOC_MOD_CLK_RC_FAST, /*< RC_FAST_CLK comes from the internal 8MHz rc oscillator, passing a clock gating to the peripherals */
SOC_MOD_CLK_XTAL, /*< XTAL_CLK comes from the external 32MHz crystal */
SOC_MOD_CLK_PLL, /*< PLL_CLK is the output of 32MHz crystal oscillator frequency multiplier, 96MHz */
} soc_module_clk_t;
@ -206,6 +207,21 @@ typedef enum {
UART_SCLK_DEFAULT = SOC_MOD_CLK_AHB, /*!< UART source clock default choice is AHB */
} soc_periph_uart_clk_src_legacy_t;
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of
*/
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL}
/**
* @brief I2S clock source enum
*/
typedef enum {
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL, /*!< Select SOC_MOD_CLK_PLL as the default source clock */
I2S_CLK_SRC_PLL_96M = SOC_MOD_CLK_PLL, /*!< Select PLL as the source clock */
} soc_periph_i2s_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -219,6 +219,22 @@ typedef enum {
UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */
} soc_periph_uart_clk_src_legacy_t;
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of
*/
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_APLL}
/**
* @brief I2S clock source enum
*/
typedef enum {
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
I2S_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
} soc_periph_i2s_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -248,6 +248,21 @@ typedef enum {
MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< SElect APB as the default clock choice */
} soc_periph_mcpwm_capture_clk_src_t;
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of
*/
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M}
/**
* @brief I2S clock source enum
*/
typedef enum {
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
} soc_periph_i2s_clk_src_t;
#ifdef __cplusplus
}
#endif

View File

@ -197,7 +197,7 @@ typedef volatile struct i2s_dev_s {
uint32_t reserved26 : 6; /*Reserved*/
};
uint32_t val;
} tx_pcm2pdm_conf;
} tx_pcm2pdm_conf; // Only available on I2S0
union {
struct {
uint32_t tx_pdm_fp : 10; /*I2S TX PDM Fp*/
@ -207,7 +207,7 @@ typedef volatile struct i2s_dev_s {
uint32_t reserved26 : 6; /*Reserved*/
};
uint32_t val;
} tx_pcm2pdm_conf1;
} tx_pcm2pdm_conf1; // Only available on I2S0
uint32_t reserved_48;
uint32_t reserved_4c;
union {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 52 KiB

47
docs/_static/diagrams/i2s/pdm.json vendored Normal file
View File

@ -0,0 +1,47 @@
{
"head": {
"text": "PDM Timing Diagram"
},
"signal": [
{
"node": ".A.B.C..D.E.F.G.H."
},
{
"name": "CLK",
"wave": "10.1.x|.0.1.0.1.x"
},
{
"name": "DIN / DOUT",
"wave": "x2.2.x|.2.2.2.2.x",
"data": [
"LMSB",
"RMSB",
"LLSB",
"RLSB",
"LMSB",
"RMSB"
],
"node": "...L.M..O.P...R.S."
},
{
"node": ".I..........J"
}
],
"edge": [
"A<->B left",
"B<->C right",
"D<->E left",
"E<->F right",
"F<->G left",
"G<->H right",
"I<->J left slot & right slot",
"A-I",
"B-L",
"C-M",
"D-O",
"E-P",
"F-J",
"G-R",
"H-S"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

52
docs/_static/diagrams/i2s/std_msb.json vendored Normal file
View File

@ -0,0 +1,52 @@
{
"head": {
"text": "Standard MSB Timing Diagram"
},
"signal": [
{
"node": "..C.....D",
"phase": 0.65
},
{
"node": "..A...B",
"phase": 0.65
},
{
"name": "BCLK",
"wave": "p..pd.pd.pd.pd"
},
{
"name": "WS",
"wave": "xx0.d0.d1.u1u10",
"node": ".H.....I......J",
"phase": 0.65
},
{
"name": "DIN / DOUT",
"wave": "xx2x|2x|2x|2x|2",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": "......K",
"phase": 0.65
},
{
"node": "..E.....F.....G",
"phase": 0.65
}
],
"edge": [
"C<->D slot_bit_width",
"A<->B data_bit_width",
"E<->F Left Slot",
"F<->G Right Slot",
"E-C",
"K-B",
"F-D",
"J-G"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

54
docs/_static/diagrams/i2s/std_pcm.json vendored Normal file
View File

@ -0,0 +1,54 @@
{
"head": {
"text": "Standard PCM Timing Diagram"
},
"signal": [
{
"node": "...C.....D",
"phase": 0.65
},
{
"node": "...A...B",
"phase": 0.65
},
{
"name": "BCLK",
"wave": "p..dp.dp.dp.dp."
},
{
"name": "WS",
"wave": "x10d0.d10d0.d10",
"node": "..P",
"phase": 0.65
},
{
"name": "DIN / DOUT",
"wave": "xx.2x|2x|2x|2x|2",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": ".......K.......L",
"phase": 0.65
},
{
"node": "..ME.....F.....G",
"phase": 0.65
}
],
"edge": [
"C<->D slot_bit_width",
"A<->B data_bit_width",
"M<->E bit shift",
"E<->F Left Slot",
"F<->G Right Slot",
"P-M",
"E-C",
"K-B",
"F-D",
"G-L"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,54 @@
{
"head": {
"text": "Standard Philip Timing Diagram"
},
"signal": [
{
"node": "...C.....D",
"phase": 0.65
},
{
"node": "...A...B",
"phase": 0.65
},
{
"name": "BCLK",
"wave": "p..dp.dp.dp.dp."
},
{
"name": "WS",
"wave": "xx0.d0.d1.u1u10.",
"node": "..P",
"phase": 0.65
},
{
"name": "DIN / DOUT",
"wave": "xx.2x|2x|2x|2x|2",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": ".......K.......L",
"phase": 0.65
},
{
"node": "..ME.....F.....G",
"phase": 0.65
}
],
"edge": [
"C<->D slot_bit_width",
"A<->B data_bit_width",
"M<->E bit shift",
"E<->F Left Slot",
"F<->G Right Slot",
"P-M",
"E-C",
"K-B",
"F-D",
"L-G"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

60
docs/_static/diagrams/i2s/tdm_msb.json vendored Normal file
View File

@ -0,0 +1,60 @@
{
"head": {
"text": "TDM MSB Timing Diagram"
},
"signal": [
{
"node": ".E.........F.........G",
"phase": -0.35
},
{
"name": "BCLK",
"wave": "p..d.p.d.pd.pd.p.d.pd.p"
},
{
"name": "WS",
"wave": "x0dd0.dd0dd1uu1.uu1uu0",
"node": ".H...................R",
"phase": -0.35
},
{
"name": "DIN / DOUT",
"wave": "x2x|22x|2x|2x|22x|2x|2",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": ".H...K...I.M...N...P.Q",
"phase": -0.35
},
{
"node": ".A...B...C.D...J...L.S",
"phase": -0.35
}
],
"edge": [
"E<->F Left Slots",
"F<->G Right Slots",
"A<->B Slot 1",
"B<->C Slot 2",
"C<->D ...",
"D<->J Slot n",
"J<->L Slot n+1",
"L<->S ...",
"A-E",
"K-B",
"C-I",
"D-F",
"J-N",
"L-P",
"Q-G",
"S-R"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,57 @@
{
"head": {
"text": "TDM PCM (long) Timing Diagram"
},
"signal": [
{
"node": ".T...L",
"phase": -0.35
},
{
"node": ".UE.........G",
"phase": -0.35
},
{
"name": "BCLK",
"wave": "p..d.p.d.pd.pd"
},
{
"name": "WS",
"wave": "01u.10d...01u.",
"node": ".NH",
"phase": -0.35
},
{
"name": "DIN / DOUT",
"wave": "xx2x|2x|2x|22x",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": "..H..FK.I...M",
"phase": -0.35
},
{
"node": "..A...B.C...D...J",
"phase": -0.35
}
],
"edge": [
"T<->L one slot pulse",
"U<->E bit shift",
"E<->G Frame",
"A<->B Slot 1",
"B<->C ...",
"C<->D Slot n",
"T-N",
"A-E",
"K-B",
"C-I",
"D-M",
"D-G",
"L-F"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,52 @@
{
"head": {
"text": "TDM PCM (short) Timing Diagram"
},
"signal": [
{
"node": ".TE.........G",
"phase": -0.35
},
{
"name": "BCLK",
"wave": "p..d.p.d.pd.pd"
},
{
"name": "WS",
"wave": "010d......010d",
"node": ".NH",
"phase": -0.35
},
{
"name": "DIN / DOUT",
"wave": "xx2x|2x|2x|22x",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": "..H..FK.I...M",
"phase": -0.35
},
{
"node": ".UA...B.C...D...J",
"phase": -0.35
}
],
"edge": [
"T<->E pulse",
"E<->G Frame",
"U<->A bit shift",
"A<->B Slot 1",
"B<->C ...",
"C<->D Slot n",
"T-U",
"A-E",
"K-B",
"C-I",
"D-M",
"D-G"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,62 @@
{
"head": {
"text": "TDM Philip Timing Diagram"
},
"signal": [
{
"node": "..E.........F.........G",
"phase": -0.35
},
{
"name": "BCLK",
"wave": "p..d.p.d.pd.pd.p.d.pd.p"
},
{
"name": "WS",
"wave": "x0dd0.dd0dd1uu1.uu1uu0.",
"node": ".TH...................R",
"phase": -0.35
},
{
"name": "DIN / DOUT",
"wave": "xx2x|22x|2x|2x|22x|2x|2",
"data": [
"MSB",
"LSB",
"MSB",
"LSB",
"MSB",
"LSB",
"MSB",
"LSB",
"MSB"
],
"node": "..H...K...I.M...N...P.Q",
"phase": -0.35
},
{
"node": ".UA...B...C.D...J...L.S",
"phase": -0.35
}
],
"edge": [
"E<->F Left Slots",
"F<->G Right Slots",
"U<->A bit shift",
"A<->B Slot 1",
"B<->C Slot 2",
"C<->D ...",
"D<->J Slot n",
"J<->L Slot n+1",
"L<->S ...",
"A-E",
"K-B",
"C-I",
"D-F",
"J-N",
"L-P",
"Q-G",
"S-R",
"T-U"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@ -67,7 +67,6 @@ INPUT = \
$(PROJECT_PATH)/components/driver/include/driver/gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \
$(PROJECT_PATH)/components/driver/include/driver/i2c.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s.h \
$(PROJECT_PATH)/components/driver/include/driver/ledc.h \
$(PROJECT_PATH)/components/driver/include/driver/rtc_io.h \
$(PROJECT_PATH)/components/driver/include/driver/sdio_slave.h \
@ -156,14 +155,8 @@ INPUT = \
$(PROJECT_PATH)/components/hal/include/hal/esp_flash_err.h \
$(PROJECT_PATH)/components/hal/include/hal/gpio_types.h \
$(PROJECT_PATH)/components/hal/include/hal/i2c_types.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/lcd_types.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_tdm.h \
$(PROJECT_PATH)/components/hal/include/hal/ledc_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rtc_io_types.h \
$(PROJECT_PATH)/components/hal/include/hal/sdio_slave_types.h \
$(PROJECT_PATH)/components/hal/include/hal/sigmadelta_types.h \

View File

@ -1,6 +1,10 @@
INPUT += \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
@ -11,6 +15,7 @@ INPUT += \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_himem.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

View File

@ -1,4 +1,9 @@
INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_tdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
@ -7,4 +12,5 @@ INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_hmac.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

View File

@ -1,4 +1,8 @@
INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
@ -7,4 +11,5 @@ INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_hmac.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

View File

@ -2,6 +2,9 @@ INPUT += \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
@ -10,6 +13,7 @@ INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_hmac.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \

View File

@ -1,5 +1,10 @@
INPUT += \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_tdm.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
@ -12,6 +17,7 @@ INPUT += \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_hmac.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

File diff suppressed because it is too large Load Diff

View File

@ -256,62 +256,66 @@ LCD
- All of the dedicated GPIO related LL functionsn in ``cpu_ll.h`` have been moved to ``dedic_gpio_cpu_ll.h`` and renamed.
I2S driver
----------
.. only:: SOC_I2S_SUPPORTED
Since the old driver is unable to support all the new features on ESP32-C3 & ESP32-S3, I2S driver is re-designed to make it more compatibile and flexibile to all the communication modes. New APIs are available by including :component_file:`driver/include/driver/i2s_controller.h`. Meanwhile, the old APIs in :component_file:`driver/deprecated/driver/i2s.h` are still supported for backward compatibility. But there will be warnings if you keep using the old APIs in your project, these warnings can be suppressed by the Kconfig option :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN`. Here is the general overview of the current I2S files:
I2S driver
----------
.. figure:: ../../_static/diagrams/i2s/i2s_file_structure.png
{I2S_DRIVER_HEADERS:default=":component_file:`driver/include/driver/i2s_std.h`, :component_file:`driver/include/driver/i2s_pdm.h` or :component_file:`driver/include/driver/i2s_tdm.h`", esp32=":component_file:`driver/include/driver/i2s_std.h` or :component_file:`driver/include/driver/i2s_pdm.h`", esp32s2=":component_file:`driver/include/driver/i2s_std.h`"}
Shortcomings are exposed when supporting all the new features of ESP32-C3 & ESP32-S3 by the old I2S driver, so it is re-designed to make it more compatible and flexible to all the communication modes. New APIs are available by including corresponding mode header files {I2S_DRIVER_HEADERS}. Meanwhile, the old APIs in :component_file:`driver/deprecated/driver/i2s.h` are still supported for backward compatibility. But there will be warnings if you keep using the old APIs in your project, these warnings can be suppressed by the Kconfig option :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN`. Here is the general overview of the current I2S files:
.. figure:: ../../_static/diagrams/i2s/i2s_file_structure.png
:align: center
:alt: I2S File Structure
Breaking changes in Concepts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Breaking changes in Concepts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The minimum control unit in new I2S driver will be tx/rx channel instead of a whole I2S controller.
- The minimum control unit in new I2S driver will be tx/rx channel instead of a whole I2S controller.
1. The tx/rx channel in a same I2S controller can be controlled separately, that means they will be initialized, started or stopped separately. Especially for ESP32-C3 and ESP32-S3, tx and rx channels in one controller can be configured to different clocks or modes now, they are able to work in a totally separate way which can help to save the resources of I2S controller. But for ESP32 and ESP32-S2, though their tx/rx can be controlled separately, some hardware resources are still shared by tx and rx, they might affect each other if they are configured to different configurations;
2. The channels can be registered to an available I2S controller automatically by choosing :cpp:member:`i2s_port_t::I2S_NUM_AUTO` as I2S port id. The driver will help you to search for the available tx/rx channel. Of cause, you can still choose a specific port for it;
2. The channels can be registered to an available I2S controller automatically by setting :cpp:enumerator:`i2s_port_t::I2S_NUM_AUTO` as I2S port id. The driver will help you to search for the available tx/rx channel. Of cause, driver can still support to be installed by a specific port;
3. :c:type:`i2s_chan_handle_t` is the handle that used for identifying the I2S channels. All the APIs will require the channel handle, users need to maintain the channel handles by themselves;
4. In order to distinguish tx/rx channel and sound channel, now the word 'channel' is only stand for the tx/rx channel in new driver, meanwhile the sound channel will be called 'slot'.
- I2S communication modes are extracted into three modes.
- I2S communication modes are extracted into three modes.
1. **Standard mode**: Standard mode always has two slots, it can support Philip, MSB and PCM(short sync) format, please refer to :component_file:`hal/include/hal/i2s_std.h` for details;
2. **PDM mode**: PDM mode only support two slots with 16 bits data width, but the configurations of PDM TX and PDM RX are little bit different. For PDM TX, the sample rate can be set by :cpp:member:`i2s_pdm_tx_clk_config_t::sample_rate`, and its clock frequency is depended on the up-sampling configuration. For PDM RX, the sample rate can be set by :cpp:member:`i2s_pdm_rx_clk_config_t::sample_rate`, and its clock frequency is depended on the down-sampling configuration. Please refer to :component_file:`hal/include/hal/i2s_pdm.h` for details;
3. **TDM mode**: TDM mode can support upto 16 slots. It can work in Philip, MSB, PCM(short sync) and PCM(long sync) format, please refer to :component_file:`hal/include/hal/i2s_tdm.h` for details;
4. When we allocate a new channel in a specific mode, we must initialize this channel by corresponding slot configurations and clock configurations. We strongly recommend to use the helper macros to generate the default configurations, in case the default values will be changed one day.
5. Although there are three modes, they still share some general slot and clock configurations which are defined in :component_file:`hal/include/hal/i2s_hal.h`
1. **Standard mode**: Standard mode always has two slots, it can support Philip, MSB and PCM(short sync) format, please refer to :component_file:`driver/include/driver/i2s_std.h` for details;
2. **PDM mode**: PDM mode only support two slots with 16 bits data width, but the configurations of PDM TX and PDM RX are little bit different. For PDM TX, the sample rate can be set by :cpp:member:`i2s_pdm_tx_clk_config_t::sample_rate`, and its clock frequency is depended on the up-sampling configuration. For PDM RX, the sample rate can be set by :cpp:member:`i2s_pdm_rx_clk_config_t::sample_rate`, and its clock frequency is depended on the down-sampling configuration. Please refer to :component_file:`driver/include/driver/i2s_pdm.h` for details;
3. **TDM mode**: TDM mode can support upto 16 slots. It can work in Philip, MSB, PCM(short sync) and PCM(long sync) format, please refer to :component_file:`driver/include/driver/i2s_tdm.h` for details;
4. When allocating a new channel in a specific mode, must initialize this channel by corresponding function. It is strongly recommended to use the helper macros to generate the default configurations, in case the default values will be changed one day.
- States and state-machine are adopted in the new I2S driver to avoid APIs are called in wrong state.
- States and state-machine are adopted in the new I2S driver to avoid APIs called in wrong state.
- The slot configurations and clock configurations can be configured separately.
- The slot configurations and clock configurations can be configured separately.
1. Calling :func:`i2s_init_channel` to initialize the slot/clock/gpio_pin configurations;
2. Calling :c:func:`i2s_set_slot` can change the slot configurations after initialization;
3. Calling :c:func:`i2s_set_clock` can change the clock configurations after initialization.
1. Calling :cpp:func:`i2s_channel_init_std_mode`, :cpp:func:`i2s_channel_init_pdm_rx_mode`, :cpp:func:`i2s_channel_init_pdm_tx_mode` or :cpp:func:`i2s_channel_init_tdm_mode` to initialize the slot/clock/gpio_pin configurations;
2. Calling :cpp:func:`i2s_channel_reconfig_std_slot`, :cpp:func:`i2s_channel_reconfig_pdm_rx_slot`, :cpp:func:`i2s_channel_reconfig_pdm_tx_slot` or :cpp:func:`i2s_channel_reconfig_tdm_slot` can change the slot configurations after initialization;
3. Calling :cpp:func:`i2s_channel_reconfig_std_clock`, :cpp:func:`i2s_channel_reconfig_pdm_rx_clock`, :cpp:func:`i2s_channel_reconfig_pdm_tx_clock` or :cpp:func:`i2s_channel_reconfig_tdm_clock` can change the clock configurations after initialization;
4. Calling :cpp:func:`i2s_channel_reconfig_std_gpio`, :cpp:func:`i2s_channel_reconfig_pdm_rx_gpio`, :cpp:func:`i2s_channel_reconfig_pdm_tx_gpio` or :cpp:func:`i2s_channel_reconfig_tdm_gpio` can change the gpio configurations after initialization.
- ADC and DAC modes are removed. They will only be supported in their own driver and legacy I2S driver.
- ADC and DAC modes are removed. They will only be supported in their own driver and legacy I2S driver.
- :c:func:`i2s_write_channel` and :c:func:`i2s_read_channel` can be aborted by :c:func:`i2s_abort_reading_writing` now.
- :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` can be aborted by :cpp:func:`i2s_channel_abort_reading_writing` now.
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
To use the new I2S driver, please follow these steps:
To use the new I2S driver, please follow these steps:
1. Calling :c:func:`i2s_new_channel` to aquire the channel handles. We should specify the GPIO pins, work mode, work role and I2S port in this step. Besides, we need to input the tx or rx handle to acuire the channel handles that generated by the driver. We don't have to input both two tx and rx handles but at least one handle is needed. While we input both two handles, the driver will work in duplex mode, both tx and rx channel will be avaliable on a same port, and they will share the MCLK, BCLK and WS signal, there will be MCLK(optional), BCLK, WS, DATA_IN and DATA_OUT signals in this case. But if we only input tx or rx handle, this channel will only work in simplex mode.
1. Calling :cpp:func:`i2s_new_channel` to aquire the channel handles. We should specify the work role and I2S port in this step. Besides, the tx or rx channel handles will be generated by the driver. Inputting both two tx and rx handles is not necessary but at least one handle is needed. In the case of inputting both two handles, the driver will work at duplex mode, both tx and rx channel will be avaliable on a same port, and they will share the MCLK, BCLK and WS signal. But if only one of the tx or rx handle is inputted, this channel will only work in simplex mode.
2. Calling :c:func:`i2s_init_channel` to initialize the channel that specified by the given channel handle. We are supposed to input corresponding slot and clock configurations according to the mode that we set in the first step.
2. Calling :func:`i2s_channel_init_std_mode`, :func:`i2s_channel_init_pdm_rx_mode`, :func:`i2s_channel_init_pdm_tx_mode` or :func:`i2s_channel_init_tdm_mode` to initialize the channel to the specified mode. Corresponding slot, clock and gpio configurations are needed in this step.
3. (Optional) We can acquire the event queue handle by :c:func:`i2s_get_event_queue` if we want to monitor the I2S event.
3. (Optional) Calling :cpp:func:`i2s_channel_register_event_callback` to register the ISR event callback functions. I2S events now can be received by the callback function synchronously, instead of from event queue asynchronously.
4. Calling :c:func:`i2s_start_channel` to start the I2S channel. In the new driver, I2S won't start automatically after installed anymore, we are supposed to know clearly whether the channel has started or not.
4. Calling :cpp:func:`i2s_channel_enable` to start the hardware of I2S channel. In the new driver, I2S won't start automatically after installed anymore, users are supposed to know clearly whether the channel has started or not.
5. Reading or writing data by :c:func:`i2s_read_channel` or :c:func:`i2s_write_channel`. Of cause we can only use rx channel handle in :c:func:`i2s_read_channel` and tx channel handle in :c:func:`i2s_write_channel`.
5. Reading or writing data by :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`. Certainly, only rx channel handle is suppoesd to be inputted in :cpp:func:`i2s_channel_read` and tx channel handle in :cpp:func:`i2s_channel_write`.
6. (Optional) We can clear the legacy data in DMA buffer by :c:func:`i2s_clear_dma_buffer`. If we need the driver clear the tx dma buffer automatically, we can set :c:member`auto_clear` in slot configurations.
6. (Optional) The slot, clock and gpio configurations can be changed by corresponding 'reconfig' functions, but :cpp:func:`i2s_channel_disable` must be called before updating the configurations.
7. (Optional) We can change the slot and clock configurations after initialized by :c:func:`i2s_set_slot`, :c:func:`i2s_set_clock`, but we have to call :c:func:`i2s_stop_channel` before we update the configurations.
7. Calling :cpp:func:`i2s_channel_disable` to stop the hardware of I2S channel.
8. (Optional) We can delete the i2s channel by calling :c:func:`i2s_del_channel` if we don't need it any more. The related resources will be released if delete function is called.
8. Calling :cpp:func:`i2s_del_channel` to delete and release the resources of the channel if it is not needed any more, but the channel must be disabled before deleting it.

View File

@ -183,9 +183,9 @@ void bt_i2s_driver_install(void)
};
/* enable I2S */
i2s_driver_install(0, &i2s_config, 0, NULL);
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
i2s_set_pin(0, NULL);
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
@ -198,12 +198,17 @@ void bt_i2s_driver_install(void)
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* enable I2S */
i2s_new_channel(&chan_cfg, &tx_chan, NULL);
i2s_init_std_channel(tx_chan, &std_cfg);
i2s_start_channel(tx_chan);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
#endif
}
@ -212,7 +217,8 @@ void bt_i2s_driver_uninstall(void)
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_driver_uninstall(0);
#else
i2s_del_channel(tx_chan);
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
#endif
}
@ -308,7 +314,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
i2s_set_clk(0, sample_rate, 16, 2);
#else
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
i2s_reconfig_std_clock(tx_chan, &clk_cfg);
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
#endif
ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x",
a2d->audio_cfg.mcc.cie.sbc[0],

View File

@ -110,7 +110,7 @@ static void bt_i2s_task_handler(void *arg)
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
#else
i2s_write_channel(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif
vRingbufferReturnItem(s_ringbuf_i2s, (void *)data);
}

View File

@ -102,11 +102,10 @@ The main function installs I2S to play the audio. A loudspeaker, additional ADC
};
/* enable I2S */
i2s_driver_install(0, &i2s_config, 0, NULL);
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
i2s_set_pin(0, NULL);
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
i2s_std_config_t std_cfg = {
@ -118,12 +117,17 @@ The main function installs I2S to play the audio. A loudspeaker, additional ADC
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* enable I2S */
i2s_new_channel(&chan_cfg, &tx_chan, NULL);
i2s_init_std_channel(tx_chan, &std_cfg);
i2s_start_channel(tx_chan);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
#endif
```

View File

@ -174,7 +174,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
i2s_set_clk(0, sample_rate, 16, 2);
#else
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
i2s_reconfig_clock(tx_chan, &clk_cfg);
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
#endif
ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x",

View File

@ -134,7 +134,7 @@ static void bt_i2s_task_handler(void *arg)
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
#else
i2s_write_channel(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif
vRingbufferReturnItem(s_ringbuf_i2s,(void *)data);
}

View File

@ -705,9 +705,9 @@ void app_main(void)
};
/* enable I2S */
i2s_driver_install(0, &i2s_config, 0, NULL);
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
i2s_set_pin(0, NULL);
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
@ -720,12 +720,17 @@ void app_main(void)
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* enable I2S */
i2s_new_channel(&chan_cfg, &tx_chan, NULL);
i2s_init_std_channel(tx_chan, &std_cfg);
i2s_start_channel(tx_chan);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
#endif
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

Some files were not shown because too many files have changed in this diff Show More