i2s: Introduced a brand new driver

This commit is contained in:
laokaiyao
2021-08-18 19:45:51 +08:00
parent e8d172eb87
commit 621d0aa942
71 changed files with 4934 additions and 1549 deletions

View File

@@ -58,7 +58,15 @@ if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
endif()
if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "i2s.c")
list(APPEND srcs "i2s/i2s_common.c"
"i2s/i2s_std.c"
"i2s/i2s_legacy.c")
if(CONFIG_SOC_I2S_SUPPORTS_PDM)
list(APPEND srcs "i2s/i2s_pdm.c")
endif()
if(CONFIG_SOC_I2S_SUPPORTS_TDM)
list(APPEND srcs "i2s/i2s_tdm.c")
endif()
endif()
if(CONFIG_SOC_TEMP_SENSOR_SUPPORTED)

View File

@@ -305,4 +305,20 @@ menu "Driver Configurations"
endmenu # MCPWM Configuration
endmenu # Driver Configurations
menu "I2S Configuration"
depends on SOC_I2S_SUPPORTED
config I2S_ISR_IRAM_SAFE
bool "I2S ISR IRAM-Safe"
default n
help
Ensure the I2S interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config I2S_SUPPRESS_DEPRECATE_WARN
bool "Suppress leagcy driver deprecated warning"
default n
help
Enable this option will suppress the deprecation warnings of using APIs in deprecated I2S driver.
endmenu # I2S Configuration
endmenu # Driver configurations

View File

@@ -37,7 +37,6 @@
#include "hal/spi_types.h"
#include "driver/spi_common_internal.h"
#elif CONFIG_IDF_TARGET_ESP32
#include "driver/i2s.h"
#include "hal/i2s_types.h"
#include "soc/i2s_periph.h"
#include "esp_private/i2s_platform.h"
@@ -259,7 +258,7 @@ esp_err_t adc_digi_initialize(const adc_digi_init_config_t *init_config)
#elif CONFIG_IDF_TARGET_ESP32
//ADC utilises I2S0 DMA on ESP32
uint32_t dma_chan = 0;
ret = i2s_priv_register_object(&s_adc_digi_ctx, I2S_NUM_0);
ret = i2s_platform_acquire_occupation(I2S_NUM_0, "adc");
if (ret != ESP_OK) {
goto cleanup;
}
@@ -541,7 +540,7 @@ esp_err_t adc_digi_deinitialize(void)
spicommon_periph_free(s_adc_digi_ctx->spi_host);
#elif CONFIG_IDF_TARGET_ESP32
esp_intr_free(s_adc_digi_ctx->intr_hdl);
i2s_priv_deregister_object(s_adc_digi_ctx->i2s_host);
i2s_platform_release_occupation(s_adc_digi_ctx->i2s_host);
#endif
free(s_adc_digi_ctx);
s_adc_digi_ctx = NULL;

View File

@@ -4,147 +4,37 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is for the backward compatible to the deprecated I2S APIs,
* The deprecated APIs will no longer supported in the future
* Please refer to "driver/i2s_controller.h" for the latest I2S driver
* Note that only one set of I2S APIs is allowed to be used at the same time
*/
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/soc_caps.h"
#include "hal/i2s_types.h"
#include "esp_intr_alloc.h"
#include "soc/i2s_periph.h"
#include "driver/i2s_types_legacy.h"
#if SOC_I2S_SUPPORTS_ADC
#include "driver/adc.h"
#endif
#if !CONFIG_I2S_SUPPRESS_DEPRECATE_WARN
#warning "This set of I2S APIs has been deprecated, \
please include 'driver/i2s_std.h', 'driver/i2s_pdm' or 'driver/i2s_tdm' instead. \
if you want to keep using the old APIs and ignore this warning, \
you can enable 'Suppress leagcy driver deprecated warning' option under 'I2S Configuration' menu in Kconfig"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define I2S_PIN_NO_CHANGE (-1) /*!< Use in i2s_pin_config_t for pins which should not be changed */
/**
* @brief I2S port number, the max port number is (I2S_NUM_MAX -1).
*/
typedef enum {
I2S_NUM_0 = 0, /*!< I2S port 0 */
#if SOC_I2S_NUM > 1
I2S_NUM_1 = 1, /*!< I2S port 1 */
#endif
I2S_NUM_MAX, /*!< I2S port max */
} i2s_port_t;
#if SOC_I2S_SUPPORTS_PCM
/**
* @brief I2S PCM configuration
*
*/
typedef struct {
i2s_pcm_compress_t pcm_type; /*!< I2S PCM a/u-law decompress or compress type */
} i2s_pcm_cfg_t;
#endif
#if SOC_I2S_SUPPORTS_PDM_TX
/**
* @brief Default I2S PDM Up-Sampling Rate configuration
*/
#define I2S_PDM_DEFAULT_UPSAMPLE_CONFIG(rate) { \
.sample_rate = rate, \
.fp = 960, \
.fs = (rate) / 100, \
}
/**
* @brief I2S PDM up-sample rate configuration
* @note TX PDM can only be set to the following two upsampling rate configurations:
* 1: fp = 960, fs = sample_rate / 100, in this case, Fpdm = 128*48000
* 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate
* If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000.
* Otherwise, the second configuration should be applied.
*/
typedef struct {
int sample_rate; /*!< I2S PDM sample rate */
int fp; /*!< I2S PDM TX upsampling paramater. Normally it should be set to 960 */
int fs; /*!< I2S PDM TX upsampling paramater. When it is set to 480, the pdm clock frequency Fpdm = 128 * sample_rate, when it is set to sample_rate / 100 Fpdm will be fixed to 128*48000 */
} i2s_pdm_tx_upsample_cfg_t;
#endif
/**
* @brief I2S pin number for i2s_set_pin
*
*/
typedef struct {
int mck_io_num; /*!< MCK in out pin. Note that ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only*/
int bck_io_num; /*!< BCK in out pin*/
int ws_io_num; /*!< WS in out pin*/
int data_out_num; /*!< DATA out pin*/
int data_in_num; /*!< DATA in pin*/
} i2s_pin_config_t;
/**
* @brief I2S driver configuration parameters
*
*/
typedef struct {
i2s_mode_t mode; /*!< I2S work mode */
uint32_t sample_rate; /*!< I2S sample rate */
i2s_bits_per_sample_t bits_per_sample; /*!< I2S sample bits in one channel */
i2s_channel_fmt_t channel_format; /*!< I2S channel format.*/
i2s_comm_format_t communication_format; /*!< I2S communication format */
int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */
union {
int dma_desc_num; /*!< The total number of descriptors used by I2S DMA to receive/transmit data */
int dma_buf_count __attribute__((deprecated)); /*!< This is an alias to 'dma_desc_num' for backward compatibility */
};
union {
int dma_frame_num; /*!< Number of frames for one-time sampling. The frame here means the total data from all the channels in a WS cycle */
int dma_buf_len __attribute__((deprecated)); /*!< This is an alias to 'dma_frame_num' for backward compatibility */
};
bool use_apll; /*!< I2S using APLL as main I2S clock, enable it to get accurate clock */
bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor if there is underflow condition (helps in avoiding noise in case of data unavailability) */
int fixed_mclk; /*!< I2S using fixed MCLK output. If use_apll = true and fixed_mclk > 0, then the clock output for i2s is fixed and equal to the fixed_mclk value. If fixed_mclk set, mclk_multiple won't take effect */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of I2S master clock(MCLK) to sample rate */
i2s_bits_per_chan_t bits_per_chan; /*!< I2S total bits in one channel only take effect when larger than 'bits_per_sample', default '0' means equal to 'bits_per_sample' */
#if SOC_I2S_SUPPORTS_TDM
i2s_channel_t chan_mask; /*!< I2S active channel bit mask, set value in `i2s_channel_t` to enable specific channel, the bit map of active channel can not exceed (0x1<<total_chan). */
uint32_t total_chan; /*!< I2S Total number of channels. If it is smaller than the biggest active channel number, it will be set to this number automatically. */
bool left_align; /*!< Set to enable left alignment */
bool big_edin; /*!< Set to enable big edin */
bool bit_order_msb; /*!< Set to enable msb order */
bool skip_msk; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
#endif // SOC_I2S_SUPPORTS_TDM
} i2s_driver_config_t;
typedef i2s_driver_config_t i2s_config_t; // for backward compatible
typedef intr_handle_t i2s_isr_handle_t; // for backward compatible
/**
* @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;
/**
* @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 Set I2S pin number
*
@@ -156,7 +46,7 @@ typedef struct {
*
* @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_PIN_NO_CHANGE for any pin where
* Inside the pin configuration structure, set I2S_GPIO_UNUSED 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

@@ -0,0 +1,232 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is for the backward compatible to the deprecated I2S APIs,
* The deprecated APIs will no longer supported in the future
* Please refer to "hal/i2s_types.h" for the latest I2S driver types
* Note that only one set of I2S APIs is allowed to be used at the same time
*/
#pragma once
#include "hal/i2s_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/******************** Deprecated Types **********************/
#define I2S_PIN_NO_CHANGE (-1) /*!< Used in i2s_pin_config_t for pins which should not be changed */
/**
* @brief I2S bit width per sample.
*/
typedef enum {
I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< data bit-width: 8 */
I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< data bit-width: 16 */
I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< data bit-width: 24 */
I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< data bit-width: 32 */
} i2s_bits_per_sample_t;
/**
* @brief I2S bit width per chan.
*/
typedef enum {
I2S_BITS_PER_CHAN_DEFAULT = (0), /*!< channel bit-width equals to data bit-width */
I2S_BITS_PER_CHAN_8BIT = (8), /*!< channel bit-width: 8 */
I2S_BITS_PER_CHAN_16BIT = (16), /*!< channel bit-width: 16 */
I2S_BITS_PER_CHAN_24BIT = (24), /*!< channel bit-width: 24 */
I2S_BITS_PER_CHAN_32BIT = (32), /*!< channel bit-width: 32 */
} i2s_bits_per_chan_t;
/**
* @brief I2S channel.
*/
typedef enum {
I2S_CHANNEL_MONO = 1, /*!< I2S channel (mono), one channel activated. In this mode, you only need to send one channel data but the fifo will copy same data for the other unactivated channels automatically, then both channels will transmit same data. */
I2S_CHANNEL_STEREO = 2, /*!< I2S channel (stereo), two (or more) channels activated. In this mode, these channels will transmit different data. */
#if SOC_I2S_SUPPORTS_TDM
// Bit map of activated chan.
// There are 16 channels in TDM mode.
// For TX module, only the activated channel send the audio data, the unactivated channel send a constant(configurable) or will be skiped if 'skip_msk' is set.
// For RX module, only receive the audio data in activated channels, the data in unactivated channels will be ignored.
// the bit map of activated channel can not exceed the maximum enabled channel number (i.e. 0x10000 << total_chan_num).
// e.g: active_chan_mask = (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH3), here the active_chan_number is 2 and total_chan_num is not supposed to be smaller than 4.
I2S_TDM_ACTIVE_CH0 = (0x1 << 16), /*!< I2S channel 0 activated */
I2S_TDM_ACTIVE_CH1 = (0x1 << 17), /*!< I2S channel 1 activated */
I2S_TDM_ACTIVE_CH2 = (0x1 << 18), /*!< I2S channel 2 activated */
I2S_TDM_ACTIVE_CH3 = (0x1 << 19), /*!< I2S channel 3 activated */
I2S_TDM_ACTIVE_CH4 = (0x1 << 20), /*!< I2S channel 4 activated */
I2S_TDM_ACTIVE_CH5 = (0x1 << 21), /*!< I2S channel 5 activated */
I2S_TDM_ACTIVE_CH6 = (0x1 << 22), /*!< I2S channel 6 activated */
I2S_TDM_ACTIVE_CH7 = (0x1 << 23), /*!< I2S channel 7 activated */
I2S_TDM_ACTIVE_CH8 = (0x1 << 24), /*!< I2S channel 8 activated */
I2S_TDM_ACTIVE_CH9 = (0x1 << 25), /*!< I2S channel 9 activated */
I2S_TDM_ACTIVE_CH10 = (0x1 << 26), /*!< I2S channel 10 activated */
I2S_TDM_ACTIVE_CH11 = (0x1 << 27), /*!< I2S channel 11 activated */
I2S_TDM_ACTIVE_CH12 = (0x1 << 28), /*!< I2S channel 12 activated */
I2S_TDM_ACTIVE_CH13 = (0x1 << 29), /*!< I2S channel 13 activated */
I2S_TDM_ACTIVE_CH14 = (0x1 << 30), /*!< I2S channel 14 activated */
I2S_TDM_ACTIVE_CH15 = (0x1 << 31), /*!< I2S channel 15 activated */
#endif
} i2s_channel_t;
/**
* @brief I2S communication standard format
*/
typedef enum {
I2S_COMM_FORMAT_STAND_I2S = 0X01, /*!< I2S communication I2S Philips standard, data launch at second BCK*/
I2S_COMM_FORMAT_STAND_MSB = 0X02, /*!< I2S communication MSB alignment standard, data launch at first BCK*/
I2S_COMM_FORMAT_STAND_PCM_SHORT = 0x04, /*!< PCM Short standard, also known as DSP mode. The period of synchronization signal (WS) is 1 bck cycle.*/
I2S_COMM_FORMAT_STAND_PCM_LONG = 0x0C, /*!< PCM Long standard. The period of synchronization signal (WS) is channel_bit*bck cycles.*/
I2S_COMM_FORMAT_STAND_MAX, /*!< standard max*/
//old definition will be removed in the future.
I2S_COMM_FORMAT_I2S __attribute__((deprecated)) = 0x01, /*!< I2S communication format I2S, correspond to `I2S_COMM_FORMAT_STAND_I2S`*/
I2S_COMM_FORMAT_I2S_MSB __attribute__((deprecated)) = 0x01, /*!< I2S format MSB, (I2S_COMM_FORMAT_I2S |I2S_COMM_FORMAT_I2S_MSB) correspond to `I2S_COMM_FORMAT_STAND_I2S`*/
I2S_COMM_FORMAT_I2S_LSB __attribute__((deprecated)) = 0x02, /*!< I2S format LSB, (I2S_COMM_FORMAT_I2S |I2S_COMM_FORMAT_I2S_LSB) correspond to `I2S_COMM_FORMAT_STAND_MSB`*/
I2S_COMM_FORMAT_PCM __attribute__((deprecated)) = 0x04, /*!< I2S communication format PCM, correspond to `I2S_COMM_FORMAT_STAND_PCM_SHORT`*/
I2S_COMM_FORMAT_PCM_SHORT __attribute__((deprecated)) = 0x04, /*!< PCM Short, (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_SHORT) correspond to `I2S_COMM_FORMAT_STAND_PCM_SHORT`*/
I2S_COMM_FORMAT_PCM_LONG __attribute__((deprecated)) = 0x08, /*!< PCM Long, (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_LONG) correspond to `I2S_COMM_FORMAT_STAND_PCM_LONG`*/
} i2s_comm_format_t;
/**
* @brief I2S channel format type
*/
typedef enum {
I2S_CHANNEL_FMT_RIGHT_LEFT, /*!< Separated left and right channel */
I2S_CHANNEL_FMT_ALL_RIGHT, /*!< Load right channel data in both two channels */
I2S_CHANNEL_FMT_ALL_LEFT, /*!< Load left channel data in both two channels */
I2S_CHANNEL_FMT_ONLY_RIGHT, /*!< Only load data in right channel (mono mode) */
I2S_CHANNEL_FMT_ONLY_LEFT, /*!< Only load data in left channel (mono mode) */
#if SOC_I2S_SUPPORTS_TDM
// Multiple channels are available with TDM feature
I2S_CHANNEL_FMT_MULTIPLE, /*!< More than two channels are used */
#endif
} i2s_channel_fmt_t;
/**
* @brief I2S Mode
*/
typedef enum {
I2S_MODE_MASTER = (0x1 << 0), /*!< Master mode*/
I2S_MODE_SLAVE = (0x1 << 1), /*!< Slave mode*/
I2S_MODE_TX = (0x1 << 2), /*!< TX mode*/
I2S_MODE_RX = (0x1 << 3), /*!< RX mode*/
#if SOC_I2S_SUPPORTS_DAC
//built-in DAC functions are only supported on I2S0 for ESP32 chip.
I2S_MODE_DAC_BUILT_IN = (0x1 << 4), /*!< Output I2S data to built-in DAC, no matter the data format is 16bit or 32 bit, the DAC module will only take the 8bits from MSB*/
#endif // SOC_I2S_SUPPORTS_DAC
#if SOC_I2S_SUPPORTS_ADC
I2S_MODE_ADC_BUILT_IN = (0x1 << 5), /*!< Input I2S data from built-in ADC, each data can be 12-bit width at most*/
#endif // SOC_I2S_SUPPORTS_ADC
// PDM functions are only supported on I2S0 (all chips).
I2S_MODE_PDM = (0x1 << 6), /*!< I2S PDM mode*/
} i2s_mode_t;
#if SOC_I2S_SUPPORTS_DAC
/**
* @brief I2S DAC mode for i2s_set_dac_mode.
*
* @note Built-in DAC functions are only supported on I2S0 for current ESP32 chip.
*/
typedef enum {
I2S_DAC_CHANNEL_DISABLE = 0, /*!< Disable I2S built-in DAC signals*/
I2S_DAC_CHANNEL_RIGHT_EN = 1, /*!< Enable I2S built-in DAC right channel, maps to DAC channel 1 on GPIO25*/
I2S_DAC_CHANNEL_LEFT_EN = 2, /*!< Enable I2S built-in DAC left channel, maps to DAC channel 2 on GPIO26*/
I2S_DAC_CHANNEL_BOTH_EN = 0x3, /*!< Enable both of the I2S built-in DAC channels.*/
I2S_DAC_CHANNEL_MAX = 0x4, /*!< I2S built-in DAC mode max index*/
} i2s_dac_mode_t;
#endif //SOC_I2S_SUPPORTS_DAC
/**
* @brief I2S GPIO pins configuration
*/
typedef struct {
int mck_io_num; /*!< MCK pin, output */
int bck_io_num; /*!< BCK pin, input in slave role, output in master role */
int ws_io_num; /*!< WS pin, input in slave role, output in master role */
int data_out_num; /*!< DATA pin, output */
int data_in_num; /*!< DATA pin, input */
} i2s_pin_config_t;
#if SOC_I2S_SUPPORTS_PCM
/**
* @brief I2S PCM configuration
*
*/
typedef struct {
i2s_pcm_compress_t pcm_type; /*!< I2S PCM a/u-law decompress or compress type */
} i2s_pcm_cfg_t;
#endif
#if SOC_I2S_SUPPORTS_PDM_TX
/**
* @brief Default I2S PDM Up-Sampling Rate configuration
*/
#define I2S_PDM_DEFAULT_UPSAMPLE_CONFIG(rate) { \
.sample_rate = rate, \
.fp = 960, \
.fs = (rate) / 100, \
}
/**
* @brief I2S PDM up-sample rate configuration
* @note TX PDM can only be set to the following two up-sampling rate configurations:
* 1: fp = 960, fs = sample_rate / 100, in this case, Fpdm = 128*48000
* 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate
* If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000.
* Otherwise, the second configuration should be applied.
*/
typedef struct {
int sample_rate; /*!< I2S PDM sample rate */
int fp; /*!< I2S PDM TX up-sampling parameter. Normally it should be set to 960 */
int fs; /*!< I2S PDM TX up-sampling parameter. When it is set to 480, the pdm clock frequency Fpdm = 128 * sample_rate, when it is set to sample_rate / 100 Fpdm will be fixed to 128*48000 */
} i2s_pdm_tx_upsample_cfg_t;
#endif
/**
* @brief I2S driver configuration parameters
*
*/
typedef struct {
i2s_mode_t mode; /*!< I2S work mode */
uint32_t sample_rate; /*!< I2S sample rate */
i2s_bits_per_sample_t bits_per_sample; /*!< I2S sample bits in one channel */
i2s_channel_fmt_t channel_format; /*!< I2S channel format.*/
i2s_comm_format_t communication_format; /*!< I2S communication format */
int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */
union {
int dma_desc_num; /*!< The total number of descriptors used by I2S DMA to receive/transmit data */
int dma_buf_count __attribute__((deprecated)); /*!< This is an alias to 'dma_desc_num' for backward compatibility */
};
union {
int dma_frame_num; /*!< Frame number for one-time sampling. Frame here means the total data from all the channels in a WS cycle */
int dma_buf_len __attribute__((deprecated)); /*!< This is an alias to 'dma_frame_num' for backward compatibility */
};
bool use_apll; /*!< I2S using APLL as main I2S clock, enable it to get accurate clock */
bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor if there is underflow condition (helps in avoiding noise in case of data unavailability) */
int fixed_mclk; /*!< I2S using fixed MCLK output. If use_apll = true and fixed_mclk > 0, then the clock output for i2s is fixed and equal to the fixed_mclk value. If fixed_mclk set, mclk_multiple won't take effect */
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of I2S master clock(MCLK) to sample rate */
i2s_bits_per_chan_t bits_per_chan; /*!< I2S total bits in one channel only take effect when larger than 'bits_per_sample', default '0' means equal to 'bits_per_sample' */
#if SOC_I2S_SUPPORTS_TDM
i2s_channel_t chan_mask; /*!< I2S active channel bit mask, set value in `i2s_channel_t` to enable specific channel, the bit map of active channel can not exceed (0x1<<total_chan). */
uint32_t total_chan; /*!< I2S Total number of channels. If it is smaller than the biggest active channel number, it will be set to this number automatically. */
bool left_align; /*!< Set to enable left alignment */
bool big_edin; /*!< Set to enable big endian */
bool bit_order_msb; /*!< Set to enable msb order */
bool skip_msk; /*!< Set to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */
#endif // SOC_I2S_SUPPORTS_TDM
} i2s_driver_config_t;
typedef i2s_driver_config_t i2s_config_t; // for backward compatible
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,963 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "soc/i2s_periph.h"
#include "soc/soc_caps.h"
#include "soc/soc.h"
#include "hal/gpio_hal.h"
#include "hal/i2s_hal.h"
#if SOC_I2S_SUPPORTS_ADC_DAC
#include "hal/adc_ll.h"
#endif
#if SOC_I2S_SUPPORTS_APLL
#include "clk_ctrl_os.h"
#endif
#include "esp_private/i2s_platform.h"
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "driver/i2s_common.h"
#include "i2s_private.h"
#include "clk_ctrl_os.h"
#include "esp_intr_alloc.h"
#include "esp_check.h"
#include "esp_attr.h"
// // #include "esp_efuse.h"
#include "esp_rom_gpio.h"
#include "sdkconfig.h"
#define I2S_DMA_BUFFER_MAX_SIZE (4092)
// If ISR handler is allowed to run whilst cache is disabled,
// Make sure all the code and related variables used by the handler are in the SRAM
#if CONFIG_I2S_ISR_IRAM_SAFE
#define I2S_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define I2S_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define I2S_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define I2S_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif //CONFIG_I2S_ISR_IRAM_SAFE
/**
* @brief Static i2s platform object
* @note For saving all the I2S related information
*/
i2s_platform_t s_i2s = {
.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED,
.controller[0 ... (I2S_NUM_MAX - 1)] = NULL, // groups will be lazy installed
.comp_name[0 ... (I2S_NUM_MAX - 1)] = NULL,
};
static const char *TAG = "I2S_COMMON";
/*---------------------------------------------------------------------------
I2S Static APIs
----------------------------------------------------------------------------
Scope: This file only
----------------------------------------------------------------------------*/
static void i2s_tx_start(i2s_chan_handle_t handle)
{
/* No lock here beacuse semaphore has been taken while calling this function */
i2s_hal_tx_reset(&(handle->parent->hal));
i2s_hal_tx_reset_fifo(&(handle->parent->hal));
#if SOC_GDMA_SUPPORTED
gdma_reset(handle->dma.dma_chan);
gdma_start(handle->dma.dma_chan, (uint32_t) handle->dma.desc[0]);
#else
i2s_hal_tx_reset_dma(&(handle->parent->hal));
i2s_hal_tx_enable_intr(&(handle->parent->hal));
i2s_hal_tx_start_link(&(handle->parent->hal), (uint32_t) handle->dma.desc[0]);
#endif
i2s_hal_tx_start(&(handle->parent->hal));
}
static void i2s_rx_start(i2s_chan_handle_t handle)
{
/* No lock here beacuse semaphore has been taken while calling this function */
i2s_hal_rx_reset(&(handle->parent->hal));
i2s_hal_rx_reset_fifo(&(handle->parent->hal));
#if SOC_GDMA_SUPPORTED
gdma_reset(handle->dma.dma_chan);
gdma_start(handle->dma.dma_chan, (uint32_t) handle->dma.desc[0]);
#else
i2s_hal_rx_reset_dma(&(handle->parent->hal));
i2s_hal_rx_enable_intr(&(handle->parent->hal));
i2s_hal_rx_start_link(&(handle->parent->hal), (uint32_t) handle->dma.desc[0]);
#endif
i2s_hal_rx_start(&(handle->parent->hal));
}
static void i2s_tx_stop(i2s_chan_handle_t handle)
{
/* No lock here beacuse semaphore has been taken while calling this function */
#if SOC_GDMA_SUPPORTED
i2s_hal_tx_stop(&(handle->parent->hal));
gdma_stop(handle->dma.dma_chan);
#else
i2s_hal_tx_stop(&(handle->parent->hal));
i2s_hal_tx_stop_link(&(handle->parent->hal));
i2s_hal_tx_disable_intr(&(handle->parent->hal));
#endif
}
static void i2s_rx_stop(i2s_chan_handle_t handle)
{
/* No lock here beacuse semaphore has been taken while calling this function */
#if SOC_GDMA_SUPPORTED
i2s_hal_rx_stop(&(handle->parent->hal));
gdma_stop(handle->dma.dma_chan);
#else
i2s_hal_rx_stop(&(handle->parent->hal));
i2s_hal_rx_stop_link(&(handle->parent->hal));
i2s_hal_rx_disable_intr(&(handle->parent->hal));
#endif
}
static esp_err_t i2s_destroy_controller_obj(i2s_controller_t **i2s_obj)
{
I2S_NULL_POINTER_CHECK(TAG, i2s_obj);
I2S_NULL_POINTER_CHECK(TAG, *i2s_obj);
ESP_RETURN_ON_FALSE(!(*i2s_obj)->rx_chan && !(*i2s_obj)->tx_chan,
ESP_ERR_INVALID_STATE, TAG,
"there still have channels under this i2s controller");
int id = (*i2s_obj)->id;
#if SOC_I2S_HW_VERSION_1
i2s_ll_enable_dma((*i2s_obj)->hal.dev, false);
esp_intr_disable((*i2s_obj)->i2s_isr_handle);
esp_intr_free((*i2s_obj)->i2s_isr_handle);
#endif
free(*i2s_obj);
*i2s_obj = NULL;
return i2s_platform_release_occupation(id);
}
/**
* @brief Acquire i2s controller object
*
* @param id i2s port id
* @param search_reverse reverse the sequence of port acquirement
* set false to acquire from I2S_NUM_0 first
* set true to acquire from I2S_NUM_MAX - 1 first
* @return
* - pointer of acquired i2s controller object
*/
static i2s_controller_t *i2s_acquire_controller_obj(int id)
{
if (id < 0 || id >= I2S_NUM_MAX) {
return NULL;
}
/* pre-alloc controller object */
i2s_controller_t *pre_alloc = (i2s_controller_t *)heap_caps_calloc(1, sizeof(i2s_controller_t), I2S_MEM_ALLOC_CAPS);
if (pre_alloc == NULL) {
return NULL;
}
pre_alloc->id = id;
i2s_hal_init(&pre_alloc->hal, id);
#if !SOC_GDMA_SUPPORTED
pre_alloc->i2s_isr_handle = NULL;
#endif
pre_alloc->full_duplex = false;
pre_alloc->tx_chan = NULL;
pre_alloc->rx_chan = NULL;
pre_alloc->mclk = I2S_GPIO_UNUSED;
i2s_controller_t *i2s_obj = NULL;
if (!s_i2s.controller[id]) {
/* Try to occupy this i2s controller
if failed, this controller could be occupied by other components */
if (i2s_platform_acquire_occupation(id, "i2s_controller") == ESP_OK) {
i2s_obj = pre_alloc;
portENTER_CRITICAL(&s_i2s.spinlock);
s_i2s.controller[id] = i2s_obj;
portEXIT_CRITICAL(&s_i2s.spinlock);
#if SOC_I2S_SUPPORTS_ADC_DAC
if (id == I2S_NUM_0) {
adc_ll_digi_set_data_source(ADC_I2S_DATA_SRC_IO_SIG);
}
#endif
} else {
free(pre_alloc);
ESP_LOGE(TAG, "i2s%d might be occupied by other component", id);
}
} else {
i2s_obj = s_i2s.controller[id];
free(pre_alloc);
}
return i2s_obj;
}
static inline bool i2s_take_available_channel(i2s_controller_t *i2s_obj, uint8_t chan_search_mask)
{
bool is_available = false;
#if SOC_I2S_HW_VERSION_1
/* In ESP32 and ESP32-S2, tx channel and rx channel are not totally separated
* Take both two channels in case one channel can affect another
*/
chan_search_mask = I2S_DIR_RX | I2S_DIR_TX;
#endif
portENTER_CRITICAL(&s_i2s.spinlock);
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
i2s_obj->chan_occupancy |= chan_search_mask;
is_available = true;
}
portEXIT_CRITICAL(&s_i2s.spinlock);
return is_available;
}
static esp_err_t i2s_register_channel(i2s_controller_t *i2s_obj, i2s_dir_t dir)
{
I2S_NULL_POINTER_CHECK(TAG, i2s_obj);
i2s_chan_handle_t new_chan = (i2s_chan_handle_t)heap_caps_calloc(1, sizeof(struct i2s_channel_t), I2S_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(new_chan, ESP_ERR_NO_MEM, TAG, "No memory for new channel");
new_chan->mode = I2S_COMM_MODE_NONE;
new_chan->role = I2S_ROLE_MASTER; // Set default role to master
new_chan->dir = dir;
new_chan->state = I2S_CHAN_STATE_REGISTER;
#if SOC_I2S_SUPPORTS_APLL
new_chan->apll_en = false;
#endif
new_chan->mode_info = NULL;
new_chan->parent = i2s_obj;
new_chan->event_queue = NULL;
new_chan->pm_lock = NULL; // Init in i2s_set_clock according to clock source
new_chan->msg_queue = NULL;
new_chan->mutex = xSemaphoreCreateMutex();
new_chan->start = NULL;
new_chan->stop = NULL;
if (!new_chan->mutex) {
ESP_LOGE(TAG, "mutex create failed");
free(new_chan);
return ESP_ERR_NO_MEM;
}
if (dir == I2S_DIR_TX) {
if (i2s_obj->tx_chan) {
i2s_del_channel(i2s_obj->tx_chan);
}
i2s_obj->tx_chan = new_chan;
} else {
if (i2s_obj->rx_chan) {
i2s_del_channel(i2s_obj->rx_chan);
}
i2s_obj->rx_chan = new_chan;
}
return ESP_OK;
}
uint32_t i2s_get_buf_size(i2s_chan_handle_t handle, uint32_t data_bit_width, uint32_t dma_frame_num)
{
uint32_t active_chan = handle->active_slot;
uint32_t bytes_per_sample = ((data_bit_width + 15) / 16) * 2;
uint32_t bytes_per_frame = bytes_per_sample * active_chan;
uint32_t bufsize = dma_frame_num * bytes_per_frame;
/* Limit DMA buffer size if it is out of range (DMA buffer limitation is 4092 bytes) */
if (bufsize > I2S_DMA_BUFFER_MAX_SIZE) {
uint32_t frame_num = I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame;
bufsize = frame_num * bytes_per_frame;
ESP_LOGW(TAG, "dma frame num is out of dma buffer size, limited to %d", frame_num);
}
return bufsize;
}
esp_err_t i2s_free_dma_desc(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
if (!handle->dma.desc) {
return ESP_OK;
}
for (int i = 0; i < handle->dma.desc_num; i++) {
if (handle->dma.bufs[i]) {
free(handle->dma.bufs[i]);
}
if (handle->dma.desc[i]) {
free(handle->dma.desc[i]);
}
}
free(handle->dma.bufs);
free(handle->dma.desc);
handle->dma.desc = NULL;
return ESP_OK;
}
esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bufsize)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(bufsize <= I2S_DMA_BUFFER_MAX_SIZE, ESP_ERR_INVALID_ARG, TAG, "dma buffer can't be bigger than %d", I2S_DMA_BUFFER_MAX_SIZE);
handle->dma.desc_num = num;
handle->dma.buf_size = bufsize;
handle->msg_queue = xQueueCreate(num, sizeof(uint8_t *));
ESP_GOTO_ON_FALSE(handle->msg_queue, ESP_ERR_NO_MEM, err, TAG, "message queue create failed");
handle->dma.desc = (lldesc_t **)heap_caps_calloc(num, sizeof(lldesc_t *), I2S_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc, ESP_ERR_NO_MEM, err, TAG, "create I2S DMA decriptor array failed");
handle->dma.bufs = (uint8_t **)heap_caps_calloc(num, sizeof(uint8_t *), I2S_MEM_ALLOC_CAPS);
for (int i = 0; i < num; i++) {
/* Allocate DMA descriptor */
handle->dma.desc[i] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(handle->dma.desc[i], ESP_ERR_NO_MEM, err, TAG, "allocate DMA description failed");
handle->dma.desc[i]->owner = 1;
handle->dma.desc[i]->eof = 1;
handle->dma.desc[i]->sosf = 0;
handle->dma.desc[i]->length = bufsize;
handle->dma.desc[i]->size = bufsize;
handle->dma.desc[i]->offset = 0;
handle->dma.bufs[i] = (uint8_t *) heap_caps_calloc(1, bufsize * sizeof(uint8_t), MALLOC_CAP_DMA);
handle->dma.desc[i]->buf = handle->dma.bufs[i];
ESP_GOTO_ON_FALSE(handle->dma.desc[i]->buf, ESP_ERR_NO_MEM, err, TAG, "allocate DMA buffer failed");
}
/* Connect DMA descriptor as a circle */
for (int i = 0; i < num; i++) {
/* Link to the next descriptor */
handle->dma.desc[i]->empty = (uint32_t)((i < (num - 1)) ? (handle->dma.desc[i + 1]) : handle->dma.desc[0]);
}
if (handle->dir == I2S_DIR_RX) {
i2s_ll_rx_set_eof_num(handle->parent->hal.dev, bufsize);
}
ESP_LOGD(TAG, "DMA malloc info: dma_desc_num = %d, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = %d, ", bufsize, num);
return ESP_OK;
err:
i2s_free_dma_desc(handle);
return ret;
}
#if SOC_I2S_SUPPORTS_APLL
uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq)
{
/* Calculate the expected APLL */
int mclk_div = (int)((SOC_APLL_MIN_HZ / mclk_freq) + 1);
/* apll_freq = mclk * div
* when div = 1, hardware will still divide 2
* when div = 0, the final mclk will be unpredictable
* So the div here should be at least 2 */
mclk_div = mclk_div < 2 ? 2 : mclk_div;
uint32_t expt_freq = mclk_freq * mclk_div;
uint32_t real_freq = 0;
esp_err_t ret = periph_rtc_apll_freq_set(expt_freq, &real_freq);
if (ret == ESP_ERR_INVALID_ARG) {
ESP_LOGE(TAG, "set APLL freq failed due to invalid argument");
return 0;
}
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz while the expected frequency is %d Hz", real_freq, expt_freq);
ESP_LOGW(TAG, "Trying to work at %d Hz...", real_freq);
}
ESP_LOGI(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
return real_freq;
}
#endif
static inline void i2s_isr_send_event_queue(QueueHandle_t event_queue, i2s_event_type_t type, portBASE_TYPE *need_yield)
{
if (event_queue) {
i2s_event_t i2s_event;
portBASE_TYPE _need_yield;
i2s_event.type = type;
xQueueSendFromISR(event_queue, (void * )&i2s_event, &_need_yield);
*need_yield |= _need_yield;
}
}
#if SOC_GDMA_SUPPORTED
static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
i2s_chan_handle_t handle = (i2s_chan_handle_t)user_data;
portBASE_TYPE need_yield = 0;
BaseType_t ret = 0;
lldesc_t *finish_desc;
if (handle) {
finish_desc = (lldesc_t *)event_data->rx_eof_desc_addr;
ret = xQueueSendFromISR(handle->msg_queue, &(finish_desc->buf), &need_yield);
i2s_event_type_t type = (ret == pdTRUE) ? I2S_EVENT_RX_DONE : I2S_EVENT_RX_Q_OVF;
i2s_isr_send_event_queue(handle->event_queue, type, &need_yield);
}
return need_yield;
}
static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
i2s_chan_handle_t handle = (i2s_chan_handle_t)user_data;
portBASE_TYPE need_yield = 0;
BaseType_t ret;
lldesc_t *finish_desc;
if (handle) {
finish_desc = (lldesc_t *)(event_data->tx_eof_desc_addr);
if (handle->dma.auto_clear) {
uint8_t *sent_buf = (uint8_t *)finish_desc->buf;
memset(sent_buf, 0, handle->dma.buf_size);
}
ret = xQueueSendFromISR(handle->msg_queue, &(finish_desc->buf), &need_yield);
i2s_event_type_t type = (ret == pdTRUE) ? I2S_EVENT_TX_DONE : I2S_EVENT_TX_Q_OVF;
i2s_isr_send_event_queue(handle->event_queue, type, &need_yield);
}
return need_yield;
}
#else
static void IRAM_ATTR i2s_default_intr_handler(void *arg)
{
portBASE_TYPE tx_need_yield = 0;
portBASE_TYPE rx_need_yield = 0;
lldesc_t *finish_desc = NULL;
BaseType_t ret;
i2s_controller_t *obj = (i2s_controller_t *)arg;
uint32_t status = i2s_hal_get_intr_status(&(obj->hal));
i2s_hal_clear_intr_status(&(obj->hal), status);
if (!obj || !status) {
return;
}
if (obj->tx_chan && (status & I2S_LL_EVENT_TX_EOF)) {
i2s_hal_get_out_eof_des_addr(&(obj->hal), (uint32_t *)&finish_desc);
// Auto clear the dma buffer after data sent
if (obj->tx_chan->dma.auto_clear) {
uint8_t *buff = (uint8_t *)finish_desc->buf;
memset(buff, 0, obj->tx_chan->dma.buf_size);
}
ret = xQueueSendFromISR(obj->tx_chan->msg_queue, &(finish_desc->buf), &tx_need_yield);
i2s_event_type_t type = (ret == pdTRUE) ? I2S_EVENT_TX_DONE : I2S_EVENT_TX_Q_OVF;
i2s_isr_send_event_queue(obj->tx_chan->event_queue, type, &tx_need_yield);
}
if (obj->rx_chan && (status & I2S_LL_EVENT_RX_EOF)) {
i2s_hal_get_in_eof_des_addr(&(obj->hal), (uint32_t *)&finish_desc);
ret = xQueueSendFromISR(obj->rx_chan->msg_queue, &(finish_desc->buf), &rx_need_yield);
i2s_event_type_t type = (ret == pdTRUE) ? I2S_EVENT_RX_DONE : I2S_EVENT_RX_Q_OVF;
i2s_isr_send_event_queue(obj->rx_chan->event_queue, type, &rx_need_yield);
}
if (tx_need_yield || rx_need_yield) {
portYIELD_FROM_ISR();
}
}
#endif
/**
* @brief I2S DMA interrupt initialization
* @note I2S will use GDMA if chip supports, and the interrupt is triggered by GDMA.
*
* @param handle I2S channel handle
* @param intr_flag Interrupt allocation flag
* @return
* - ESP_OK I2S DMA interrupt initialize success
* - ESP_ERR_NOT_FOUND GDMA channel not found
* - ESP_ERR_INVALID_ARG Invalid arguments
* - ESP_ERR_INVALID_STATE GDMA state error
*/
esp_err_t i2s_init_dma_intr(i2s_chan_handle_t handle, int intr_flag)
{
i2s_port_t port_id = handle->parent->id;
ESP_RETURN_ON_FALSE((port_id >= 0) && (port_id < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid handle");
#if SOC_GDMA_SUPPORTED
/* Set GDMA trigger module */
gdma_trigger_t trig = {.periph = GDMA_TRIG_PERIPH_I2S};
switch (port_id) {
#if SOC_I2S_NUM > 1
case I2S_NUM_1:
trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S1;
break;
#endif
default:
trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0;
break;
}
/* Set GDMA config */
gdma_channel_alloc_config_t dma_cfg = {};
if (handle->dir & I2S_DIR_TX) {
dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX;
/* Register a new GDMA tx channel */
ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &handle->dma.dma_chan), TAG, "Register tx dma channel error");
ESP_RETURN_ON_ERROR(gdma_connect(handle->dma.dma_chan, trig), TAG, "Connect tx dma channel error");
gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback};
/* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */
gdma_register_tx_event_callbacks(handle->dma.dma_chan, &cb, handle);
}
if (handle->dir & I2S_DIR_RX) {
dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX;
/* Register a new GDMA rx channel */
ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &handle->dma.dma_chan), TAG, "Register rx dma channel error");
ESP_RETURN_ON_ERROR(gdma_connect(handle->dma.dma_chan, trig), TAG, "Connect rx dma channel error");
gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback};
/* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */
gdma_register_rx_event_callbacks(handle->dma.dma_chan, &cb, handle);
}
#else
/* Initial I2S module interrupt */
if (!handle->parent->i2s_isr_handle) {
ESP_RETURN_ON_ERROR(esp_intr_alloc(i2s_periph_signal[port_id].irq, intr_flag, i2s_default_intr_handler, handle->parent, &(handle->parent->i2s_isr_handle)), TAG, "Register I2S Interrupt error");
esp_intr_enable(handle->parent->i2s_isr_handle);
}
/* Start DMA */
i2s_ll_enable_dma(handle->parent->hal.dev, true);
#endif // SOC_GDMA_SUPPORTED
return ESP_OK;
}
void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input)
{
/* Ignore the pin if pin = -1 */
if (gpio != I2S_GPIO_UNUSED) {
if (is_input) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
/* Set direction, for some GPIOs, the input function are not enabled as default */
gpio_set_direction(gpio, GPIO_MODE_INPUT);
esp_rom_gpio_connect_in_signal(gpio, signal_idx, 0);
} else {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(gpio, signal_idx, 0, 0);
}
}
}
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num)
{
if (gpio_num == -1) {
return ESP_OK;
}
#if CONFIG_IDF_TARGET_ESP32
ESP_RETURN_ON_FALSE((gpio_num == GPIO_NUM_0 || gpio_num == GPIO_NUM_1 || gpio_num == GPIO_NUM_3),
ESP_ERR_INVALID_ARG, TAG,
"ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num);
bool is_i2s0 = id == I2S_NUM_0;
if (gpio_num == GPIO_NUM_0) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFFF0 : 0xFFFF);
} else if (gpio_num == GPIO_NUM_1) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xF0F0 : 0xF0FF);
} else {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFF00 : 0xFF0F);
}
#else
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid");
gpio_matrix_out_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, 0, 0);
#endif
ESP_LOGI(TAG, "MCLK is pinned to GPIO%d on I2S%d", id, gpio_num);
return ESP_OK;
}
/*---------------------------------------------------------------------------
I2S bus Public APIs
----------------------------------------------------------------------------
Scope: Public
----------------------------------------------------------------------------*/
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)
{
/* Parameter validity check */
I2S_NULL_POINTER_CHECK(TAG, chan_cfg);
I2S_NULL_POINTER_CHECK(TAG, tx_handle || rx_handle);
ESP_RETURN_ON_FALSE(chan_cfg->id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid I2S port id");
ESP_RETURN_ON_FALSE(chan_cfg->dma_desc_num >= 2, ESP_ERR_INVALID_ARG, TAG, "there should be at least 2 DMA buffers");
esp_err_t ret = ESP_OK;
i2s_controller_t *i2s_obj = NULL;
i2s_port_t id = chan_cfg->id;
bool channel_found = false;
uint8_t chan_search_mask = 0;
chan_search_mask |= tx_handle ? I2S_DIR_TX : 0;
chan_search_mask |= rx_handle ? I2S_DIR_RX : 0;
/* Channel will be registered to one i2s port automatically if id is I2S_NUM_AUTO
* Otherwise, the channel will be registered to the specific port. */
if (id == I2S_NUM_AUTO) {
for (int i = 0; i < I2S_NUM_MAX && !channel_found; i++) {
i2s_obj = i2s_acquire_controller_obj(i);
if (!i2s_obj) {
continue;
}
channel_found = i2s_take_available_channel(i2s_obj, chan_search_mask);
}
ESP_RETURN_ON_FALSE(i2s_obj, ESP_ERR_NOT_FOUND, TAG, "get i2s object failed");
} else {
i2s_obj = i2s_acquire_controller_obj(id);
ESP_RETURN_ON_FALSE(i2s_obj, ESP_ERR_NOT_FOUND, TAG, "get i2s object failed");
channel_found = i2s_take_available_channel(i2s_obj, chan_search_mask);
}
ESP_GOTO_ON_FALSE(channel_found, ESP_ERR_NOT_FOUND, err, TAG, "no available channel found");
/* Register and specify the tx handle */
if (tx_handle) {
ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_TX), err, TAG, "register I2S tx channel failed");
i2s_obj->tx_chan->role = chan_cfg->role;
i2s_obj->tx_chan->dma.auto_clear = chan_cfg->auto_clear;
i2s_obj->tx_chan->dma.desc_num = chan_cfg->dma_desc_num;
i2s_obj->tx_chan->dma.frame_num = chan_cfg->dma_frame_num;
i2s_obj->tx_chan->start = i2s_tx_start;
i2s_obj->tx_chan->stop = i2s_tx_stop;
*tx_handle = i2s_obj->tx_chan;
ESP_LOGI(TAG, "tx channel is registered on I2S%d successfully", i2s_obj->id);
}
/* Register and specify the rx handle */
if (rx_handle) {
ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_RX), err, TAG, "register I2S rx channel failed");
i2s_obj->rx_chan->role = chan_cfg->role;
i2s_obj->rx_chan->dma.desc_num = chan_cfg->dma_desc_num;
i2s_obj->rx_chan->dma.frame_num = chan_cfg->dma_frame_num;
i2s_obj->rx_chan->start = i2s_rx_start;
i2s_obj->rx_chan->stop = i2s_rx_stop;
*rx_handle = i2s_obj->rx_chan;
ESP_LOGI(TAG, "rx channel is registered on I2S%d successfully", i2s_obj->id);
}
if ((tx_handle != NULL) && (rx_handle != NULL)) {
i2s_obj->full_duplex = true;
}
return ESP_OK;
/* i2s_obj allocated but register channel failed */
err:
/* if the controller object has no channel, find the corresponding global object and destroy it */
if (i2s_obj != NULL && i2s_obj->rx_chan == NULL && i2s_obj->tx_chan == NULL) {
for (int i = 0; i < I2S_NUM_MAX; i++) {
if (i2s_obj == s_i2s.controller[i]) {
i2s_destroy_controller_obj(&s_i2s.controller[i]);
break;
}
}
}
return ret;
}
// TODO: finish delete channel
esp_err_t i2s_del_channel(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
/* Static mutex to avoid double delete */
static SemaphoreHandle_t del_mut = NULL;
if (del_mut == NULL) {
del_mut = xSemaphoreCreateMutex();
}
i2s_controller_t *i2s_obj = handle->parent;
int id = i2s_obj->id;
i2s_dir_t dir = handle->dir;
bool is_bound = true;
xSemaphoreTake(del_mut, portMAX_DELAY);
/* Stop the channel first */
if (handle->state > I2S_CHAN_STATE_READY) {
i2s_abort_reading_writing(handle);
i2s_stop_channel(handle);
}
#if SOC_I2S_HW_VERSION_2
if (dir == I2S_DIR_TX) {
i2s_ll_tx_disable_clock(handle->parent->hal.dev);
} else {
i2s_ll_rx_disable_clock(handle->parent->hal.dev);
}
#endif
#if SOC_I2S_SUPPORTS_APLL
if (handle->apll_en) {
/* Must switch back to D2CLK on ESP32-S2,
* because the clock of some registers are bound to APLL,
* otherwise, once APLL is disabled, the registers can't be updated anymore */
if (handle->dir == I2S_DIR_TX) {
i2s_ll_tx_clk_set_src(handle->parent->hal.dev, I2S_CLK_160M_PLL);
} else {
i2s_ll_rx_clk_set_src(handle->parent->hal.dev, I2S_CLK_160M_PLL);
}
periph_rtc_apll_release();
}
#endif
if (handle->mode_info) {
free(handle->mode_info);
}
if (handle->dma.desc) {
i2s_free_dma_desc(handle);
}
if (handle->msg_queue) {
vQueueDelete(handle->msg_queue);
}
if (handle->event_queue) {
vQueueDelete(handle->event_queue);
}
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
}
#if SOC_I2S_HW_VERSION_1
i2s_obj->chan_occupancy = 0;
#else
i2s_obj->chan_occupancy &= ~(uint32_t)dir;
#endif
#if SOC_GDMA_SUPPORTED
if (handle->dma.dma_chan) {
gdma_del_channel(handle->dma.dma_chan);
}
#endif
if (handle == i2s_obj->tx_chan) {
free(i2s_obj->tx_chan);
i2s_obj->tx_chan = NULL;
i2s_obj->full_duplex = false;
} else if (handle == i2s_obj->rx_chan) {
free(i2s_obj->rx_chan);
i2s_obj->rx_chan = NULL;
i2s_obj->full_duplex = false;
} else {
/* Indicate the delete channel is an unbound free channel */
is_bound = false;
free(handle);
}
/* If the delete channel was bound to a controller before,
we need to destroy this controller object if there is no channel any more */
if (is_bound) {
if (!(i2s_obj->tx_chan) && !(i2s_obj->rx_chan)) {
i2s_destroy_controller_obj(&s_i2s.controller[i2s_obj->id]);
}
ESP_LOGI(TAG, "%s channel on I2S%d deleted", dir == I2S_DIR_TX ? "tx" : "rx", id);
}
xSemaphoreGive(del_mut);
return ESP_OK;
}
QueueHandle_t i2s_get_event_queue(i2s_chan_handle_t handle, uint32_t que_len)
{
QueueHandle_t que;
if (!handle) {
return NULL;
}
xSemaphoreTake(handle->mutex, portMAX_DELAY);
if (handle->event_queue) {
que = handle->event_queue;
} else {
handle->event_queue = xQueueCreate(que_len, sizeof(int *));
que = handle->event_queue;
}
xSemaphoreGive(handle->mutex);
return que;
}
esp_err_t i2s_start_channel(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "the channel has already started or not initialized");
handle->dma.curr_ptr = NULL;
handle->dma.rw_pos = 0;
handle->start(handle);
handle->state = I2S_CHAN_STATE_IDLE;
xSemaphoreGive(handle->mutex);
ESP_LOGI(TAG, "i2s %s channel started", handle->dir == I2S_DIR_TX ? "tx" : "rx");
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_stop_channel(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(handle->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "the channel has not started yet");
handle->stop(handle);
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
ESP_LOGI(TAG, "i2s %s channel stopped", handle->dir == I2S_DIR_TX ? "tx" : "rx");
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
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)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "this channel is not tx channel");
esp_err_t ret = ESP_OK;
char *data_ptr, *src_byte;
size_t bytes_can_write;
*bytes_written = 0;
/* Take the semaphore brefore changing state to ensure only one writing thread running at the same time */
xSemaphoreTake(handle->mutex, ticks_to_wait);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
ESP_GOTO_ON_FALSE(handle->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "this channel has not started yet");
handle->state = I2S_CHAN_STATE_WRITING;
src_byte = (char *)src;
while (size > 0 && handle->state == I2S_CHAN_STATE_WRITING) {
if (handle->dma.rw_pos == handle->dma.buf_size || handle->dma.curr_ptr == NULL) {
if (xQueueReceive(handle->msg_queue, &(handle->dma.curr_ptr), ticks_to_wait) == pdFALSE) {
ret = ESP_ERR_TIMEOUT;
break;
}
handle->dma.rw_pos = 0;
}
ESP_LOGD(TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, handle->dma.rw_pos, handle->dma.buf_size, (int)handle->dma.curr_ptr);
data_ptr = (char *)handle->dma.curr_ptr;
data_ptr += handle->dma.rw_pos;
bytes_can_write = handle->dma.buf_size - handle->dma.rw_pos;
if (bytes_can_write > size) {
bytes_can_write = size;
}
memcpy(data_ptr, src_byte, bytes_can_write);
size -= bytes_can_write;
src_byte += bytes_can_write;
handle->dma.rw_pos += bytes_can_write;
(*bytes_written) += bytes_can_write;
}
/* Need to judge the state before switch back to idle, in case the state has been changed by 'stop' or 'abort' */
if (handle->state == I2S_CHAN_STATE_WRITING) {
handle->state = I2S_CHAN_STATE_IDLE;
}
err:
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
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)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_RX, ESP_ERR_INVALID_ARG, TAG, "this channel is not rx channel");
esp_err_t ret = ESP_OK;
uint8_t *data_ptr, *dest_byte;
int bytes_can_read;
*bytes_read = 0;
dest_byte = (uint8_t *)dest;
/* Take the semaphore brefore changing state to ensure only one reading thread running at the same time */
xSemaphoreTake(handle->mutex, ticks_to_wait);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
ESP_GOTO_ON_FALSE(handle->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, err, TAG, "this channel has not started yet");
handle->state = I2S_CHAN_STATE_READING;
while (size > 0 && handle->state == I2S_CHAN_STATE_READING) {
if (handle->dma.rw_pos == handle->dma.buf_size || handle->dma.curr_ptr == NULL) {
if (xQueueReceive(handle->msg_queue, &(handle->dma.curr_ptr), ticks_to_wait) == pdFALSE) {
ret = ESP_ERR_TIMEOUT;
break;
}
handle->dma.rw_pos = 0;
}
data_ptr = (uint8_t *)handle->dma.curr_ptr;
data_ptr += handle->dma.rw_pos;
bytes_can_read = handle->dma.buf_size - handle->dma.rw_pos;
if (bytes_can_read > (int)size) {
bytes_can_read = size;
}
memcpy(dest_byte, data_ptr, bytes_can_read);
size -= bytes_can_read;
dest_byte += bytes_can_read;
handle->dma.rw_pos += bytes_can_read;
(*bytes_read) += bytes_can_read;
}
/* Need to judge the state before switch back to idle, in case the state has been changed by 'stop' or 'abort' */
if (handle->state == I2S_CHAN_STATE_READING) {
handle->state = I2S_CHAN_STATE_IDLE;
}
err:
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_clear_dma_buffer(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->state > I2S_CHAN_STATE_INIT, ESP_ERR_INVALID_STATE, TAG, "this channel has not initialized yet");
/* Clear all the DMA buffer */
for (int desc_num = handle->dma.desc_num; desc_num > 0; desc_num--) {
memset(handle->dma.bufs[desc_num-1], 0, handle->dma.buf_size);
}
return ESP_OK;
}
esp_err_t i2s_abort_reading_writing(i2s_chan_handle_t handle)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, TAG, "this channel has not started yet");
/* No lock here. Force change state to idle when writing or reading */
handle->state = I2S_CHAN_STATE_IDLE;
return ESP_OK;
}
/*---------------------------------------------------------------------------
I2S Platform APIs
----------------------------------------------------------------------------
Scope: This file and ADC/DAC/LCD driver
----------------------------------------------------------------------------*/
esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name)
{
esp_err_t ret = ESP_OK;
const char *occupied_comp = NULL;
ESP_RETURN_ON_FALSE(id < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
portENTER_CRITICAL(&s_i2s.spinlock);
if ((!s_i2s.controller[id]) && (s_i2s.comp_name[id] == NULL)) {
s_i2s.comp_name[id] = comp_name;
/* Enable module clock */
periph_module_enable(i2s_periph_signal[id].module);
i2s_ll_enable_clock(I2S_LL_GET_HW(id));
} else {
occupied_comp = s_i2s.comp_name[id];
ret = ESP_ERR_NOT_FOUND;
}
portEXIT_CRITICAL(&s_i2s.spinlock);
if (occupied_comp != NULL) {
ESP_LOGE(TAG, "i2s controller %d has been occupied by %s", id, occupied_comp);
}
return ret;
}
esp_err_t i2s_platform_release_occupation(int id)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(id < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
portENTER_CRITICAL(&s_i2s.spinlock);
if ((!s_i2s.controller[id])) {
s_i2s.comp_name[id] = NULL;
/* Disable module clock */
periph_module_disable(i2s_periph_signal[id].module);
i2s_ll_disable_clock(I2S_LL_GET_HW(id));
} else {
ret = ESP_ERR_INVALID_STATE;
}
portEXIT_CRITICAL(&s_i2s.spinlock);
return ret;
}

View File

@@ -16,14 +16,11 @@
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "hal/i2s_hal.h"
#include "hal/i2s_std.h"
#include "hal/i2s_pdm.h"
#include "hal/i2s_tdm.h"
#include "driver/i2s.h"
#if SOC_I2S_SUPPORTS_DAC
#include "driver/dac.h"
#include "driver/adc.h"
#include "adc1_private.h"
#include "../adc1_private.h"
#endif // SOC_I2S_SUPPORTS_ADC
#if SOC_GDMA_SUPPORTED
@@ -57,6 +54,23 @@ static const char *TAG = "I2S";
#define I2S_COMM_MODE_ADC_DAC -1
#endif
/**
* @brief General clock configuration information
* @note It is a general purpose struct, not supposed to be used directly by user
*/
typedef struct {
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 */
#if SOC_I2S_SUPPORTS_PDM_TX
uint32_t up_sample_fp; /*!< Up-sampling param fp */
uint32_t up_sample_fs; /*!< Up-sampling param fs */
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
i2s_pdm_dsr_t dn_sample_mode; /*!< Down-sampling rate mode */
#endif
} i2s_clk_config_t;
/**
* @brief DMA buffer object
*
@@ -104,8 +118,8 @@ typedef struct {
i2s_dir_t dir;
i2s_role_t role;
i2s_comm_mode_t mode;
void *slot_cfg;
void *clk_cfg;
i2s_hal_slot_config_t slot_cfg;
i2s_clk_config_t clk_cfg;
uint32_t active_slot; /*!< Active slot number */
uint32_t total_slot; /*!< Total slot number */
} i2s_obj_t;
@@ -113,7 +127,6 @@ typedef struct {
static i2s_obj_t *p_i2s[SOC_I2S_NUM] = {
[0 ... SOC_I2S_NUM - 1] = NULL,
};
static portMUX_TYPE i2s_platform_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
static portMUX_TYPE i2s_spinlock[SOC_I2S_NUM] = {
[0 ... SOC_I2S_NUM - 1] = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED,
};
@@ -443,7 +456,7 @@ esp_err_t i2s_stop(i2s_port_t i2s_num)
-------------------------------------------------------------*/
static inline uint32_t i2s_get_buf_size(i2s_port_t i2s_num)
{
i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
/* Calculate bytes per sample, align to 16 bit */
uint32_t bytes_per_sample = ((slot_cfg->data_bit_width + 15) / 16) * 2;
/* The DMA buffer limitation is 4092 bytes */
@@ -623,20 +636,20 @@ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint3
return I2S_LL_BASE_CLK;
#else
if (use_apll) {
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source");
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_160M_PLL as default clock source");
}
return I2S_LL_BASE_CLK;
#endif
}
#if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC
static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_clock_info_t *clk_info)
static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_hal_clock_info_t *clk_info)
{
/* For ADC/DAC mode, the built-in ADC/DAC is driven by 'mclk' instead of 'bclk'
* 'bclk' should be fixed to the double of sample rate
* 'bclk_div' is the real coefficient that affects the slot bit */
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
uint32_t slot_bits = slot_cfg->slot_bit_width;
/* Set I2S bit clock */
clk_info->bclk = clk_cfg->sample_rate_hz * I2S_LL_AD_BCK_FACTOR;
@@ -661,9 +674,9 @@ static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_clock_info_t *clk_
#endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC
#if SOC_I2S_SUPPORTS_PDM_TX
static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_clock_info_t *clk_info)
static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_hal_clock_info_t *clk_info)
{
i2s_pdm_tx_clk_config_t *clk_cfg = (i2s_pdm_tx_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
int fp = clk_cfg->up_sample_fp;
int fs = clk_cfg->up_sample_fs;
@@ -690,9 +703,9 @@ static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_clock_info_t *clk_i
#endif // SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_SUPPORTS_PDM_RX
static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_clock_info_t *clk_info)
static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_hal_clock_info_t *clk_info)
{
i2s_pdm_rx_clk_config_t *clk_cfg = (i2s_pdm_rx_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
i2s_pdm_dsr_t dsr = clk_cfg->dn_sample_mode;
/* Set I2S bit clock */
clk_info->bclk = clk_cfg->sample_rate_hz * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1);
@@ -715,10 +728,10 @@ static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_clock_info_t *clk_i
return ESP_OK;
}
#endif // SOC_I2S_SUPPORTS_PDM_RX
static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_clock_info_t *clk_info)
static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_hal_clock_info_t *clk_info)
{
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
uint32_t rate = clk_cfg->sample_rate_hz;
uint32_t slot_num = p_i2s[i2s_num]->total_slot < 2 ? 2 : p_i2s[i2s_num]->total_slot;
uint32_t slot_bits = slot_cfg->slot_bit_width;
@@ -745,7 +758,7 @@ static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_clock_info_t *clk_i
}
static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_clock_info_t *clk_info)
static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_info_t *clk_info)
{
/* Calculate clock for ADC/DAC mode */
#if SOC_I2S_SUPPORTS_ADC_DAC
@@ -782,14 +795,6 @@ static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_clock_info_t *clk_i
-------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_TDM
static uint32_t i2s_get_max_channel_num(uint32_t chan_mask)
{
int max_chan;
for (max_chan = 0; chan_mask; max_chan++, chan_mask >>= 1);
/* Can't be smaller than 2 */
return max_chan < 2 ? 2 : max_chan;
}
static uint32_t i2s_get_active_channel_num(uint32_t chan_mask)
{
uint32_t num = 0;
@@ -806,7 +811,7 @@ static uint32_t i2s_get_active_channel_num(uint32_t chan_mask)
static void i2s_dac_set_slot_legacy(void)
{
i2s_dev_t *dev = p_i2s[0]->hal.dev;
i2s_slot_config_t *slot_cfg = p_i2s[0]->slot_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[0]->slot_cfg;
i2s_ll_tx_reset(dev);
i2s_ll_tx_set_slave_mod(dev, false);
@@ -845,7 +850,7 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode)
static void i2s_adc_set_slot_legacy(void)
{
i2s_dev_t *dev = p_i2s[0]->hal.dev;
i2s_slot_config_t *slot_cfg = p_i2s[0]->slot_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[0]->slot_cfg;
// When ADC/DAC are installed as duplex mode, ADC will share the WS and BCLK clock by working in slave mode
i2s_ll_rx_set_slave_mod(dev, false);
i2s_ll_rx_set_sample_bit(dev, slot_cfg->slot_bit_width, slot_cfg->data_bit_width);
@@ -958,22 +963,22 @@ static void i2s_set_slot_legacy(i2s_port_t i2s_num)
}
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) {
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
i2s_hal_std_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, p_i2s[i2s_num]->slot_cfg );
i2s_hal_std_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_hal_std_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg );
i2s_hal_std_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
}
#if SOC_I2S_SUPPORTS_PDM
else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
#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, p_i2s[i2s_num]->slot_cfg );
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
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_hal_pdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg );
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
}
@@ -981,10 +986,10 @@ static void i2s_set_slot_legacy(i2s_port_t i2s_num)
#if SOC_I2S_SUPPORTS_TDM
else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
i2s_hal_tdm_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, p_i2s[i2s_num]->slot_cfg );
i2s_hal_tdm_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
i2s_hal_tdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg );
i2s_hal_tdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, (i2s_hal_slot_config_t *)(&p_i2s[i2s_num]->slot_cfg) );
}
}
#endif
@@ -1002,8 +1007,8 @@ static void i2s_set_slot_legacy(i2s_port_t i2s_num)
static void i2s_set_clock_legacy(i2s_port_t i2s_num)
{
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_clock_info_t clk_info;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
i2s_hal_clock_info_t clk_info;
i2s_calculate_clock(i2s_num, &clk_info);
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
i2s_hal_set_tx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src);
@@ -1016,7 +1021,7 @@ 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");
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
return (float)clk_cfg->sample_rate_hz;
}
@@ -1036,8 +1041,8 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
/* Stop I2S */
i2s_stop(i2s_num);
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
clk_cfg->sample_rate_hz = rate;
slot_cfg->data_bit_width = bits_cfg & 0xFFFF;
@@ -1053,9 +1058,9 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
if (slot_mask == 0) {
slot_mask = (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? 1 : 2;
}
ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->total_slot >= i2s_get_max_channel_num(slot_mask), ESP_ERR_INVALID_ARG, TAG,
ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->total_slot >= (32 - __builtin_clz(slot_mask)), ESP_ERR_INVALID_ARG, TAG,
"The max channel number can't be greater than CH%d\n", p_i2s[i2s_num]->total_slot);
p_i2s[i2s_num]->active_slot = i2s_get_active_channel_num(slot_mask);
p_i2s[i2s_num]->active_slot = __builtin_popcount(slot_mask);
} else
#endif
{
@@ -1102,7 +1107,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");
i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
uint32_t mask = 0;
#if SOC_I2S_SUPPORTS_TDM
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
@@ -1154,13 +1159,11 @@ esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsampl
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode");
xSemaphoreTake(p_i2s[i2s_num]->rx->mux, portMAX_DELAY);
i2s_stop(i2s_num);
i2s_pdm_rx_slot_config_t *slot_cfg = (i2s_pdm_rx_slot_config_t*)p_i2s[i2s_num]->slot_cfg;
i2s_pdm_rx_clk_config_t *clk_cfg = (i2s_pdm_rx_clk_config_t*)p_i2s[i2s_num]->clk_cfg;
clk_cfg->dn_sample_mode = downsample;
p_i2s[i2s_num]->clk_cfg.dn_sample_mode = downsample;
i2s_ll_rx_set_pdm_dsr(p_i2s[i2s_num]->hal.dev, downsample);
i2s_start(i2s_num);
xSemaphoreGive(p_i2s[i2s_num]->rx->mux);
return i2s_set_clk(i2s_num, clk_cfg->sample_rate_hz, slot_cfg->data_bit_width, slot_cfg->slot_mode);
return i2s_set_clk(i2s_num, p_i2s[i2s_num]->clk_cfg.sample_rate_hz, p_i2s[i2s_num]->slot_cfg.data_bit_width, p_i2s[i2s_num]->slot_cfg.slot_mode);
}
#endif
@@ -1172,14 +1175,12 @@ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample
ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode");
xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY);
i2s_stop(i2s_num);
i2s_pdm_tx_clk_config_t *clk_cfg = (i2s_pdm_tx_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
i2s_pdm_tx_slot_config_t *slot_cfg = (i2s_pdm_tx_slot_config_t *)p_i2s[i2s_num]->slot_cfg;
clk_cfg->up_sample_fp = upsample_cfg->fp;
clk_cfg->up_sample_fs = upsample_cfg->fs;
p_i2s[i2s_num]->clk_cfg.up_sample_fp = upsample_cfg->fp;
p_i2s[i2s_num]->clk_cfg.up_sample_fs = upsample_cfg->fs;
i2s_ll_tx_set_pdm_fpfs(p_i2s[i2s_num]->hal.dev, upsample_cfg->fp, upsample_cfg->fs);
i2s_start(i2s_num);
xSemaphoreGive(p_i2s[i2s_num]->tx->mux);
return i2s_set_clk(i2s_num, clk_cfg->sample_rate_hz, slot_cfg->data_bit_width, slot_cfg->slot_mode);
return i2s_set_clk(i2s_num, p_i2s[i2s_num]->clk_cfg.sample_rate_hz, p_i2s[i2s_num]->slot_cfg.data_bit_width, p_i2s[i2s_num]->slot_cfg.slot_mode);
}
#endif
@@ -1237,33 +1238,31 @@ 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
/* Convert legacy configuration into general part of slot and clock configuration */
i2s_slot_config_t slot_cfg = {};
slot_cfg.mode = p_i2s[i2s_num]->mode;
slot_cfg.data_bit_width = i2s_config->bits_per_sample;
slot_cfg.slot_bit_width = (int)i2s_config->bits_per_chan < (int)i2s_config->bits_per_sample ?
p_i2s[i2s_num]->slot_cfg.mode = p_i2s[i2s_num]->mode;
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 ?
I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO;
i2s_clk_config_t clk_cfg = {};
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_D2CLK;
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_160M_PLL;
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_D2CLK;
#if SOC_I2S_SUPPORTS_APLL
CLK_CFG().clk_src = i2s_config->use_apll ? I2S_CLK_APLL : I2S_CLK_160M_PLL;
p_i2s[i2s_num]->use_apll = i2s_config->use_apll;
#endif // SOC_I2S_SUPPORTS_APLL
#endif // SOC_I2S_SUPPORTS_APLL
/* Convert legacy configuration into particular part of slot and clock configuration */
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) {
/* Generate STD slot configuration */
i2s_std_slot_config_t *std_slot = (i2s_std_slot_config_t *)calloc(1, sizeof(i2s_std_slot_config_t));
ESP_RETURN_ON_FALSE(std_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct");
memcpy(std_slot, &slot_cfg, sizeof(i2s_slot_config_t));
std_slot->ws_width = i2s_config->bits_per_sample;
std_slot->ws_pol = false;
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;
} else if (i2s_config->channel_format == I2S_CHANNEL_FMT_ALL_LEFT ||
@@ -1273,59 +1272,45 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
std_slot->slot_sel = I2S_STD_SLOT_ONLY_RIGHT;
}
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
std_slot->bit_shift = true;
SLOT_CFG(std).bit_shift = true;
}
if (i2s_config->communication_format & I2S_COMM_FORMAT_STAND_PCM_SHORT) {
std_slot->bit_shift = true;
std_slot->ws_width = 1;
std_slot->ws_pol = true;
SLOT_CFG(std).bit_shift = true;
SLOT_CFG(std).ws_width = 1;
SLOT_CFG(std).ws_pol = true;
}
#if SOC_I2S_HW_VERSION_1
std_slot->msb_right = false;
#elif SOC_I2S_HW_VERSION_2
std_slot->left_align = i2s_config->left_align;
std_slot->big_endian = i2s_config->big_edin;
std_slot->bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect
#endif // SOC_I2S_HW_VERSION_1
p_i2s[i2s_num]->slot_cfg = std_slot;
#if SOC_I2S_HW_VERSION_1
SLOT_CFG(std).msb_right = false;
#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
/* Generate STD clock configuration */
i2s_std_clk_config_t *std_clk = (i2s_std_clk_config_t *)calloc(1, sizeof(i2s_std_clk_config_t));
ESP_RETURN_ON_FALSE(std_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct");
memcpy(std_clk, &clk_cfg, sizeof(i2s_clk_config_t));
p_i2s[i2s_num]->clk_cfg = std_clk;
p_i2s[i2s_num]->active_slot = (int)std_slot->slot_mode;
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;
goto finish;
}
#if SOC_I2S_SUPPORTS_PDM_TX
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
/* Generate PDM TX slot configuration */
i2s_pdm_tx_slot_config_t *pdm_tx_slot = (i2s_pdm_tx_slot_config_t *)calloc(1, sizeof(i2s_pdm_tx_slot_config_t));
ESP_RETURN_ON_FALSE(pdm_tx_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct");
memcpy(pdm_tx_slot, &slot_cfg, sizeof(i2s_slot_config_t));
pdm_tx_slot->sd_prescale = 0;
pdm_tx_slot->sd_scale = I2S_PDM_SIG_SCALING_MUL_1;
pdm_tx_slot->hp_scale = I2S_PDM_SIG_SCALING_MUL_1;
pdm_tx_slot->lp_scale = I2S_PDM_SIG_SCALING_MUL_1;
pdm_tx_slot->sinc_scale = I2S_PDM_SIG_SCALING_MUL_1;
SLOT_CFG(pdm_tx).sd_prescale = 0;
SLOT_CFG(pdm_tx).sd_scale = I2S_PDM_SIG_SCALING_MUL_1;
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
pdm_tx_slot->sd_en = true;
pdm_tx_slot->hp_en = true;
pdm_tx_slot->hp_cut_off_freq_hz = 49;
pdm_tx_slot->sd_dither = 0;
pdm_tx_slot->sd_dither2 = 0;
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
p_i2s[i2s_num]->slot_cfg = pdm_tx_slot;
/* Generate PDM TX clock configuration */
i2s_pdm_tx_clk_config_t *pdm_tx_clk = (i2s_pdm_tx_clk_config_t *)calloc(1, sizeof(i2s_pdm_tx_clk_config_t));
ESP_RETURN_ON_FALSE(pdm_tx_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct");
memcpy(pdm_tx_clk, &clk_cfg, sizeof(i2s_clk_config_t));
pdm_tx_clk->up_sample_fp = 960;
pdm_tx_clk->up_sample_fs = i2s_config->sample_rate / 100;
p_i2s[i2s_num]->clk_cfg = pdm_tx_clk;
p_i2s[i2s_num]->active_slot = (int)pdm_tx_slot->slot_mode;
CLK_CFG().up_sample_fp = 960;
CLK_CFG().up_sample_fs = i2s_config->sample_rate / 100;
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;
goto finish;
}
@@ -1333,20 +1318,9 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
#if SOC_I2S_SUPPORTS_PDM_RX
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
/* Generate PDM RX slot configuration */
i2s_pdm_rx_slot_config_t *pdm_rx_slot = (i2s_pdm_rx_slot_config_t *)calloc(1, sizeof(i2s_pdm_rx_slot_config_t));
ESP_RETURN_ON_FALSE(pdm_rx_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct");
memcpy(pdm_rx_slot, &slot_cfg, sizeof(i2s_slot_config_t));
p_i2s[i2s_num]->slot_cfg = pdm_rx_slot;
/* Generate PDM RX clock configuration */
i2s_pdm_rx_clk_config_t *pdm_rx_clk = (i2s_pdm_rx_clk_config_t *)calloc(1, sizeof(i2s_pdm_rx_clk_config_t));
ESP_RETURN_ON_FALSE(pdm_rx_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct");
memcpy(pdm_rx_clk, &clk_cfg, sizeof(i2s_clk_config_t));
pdm_rx_clk->dn_sample_mode = I2S_PDM_DSR_8S;
p_i2s[i2s_num]->clk_cfg = pdm_rx_clk;
p_i2s[i2s_num]->active_slot = (int)pdm_rx_slot->slot_mode;
CLK_CFG().dn_sample_mode = I2S_PDM_DSR_8S;
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;
goto finish;
}
@@ -1355,55 +1329,42 @@ static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s
#if SOC_I2S_SUPPORTS_TDM
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
/* Generate TDM slot configuration */
i2s_tdm_slot_config_t *tdm_slot = (i2s_tdm_slot_config_t *)calloc(1, sizeof(i2s_tdm_slot_config_t));
ESP_RETURN_ON_FALSE(tdm_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct");
memcpy(tdm_slot, &slot_cfg, sizeof(i2s_slot_config_t));
tdm_slot->slot_mask = i2s_config->chan_mask >> 16;
uint32_t mx_slot = i2s_get_max_channel_num(tdm_slot->slot_mask);
tdm_slot->total_slot = mx_slot < i2s_config->total_chan ? mx_slot : i2s_config->total_chan;
tdm_slot->ws_width = I2S_TDM_AUTO_WS_WIDTH;
tdm_slot->ws_pol = false;
SLOT_CFG(tdm).slot_mask = i2s_config->chan_mask >> 16;
SLOT_CFG(tdm).ws_width = I2S_TDM_AUTO_WS_WIDTH;
tdm_slot->slot_mode = I2S_SLOT_MODE_STEREO;
SLOT_CFG(tdm).ws_pol = false;
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
tdm_slot->bit_shift = true;
SLOT_CFG(tdm).bit_shift = true;
}
else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_SHORT) {
tdm_slot->bit_shift = true;
tdm_slot->ws_width = 1;
tdm_slot->ws_pol = true;
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) {
tdm_slot->bit_shift = true;
tdm_slot->ws_width = tdm_slot->slot_bit_width;
tdm_slot->ws_pol = true;
SLOT_CFG(tdm).bit_shift = true;
SLOT_CFG(tdm).ws_width = SLOT_CFG(tdm).slot_bit_width;
SLOT_CFG(tdm).ws_pol = true;
}
tdm_slot->left_align = i2s_config->left_align;
tdm_slot->big_endian = i2s_config->big_edin;
tdm_slot->bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect
tdm_slot->skip_mask = i2s_config->skip_msk;
p_i2s[i2s_num]->slot_cfg = tdm_slot;
SLOT_CFG(tdm).left_align = i2s_config->left_align;
SLOT_CFG(tdm).big_endian = i2s_config->big_edin;
SLOT_CFG(tdm).bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect
SLOT_CFG(tdm).skip_mask = i2s_config->skip_msk;
/* Generate TDM clock configuration */
i2s_tdm_clk_config_t *tdm_clk = (i2s_tdm_clk_config_t *)calloc(1, sizeof(i2s_tdm_clk_config_t));
ESP_RETURN_ON_FALSE(tdm_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct");
memcpy(tdm_clk, &clk_cfg, sizeof(i2s_clk_config_t));
p_i2s[i2s_num]->clk_cfg = tdm_clk;
p_i2s[i2s_num]->active_slot = i2s_get_active_channel_num(tdm_slot->slot_mask);
p_i2s[i2s_num]->total_slot = tdm_slot->total_slot;
p_i2s[i2s_num]->active_slot = __builtin_popcount(tdm_slot->slot_mode);
uint32_t mx_slot = 32 - __builtin_clz(SLOT_CFG(tdm).slot_mask);
mx_slot = mx_slot < 2 ? 2 : mx_slot;
p_i2s[i2s_num]->.total_slot = mx_slot < i2s_config->total_chan ? mx_slot : i2s_config->total_chan;
goto finish;
}
#endif // SOC_I2S_SUPPORTS_TDM
#if SOC_I2S_SUPPORTS_ADC_DAC
if ((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) {
i2s_slot_config_t *adc_dac_slot = (i2s_slot_config_t *)calloc(1, sizeof(i2s_slot_config_t));
ESP_RETURN_ON_FALSE(adc_dac_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct");
memcpy(adc_dac_slot, &slot_cfg, sizeof(i2s_slot_config_t));
p_i2s[i2s_num]->slot_cfg = adc_dac_slot;
i2s_clk_config_t *adc_dac_clk = (i2s_clk_config_t *)calloc(1, sizeof(i2s_clk_config_t));
ESP_RETURN_ON_FALSE(adc_dac_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct");
memcpy(adc_dac_clk, &clk_cfg, sizeof(i2s_clk_config_t));
p_i2s[i2s_num]->clk_cfg = adc_dac_clk;
p_i2s[i2s_num]->slot_cfg.slot_mode = (p_i2s[i2s_num]->dir & I2S_DIR_TX) ?
I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO;
p_i2s[i2s_num]->active_slot = (p_i2s[i2s_num]->dir & I2S_DIR_TX) ? 2 : 1;
p_i2s[i2s_num]->total_slot = 2;
}
@@ -1554,10 +1515,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_D2CLK);
i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_160M_PLL);
}
if (obj->dir & I2S_DIR_RX) {
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_D2CLK);
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_160M_PLL);
}
periph_rtc_apll_release();
}
@@ -1578,15 +1539,7 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
}
#endif
/* Disable module clock */
i2s_priv_deregister_object(i2s_num);
if (obj->clk_cfg) {
free(obj->clk_cfg);
obj->clk_cfg = NULL;
}
if (obj->slot_cfg) {
free(obj->slot_cfg);
obj->slot_cfg = NULL;
}
i2s_platform_release_occupation(i2s_num);
free(obj);
p_i2s[i2s_num] = NULL;
return ESP_OK;
@@ -1603,7 +1556,7 @@ 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");
if (i2s_priv_register_object(i2s_obj, i2s_num) != ESP_OK) {
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;
@@ -1913,31 +1866,3 @@ 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_priv_register_object(void *driver_obj, int port_id)
{
esp_err_t ret = ESP_ERR_NOT_FOUND;
ESP_RETURN_ON_FALSE(driver_obj && (port_id < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
portENTER_CRITICAL(&i2s_platform_spinlock);
if (!p_i2s[port_id]) {
ret = ESP_OK;
p_i2s[port_id] = driver_obj;
periph_module_enable(i2s_periph_signal[port_id].module);
}
portEXIT_CRITICAL(&i2s_platform_spinlock);
return ret;
}
esp_err_t i2s_priv_deregister_object(int port_id)
{
esp_err_t ret = ESP_ERR_INVALID_STATE;
ESP_RETURN_ON_FALSE(port_id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
portENTER_CRITICAL(&i2s_platform_spinlock);
if (p_i2s[port_id]) {
ret = ESP_OK;
p_i2s[port_id] = NULL;
periph_module_disable(i2s_periph_signal[port_id].module);
}
portEXIT_CRITICAL(&i2s_platform_spinlock);
return ret;
}

View File

@@ -0,0 +1,533 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_pdm.h"
#include "i2s_private.h"
#include "clk_ctrl_os.h"
#include "esp_intr_alloc.h"
#include "esp_check.h"
static const char *TAG = "I2S_PDM";
/*---------------------------------------------------------------
PDM TX
---------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_PDM_TX
static esp_err_t i2s_pdm_tx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
{
uint32_t rate = clk_cfg->sample_rate_hz;
i2s_pdm_tx_clk_config_t *pdm_tx_clk = (i2s_pdm_tx_clk_config_t *)clk_cfg;
clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * pdm_tx_clk->up_sample_fp / pdm_tx_clk->up_sample_fs;
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_160M_PLL ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
/* 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);
return ESP_OK;
}
static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg)
{
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
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when initializing or clock source changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && (handle->state == I2S_CHAN_STATE_INIT || pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_160M_PLL)) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to D2CLK */
if (clk_cfg->clk_src == I2S_CLK_160M_PLL && pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
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);
/* Set clock configurations in HAL*/
i2s_hal_set_tx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&s_i2s.spinlock);
return ret;
}
static esp_err_t i2s_pdm_tx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg)
{
uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num);
/* The DMA buffer need to re-allocate if the buffer size changed */
if (handle->dma.buf_size != buf_size) {
handle->dma.buf_size = buf_size;
ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor");
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, handle->dma.desc_num, buf_size),
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);
portENTER_CRITICAL(&s_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);
/* 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;
return ESP_OK;
}
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;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->clk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->clk)),
ESP_ERR_INVALID_ARG, TAG, "clk gpio is invalid");
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);
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);
/* 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);
}
} else {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false);
}
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->hal.dev);
#endif
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)
{
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_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
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_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 */
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_slot(handle, &pdm_tx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
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");
/* Store the configurations of standard mode */
i2s_pdm_tx_config_t *mode_info = calloc(1, sizeof(i2s_pdm_tx_config_t));
ESP_GOTO_ON_FALSE(mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
memcpy(mode_info, pdm_tx_cfg, sizeof(i2s_pdm_tx_config_t));
handle->mode_info = mode_info;
i2s_ll_tx_enable_pdm(handle->parent->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);
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a TX handle");
esp_err_t ret = ESP_OK;
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");
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_clock(handle, &pdm_tx_cfg->clk_cfg), err, TAG, "update clock failed");
/* Update the stored clock information */
memcpy(&pdm_tx_cfg->clk_cfg, clk_cfg, sizeof(i2s_pdm_tx_clk_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a TX handle");
esp_err_t ret = ESP_OK;
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");
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");
/* Update the stored slot information */
memcpy(&pdm_tx_cfg->slot_cfg, slot_cfg, sizeof(i2s_pdm_tx_slot_config_t));
/* If the slot bit width changed, then need to update the clock */
uint32_t slot_bits = slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
if (pdm_tx_cfg->slot_cfg.slot_bit_width == slot_bits) {
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_clock(handle, &pdm_tx_cfg->clk_cfg), err, TAG, "update clock failed");
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a TX handle");
esp_err_t ret = ESP_OK;
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");
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_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
/* Update the stored slot information */
memcpy(&pdm_tx_cfg->gpio_cfg, gpio_cfg, sizeof(i2s_pdm_tx_gpio_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
#endif
/*---------------------------------------------------------------
PDM RX
---------------------------------------------------------------*/
#if SOC_I2S_SUPPORTS_PDM_RX
static esp_err_t i2s_pdm_rx_calculate_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg, i2s_hal_clock_info_t *clk_info)
{
uint32_t rate = clk_cfg->sample_rate_hz;
i2s_pdm_rx_clk_config_t *pdm_rx_clk = (i2s_pdm_rx_clk_config_t *)clk_cfg;
clk_info->bclk = rate * I2S_LL_PDM_BCK_FACTOR * (pdm_rx_clk->dn_sample_mode == I2S_PDM_DSR_16S ? 2 : 1);
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_160M_PLL ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
/* 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);
return ESP_OK;
}
static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg)
{
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
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when initializing or clock source changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && (handle->state == I2S_CHAN_STATE_INIT || pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_160M_PLL)) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to D2CLK */
if (clk_cfg->clk_src == I2S_CLK_160M_PLL && pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
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);
/* Set clock configurations in HAL*/
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
portEXIT_CRITICAL(&s_i2s.spinlock);
return ret;
}
static esp_err_t i2s_pdm_rx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg)
{
uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num);
/* The DMA buffer need to re-allocate if the buffer size changed */
if (handle->dma.buf_size != buf_size) {
handle->dma.buf_size = buf_size;
ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor");
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, handle->dma.desc_num, buf_size),
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);
portENTER_CRITICAL(&s_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);
/* 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;
return ESP_OK;
}
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;
/* Check validity of selected pins */
ESP_RETURN_ON_FALSE((gpio_cfg->clk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->clk)),
ESP_ERR_INVALID_ARG, TAG, "clk gpio is invalid");
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);
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);
} else {
if (!handle->parent->full_duplex) {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_rx_ws_sig, false);
} else {
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false);
}
}
#if SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_rx_clk(handle->parent->hal.dev);
#endif
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)
{
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_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
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_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 */
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_slot(handle, &pdm_rx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
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");
/* Store the configurations of standard mode */
i2s_pdm_rx_config_t *mode_info = calloc(1, sizeof(i2s_pdm_rx_config_t));
ESP_GOTO_ON_FALSE(mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
memcpy(mode_info, pdm_rx_cfg, sizeof(i2s_pdm_rx_config_t));
handle->mode_info = mode_info;
i2s_ll_rx_enable_pdm(handle->parent->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);
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_RX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a RX handle");
esp_err_t ret = ESP_OK;
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");
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_clock(handle, &pdm_rx_cfg->clk_cfg), err, TAG, "update clock failed");
/* Update the stored clock information */
memcpy(&pdm_rx_cfg->clk_cfg, clk_cfg, sizeof(i2s_pdm_rx_clk_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_RX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a RX handle");
esp_err_t ret = ESP_OK;
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");
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");
/* Update the stored slot information */
memcpy(&pdm_rx_cfg->slot_cfg, slot_cfg, sizeof(i2s_pdm_rx_slot_config_t));
/* If the slot bit width changed, then need to update the clock */
uint32_t slot_bits = slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
if (pdm_rx_cfg->slot_cfg.slot_bit_width == slot_bits) {
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_clock(handle, &pdm_rx_cfg->clk_cfg), err, TAG, "update clock failed");
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
/* Update the stored slot information */
memcpy(&pdm_rx_cfg->gpio_cfg, gpio_cfg, sizeof(i2s_pdm_rx_gpio_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
#endif // SOC_I2S_SUPPORTS_PDM_RX

View File

@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "soc/lldesc.h"
#include "soc/soc_caps.h"
#include "hal/i2s_types.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#if SOC_GDMA_SUPPORTED
#include "esp_private/gdma.h"
#endif
#include "esp_pm.h"
#include "esp_err.h"
#ifdef __cplusplus
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__)
/**
* @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_INIT, /*!< i2s channel is initializing */
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_state_t;
/**
* @brief i2s channel level configurations
* @note It performs as channel handle
*/
typedef struct {
#if SOC_GDMA_SUPPORTED
gdma_channel_handle_t dma_chan; /*!< gdma channel handler */
#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 */
uint32_t buf_size; /*!< dma buffer size */
bool auto_clear; /*!< Set to auto clear DMA TX descriptor, i2s will always send zero automatically if no data to send */
uint32_t rw_pos; /*!< reading/writing pointer position */
void *curr_ptr; /*!< Pointer to current dma buffer */
lldesc_t **desc; /*!< dma descriptor array */
uint8_t **bufs; /*!< dma buffer array */
} i2s_dma_t;
/**
* @brief i2s controller level configurations
* @note Both i2s rx and tx channel are under its control
*/
typedef struct {
i2s_port_t id; /*!< i2s port id */
i2s_hal_context_t hal; /*!< hal context */
uint32_t chan_occupancy; /*!< channel occupancy (rx/tx) */
bool full_duplex; /*!< is full_duplex */
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_comm_mode_t mode; /*!< i2s channel communication mode */
i2s_role_t role; /*!< i2s role */
i2s_dir_t dir; /*!< i2s channel direction */
i2s_dma_t dma; /*!< i2s dma object */
i2s_state_t state; /*!< i2s driver state. Ensuring the driver working in a correct sequence */
/* Stored configurations */
void *mode_info; /*!< Slot, clock and gpio information of each mode */
#if SOC_I2S_SUPPORTS_APLL
bool apll_en; /*!< Flag of wether APLL enabled */
#endif
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 */
esp_pm_lock_handle_t pm_lock; /*!< Power management lock, to avoid apb clock frequency changes while i2s is working */
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 */
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
*/
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_platform_t;
extern i2s_platform_t s_i2s;
esp_err_t i2s_init_dma_intr(i2s_chan_handle_t handle, int intr_flag);
esp_err_t i2s_free_dma_desc(i2s_chan_handle_t handle);
esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bufsize);
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);
#endif
void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input);
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,315 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_std.h"
#include "i2s_private.h"
#include "clk_ctrl_os.h"
#include "esp_intr_alloc.h"
#include "esp_check.h"
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)
{
uint32_t rate = clk_cfg->sample_rate_hz;
i2s_std_slot_config_t *slot_cfg = &((i2s_std_config_t *)(handle->mode_info))->slot_cfg;
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
/* Calculate multiple
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
if (handle->role == I2S_ROLE_MASTER) {
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = rate * clk_cfg->mclk_multiple;
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
} else {
/* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
clk_info->bclk_div = 8;
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_160M_PLL ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
return ESP_OK;
}
static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg)
{
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
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when initializing or clock source changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && (handle->state == I2S_CHAN_STATE_INIT || std_cfg->clk_cfg.clk_src == I2S_CLK_160M_PLL)) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to D2CLK */
if (clk_cfg->clk_src == I2S_CLK_160M_PLL && std_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
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);
/* 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);
} else {
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
return ret;
}
static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
{
uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num);
/* The DMA buffer need to re-allocate if the buffer size changed */
if (handle->dma.buf_size != buf_size) {
handle->dma.buf_size = buf_size;
ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor");
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, handle->dma.desc_num, buf_size),
TAG, "allocate memory for dma descriptor failed");
}
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);
/* 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);
}
portENTER_CRITICAL(&s_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);
} else {
i2s_hal_std_set_rx_slot(&(handle->parent->hal), is_slave, (i2s_hal_slot_config_t*)slot_cfg);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
/* 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;
return ESP_OK;
}
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;
/* 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);
}
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 SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->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);
/* 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);
}
} 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);
#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);
/* 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);
}
}
return ESP_OK;
}
esp_err_t i2s_init_std_channel(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
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 */
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, &std_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");
/* Store the configurations of standard mode */
i2s_std_config_t *mode_info = calloc(1, sizeof(i2s_std_config_t));
ESP_GOTO_ON_FALSE(mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
memcpy(mode_info, std_cfg, sizeof(i2s_std_config_t));
handle->mode_info = mode_info;
#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);
} else {
i2s_ll_rx_enable_std(handle->parent->hal.dev);
i2s_ll_rx_enable_clock(handle->parent->hal.dev);
}
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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_clock(handle, &std_cfg->clk_cfg), err, TAG, "update clock failed");
/* Update the stored clock information */
memcpy(&std_cfg->clk_cfg, clk_cfg, sizeof(i2s_std_clk_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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");
/* Update the stored slot information */
memcpy(&std_cfg->slot_cfg, slot_cfg, sizeof(i2s_std_slot_config_t));
/* If the slot bit width changed, then need to update the clock */
uint32_t slot_bits = slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
if (std_cfg->slot_cfg.slot_bit_width == slot_bits) {
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, &std_cfg->clk_cfg), err, TAG, "update clock failed");
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
/* Update the stored slot information */
memcpy(&std_cfg->gpio_cfg, gpio_cfg, sizeof(i2s_std_gpio_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}

View File

@@ -0,0 +1,317 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "hal/i2s_hal.h"
#include "driver/gpio.h"
#include "driver/i2s_tdm.h"
#include "i2s_private.h"
#include "clk_ctrl_os.h"
#include "esp_intr_alloc.h"
#include "esp_check.h"
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)
{
uint32_t rate = clk_cfg->sample_rate_hz;
i2s_tdm_slot_config_t *slot_cfg = &((i2s_tdm_config_t *)(handle->mode_info))->slot_cfg;
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
/* Calculate multiple
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
if (handle->role == I2S_ROLE_MASTER) {
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = rate * clk_cfg->mclk_multiple;
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
} else {
/* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
clk_info->bclk_div = 8;
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_SUPPORTS_APLL
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_160M_PLL ? I2S_LL_BASE_CLK : i2s_set_get_apll_freq(clk_info->mclk);
#else
clk_info->sclk = I2S_LL_BASE_CLK;
#endif
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
return ESP_OK;
}
static esp_err_t i2s_tdm_set_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg)
{
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
#if SOC_I2S_SUPPORTS_APLL
/* Enable APLL and acquire its lock when initializing or clock source changed to APLL */
if (clk_cfg->clk_src == I2S_CLK_APLL && (handle->state == I2S_CHAN_STATE_INIT || tdm_cfg->clk_cfg.clk_src == I2S_CLK_160M_PLL)) {
periph_rtc_apll_acquire();
handle->apll_en = true;
}
/* Disable APLL and release its lock when clock source is changed to D2CLK */
if (clk_cfg->clk_src == I2S_CLK_160M_PLL && tdm_cfg->clk_cfg.clk_src == I2S_CLK_APLL) {
periph_rtc_apll_release();
handle->apll_en = false;
}
#endif
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);
/* 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);
} else {
i2s_hal_set_rx_clock(&handle->parent->hal, &clk_info, clk_cfg->clk_src);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
return ret;
}
static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg)
{
uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num);
/* The DMA buffer need to re-allocate if the buffer size changed */
if (handle->dma.buf_size != buf_size) {
handle->dma.buf_size = buf_size;
ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor");
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, handle->dma.desc_num, buf_size),
TAG, "allocate memory for dma descriptor failed");
}
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);
/* 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);
}
portENTER_CRITICAL(&s_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, slot_cfg);
} else {
i2s_hal_tdm_set_rx_slot(&(handle->parent->hal), is_slave, slot_cfg);
}
portEXIT_CRITICAL(&s_i2s.spinlock);
/* Update the total slot num and active slot num */
handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : __builtin_popcount(slot_cfg->slot_mask);
uint32_t max_slot_num = 32 - __builtin_clz(slot_cfg->slot_mask);
handle->total_slot = slot_cfg->total_slot < max_slot_num ? max_slot_num : slot_cfg->total_slot;
handle->total_slot = handle->total_slot < 2 ? 2 : handle->total_slot; // At least two slots in a frame
return ESP_OK;
}
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;
/* 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);
}
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 SOC_I2S_HW_VERSION_2
i2s_ll_mclk_bind_to_tx_clk(handle->parent->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);
/* 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);
}
} 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);
#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);
/* 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);
}
}
return ESP_OK;
}
esp_err_t i2s_init_tdm_channel(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
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_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 */
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
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");
/* Store the configurations of standard mode */
i2s_tdm_config_t *mode_info = calloc(1, sizeof(i2s_tdm_config_t));
ESP_GOTO_ON_FALSE(mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
memcpy(mode_info, tdm_cfg, sizeof(i2s_tdm_config_t));
handle->mode_info = mode_info;
#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);
} else {
i2s_ll_rx_enable_tdm(handle->parent->hal.dev);
i2s_ll_rx_enable_clock(handle->parent->hal.dev);
}
#endif
/* Initialization finished, mark state as ready */
handle->state = I2S_CHAN_STATE_READY;
xSemaphoreGive(handle->mutex);
return ret;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "update clock failed");
/* Update the stored clock information */
memcpy(&tdm_cfg->clk_cfg, clk_cfg, sizeof(i2s_tdm_clk_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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");
/* Update the stored slot information */
memcpy(&tdm_cfg->slot_cfg, slot_cfg, sizeof(i2s_tdm_slot_config_t));
/* If the slot bit width changed, then need to update the clock */
uint32_t slot_bits = slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
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");
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t i2s_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);
esp_err_t ret = ESP_OK;
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");
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_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
/* Update the stored slot information */
memcpy(&tdm_cfg->gpio_cfg, gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
xSemaphoreGive(handle->mutex);
return ESP_OK;
err:
xSemaphoreGive(handle->mutex);
return ret;
}

View File

@@ -0,0 +1,266 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "hal/i2s_types.h"
#include "esp_types.h"
#include "esp_err.h"
#ifdef __cplusplus
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
*/
#define I2S_CHANNEL_CONFIG(i2s_role, i2s_mode, pin_config) { \
.id = I2S_NUM_AUTO, \
.role = i2s_role, \
.mode = i2s_mode, \
.io = pin_config, \
}
#define I2S_GPIO_UNUSED GPIO_NUM_NC /*!< Used in i2s_gpio_config_t for signals which are not used */
/**
* @brief I2S controller channel configuration
*/
typedef struct {
i2s_port_t id; /*!< I2S port id */
i2s_role_t role; /*!< I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE */
/* 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 */
} 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.
*
* @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)
* @return
* - ESP_OK Allocate a new channel 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);
/**
* @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 Resource will be free automatically if all channels in one port are deleted
*
* @param[in] handle I2S channel handler
* - ESP_OK Delete successfully
* - ESP_ERR_INVALID_ARG NULL pointer
*/
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.
*
* @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.
* @return
* - ESP_OK Initialize successfully
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE This channel is not registered
*/
esp_err_t i2s_init_channel(i2s_chan_handle_t handle, const void *clk_config, const void *slot_config);
/**
* @brief Reconfigure the I2S clock
* @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.
*
* @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);
/**
* @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
*
* @param[in] handle I2S channel handler
* @return
* - ESP_OK Stop successfully
* - 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);
/**
* @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.
*
* @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
* @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);
/**
* @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.
*
* @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
* @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);
/**
* @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
*
* @param[in] handle I2S channel handler
* @return
* - ESP_OK Clear successfully
* - ESP_ERR_INVALID_STATE I2S is not initialized
*/
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);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,227 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is specified for I2S PDM communication mode
* Features:
* - Only support PDM tx/rx mode
* - Fixed to 2 slots
* - Data bit width only support 16 bits
*/
#pragma once
#include "hal/i2s_types.h"
#include "hal/gpio_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_I2S_SUPPORTS_PDM_RX
/**
* @brief PDM format in 2 slots(RX)
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @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, \
}
/**
* @brief i2s default pdm rx clock configuration
* @param rate sample rate
*/
#define I2S_PDM_RX_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_160M_PLL, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.dn_sample_mode = I2S_PDM_DSR_8S \
}
/**
* @breif 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
*/
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;
/**
* @brief I2S PDM tx mode GPIO pins configuration
*/
typedef struct {
gpio_num_t clk; /*!< PDM clk pin, output */
gpio_num_t din; /*!< DATA pin, input */
} 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;
} i2s_pdm_rx_config_t;
#endif // SOC_I2S_SUPPORTS_PDM_RX
#if SOC_I2S_SUPPORTS_PDM_TX
#if SOC_I2S_HW_VERSION_2
/**
* @brief PDM style in 2 slots(TX)
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @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, \
.sd_prescale = 0, \
.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.sd_en = true, \
.hp_en = true, \
.hp_cut_off_freq_hz = 49, \
.sd_dither = 0, \
.sd_dither2 = 0, \
}
#else
/**
* @brief PDM style in 2 slots(TX)
* @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode
* @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, \
.hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \
.sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \
}
#endif // SOC_I2S_HW_VERSION_2
/**
* @brief i2s default pdm tx clock configuration
* @note TX PDM can only be set to the following two up-sampling rate configurations:
* 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.
* @param rate sample rate
*/
#define I2S_PDM_TX_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_160M_PLL, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
.up_sample_fp = 960, \
.up_sample_fs = ((rate) / 100), \
}
/*
High Pass Filter Cut-off Frequency Sheet
+----------------+------------------+----------------+------------------+----------------+------------------+
| param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) |
+----------------+------------------+----------------+------------------+----------------+------------------+
| (0, 0) | 185 | (3, 3) | 115 | (5, 5) | 69 |
| (0, 1) | 172 | (1, 7) | 106 | (4, 7) | 63 |
| (1, 1) | 160 | (2, 4) | 104 | (5, 6) | 58 |
| (1, 2) | 150 | (4, 4) | 92 | (5, 7) | 49 |
| (2, 2) | 137 | (2, 7) | 91.5 | (6, 6) | 46 |
| (2, 3) | 126 | (4, 5) | 81 | (6, 7) | 35.5 |
| (0, 3) | 120 | (3, 7) | 77.2 | (7, 7) | 23.3 |
+----------------+------------------+----------------+------------------+----------------+------------------+
*/
/**
* @brief I2S slot configuration for pdm tx 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 */
/* Particular fields */
uint32_t sd_prescale; /*!< Sigma-delta filter prescale */
i2s_pdm_sig_scale_t sd_scale; /*!< Sigma-delta filter scaling value */
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
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
} i2s_pdm_tx_slot_config_t;
/**
* @breif 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 */
} i2s_pdm_tx_clk_config_t;
/**
* @brief I2S PDM tx mode GPIO pins configuration
*/
typedef struct {
gpio_num_t clk; /*!< PDM clk pin, output */
gpio_num_t dout; /*!< DATA pin, output */
} 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;
} i2s_pdm_tx_config_t;
#endif // SOC_I2S_SUPPORTS_PDM_TX
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,207 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is specified for I2S standard communication mode
* Features:
* - Philip/MSB/PCM are supported in standard mode
* - Fixed to 2 slots
*/
#pragma once
#include "hal/i2s_types.h"
#include "hal/gpio_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
/**
* @brief Philip format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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, \
}
/**
* @brief PCM(short) format in 2 slots
* @note PCM(long) is sample as philip in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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, \
}
/**
* @brief MSB format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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, \
}
#else
/**
* @brief Philip format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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 \
}
/**
* @brief PCM(short) format in 2 slots
* @note PCM(long) is sample as philip in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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 \
}
/**
* @brief MSB format in 2 slots
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.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 \
}
#endif
/**
* @brief i2s default standard clock configuration
* @note Please set the mclk_multiple to I2S_MCLK_MULTIPLE_384 while using 24 bits data width
* Otherwise the sample rate might be imprecise since the bclk division is not a integer
* @param rate sample rate
*/
#define I2S_STD_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_160M_PLL, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
}
/**
* @breif I2S slot configuration for standard mode
*/
typedef struct {
/* General fields */
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) */
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
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
} i2s_std_slot_config_t;
/**
* @breif I2S clock configuration for standard 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 */
} i2s_std_clk_config_t;
/**
* @brief I2S standard mode GPIO pins configuration
*/
typedef struct {
gpio_num_t mclk; /*!< MCK pin, output */
gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */
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 */
} i2s_std_gpio_config_t;
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_config_t;
esp_err_t i2s_init_std_channel(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);
esp_err_t i2s_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);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,190 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is specified for I2S TDM communication mode
* Features:
* - More than 2 slots
*/
#pragma once
#include "hal/i2s_types.h"
#include "hal/gpio_types.h"
#if SOC_I2S_SUPPORTS_TDM
#ifdef __cplusplus
extern "C" {
#endif
#define I2S_TDM_AUTO_SLOT_NUM (0) // Auto means total slot number will be equal to the maximum active slot number
#define I2S_TDM_AUTO_WS_WIDTH (0) // Auto means ws signal width will be equal to the half width of a frame
/**
* @brief Philip format in active slot that enabled by mask
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
* @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, \
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
.ws_pol = false, \
.bit_shift = true, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
/**
* @brief MSB format in active slot enabled that by mask
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
* @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, \
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
.ws_pol = false, \
.bit_shift = false, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false ,\
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
/**
* @brief PCM(short) format in active slot that enabled by mask
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
* @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, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
/**
* @brief PCM(long) format in active slot that enabled by mask
* @param bits_per_sample i2s data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
* @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, \
.ws_width = (bits_per_sample), \
.ws_pol = true, \
.bit_shift = true, \
.left_align = false, \
.big_endian = false, \
.bit_order_lsb = false, \
.skip_mask = false, \
.slot_mask = (mask), \
.total_slot = I2S_TDM_AUTO_SLOT_NUM \
}
/**
* @brief i2s default tdm clock configuration
* @note Please set the mclk_multiple to I2S_MCLK_MULTIPLE_384 while the data width in slot configuration is set to 24 bits
* Otherwise the sample rate might be imprecise since the bclk division is not a integer
* @param rate sample rate
*/
#define I2S_TDM_CLK_DEFAULT_CONFIG(rate) { \
.sample_rate_hz = rate, \
.clk_src = I2S_CLK_160M_PLL, \
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
}
/**
* @breif 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)) */
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 */
bool left_align; /*!< Set true to enable left alignment */
bool big_endian; /*!< Set true to enable big endian */
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
*/
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 */
} i2s_tdm_clk_config_t;
/**
* @brief I2S TDM mode GPIO pins configuration
*/
typedef struct {
gpio_num_t mclk; /*!< MCK pin, output */
gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */
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 */
} i2s_tdm_gpio_config_t;
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_config_t;
esp_err_t i2s_init_tdm_channel(i2s_chan_handle_t handle, const i2s_tdm_config_t *std_cfg);
esp_err_t i2s_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);
esp_err_t i2s_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg);
#ifdef __cplusplus
}
#endif
#endif // SOC_I2S_SUPPORTS_TDM

View File

@@ -10,38 +10,39 @@
#pragma once
#include "esp_err.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Register an I2S or I2S variant driver object to platform
* @brief Hold the I2S port occupation
*
* @note This private API is used to avoid applications from using the same I2S instance for different purpose.
* @note This function will help enable the peripheral APB clock as well.
*
* @param driver_obj Driver object
* @param port_id I2S port number
* @param id I2S port number
* @param comp_name The name of compnant that occupied this i2s controller
* @return
* - ESP_OK: The specific I2S port is free and register the new device object successfully
* - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id
* - ESP_ERR_NOT_FOUND: Specific I2S port is not available
* - ESP_ERR_NOT_FOUND Specific I2S port is not available
*/
esp_err_t i2s_priv_register_object(void *driver_obj, int port_id);
esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name);
/**
* @brief Deregister I2S or I2S variant driver object from platform
* @brief Release the I2S port occupation
*
* @note This function will help disable the peripheral APB clock as well.
*
* @param port_id I2S port number
* @param id I2S port number
* @return
* - ESP_OK: Deregister I2S port successfully (i.e. that I2S port can used used by other users after this function returns)
* - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id
* - ESP_ERR_INVALID_STATE: Specific I2S port is free already
*/
esp_err_t i2s_priv_deregister_object(int port_id);
esp_err_t i2s_platform_release_occupation(int id);
#ifdef __cplusplus
}

View File

@@ -1,159 +0,0 @@
/*
* 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 "test_utils.h"
#include "esp_rom_sys.h"
#include "driver/dac.h"
/*
* ADC DMA testcase
*/
#include "driver/i2s.h"
#include "test/test_common_adc.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
/**
* @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

@@ -1,161 +0,0 @@
/*
* 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_utils.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

@@ -219,35 +219,44 @@ TEST_CASE("adc2 work with wifi","[adc]")
#ifdef CONFIG_IDF_TARGET_ESP32
#include "driver/i2s.h"
#include "driver/adc.h"
#define ADC1_CHANNEL_4_IO (32)
#define SAMPLE_RATE (36000)
#define SAMPLE_BITS (16)
#define ADC_SAMPLE_RATE (36000)
#define ADC_TEST_CHANNEL ADC1_CHANNEL_4
static void i2s_adc_init(void)
static void adc_dma_init(void)
{
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,
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = 1024,
.conv_num_each_intr = 256,
.adc1_chan_mask = 1 << ADC_TEST_CHANNEL,
.adc2_chan_mask = 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);
TEST_ESP_OK(adc_digi_initialize(&adc_dma_config));
adc_digi_pattern_config_t adc_pattern = {
.atten = ADC_ATTEN_DB_0,
.channel = ADC_TEST_CHANNEL,
.unit = 0,
.bit_width = SOC_ADC_DIGI_MAX_BITWIDTH
};
adc_digi_configuration_t dig_cfg = {
.conv_limit_en = 1,
.conv_limit_num = 250,
.sample_freq_hz = ADC_SAMPLE_RATE,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE1,
.pattern_num = 1,
.adc_pattern = &adc_pattern
};
TEST_ESP_OK(adc_digi_controller_configure(&dig_cfg));
TEST_ESP_OK(adc_digi_start());
}
static void i2s_adc_test(void)
static void continuous_adc_test(void)
{
uint16_t *i2sReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
uint16_t *adcReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
size_t bytesRead;
for (int loop = 0; loop < 10; loop++) {
for (int level = 0; level <= 1; level++) {
@@ -258,12 +267,11 @@ static void i2s_adc_test(void)
}
vTaskDelay(200 / portTICK_PERIOD_MS);
// read data from adc, will block until buffer is full
i2s_read(I2S_NUM_0, (void *)i2sReadBuffer, 1024 * sizeof(uint16_t), &bytesRead, portMAX_DELAY);
adc_digi_read_bytes((uint8_t *)adcReadBuffer, 1024 * sizeof(uint16_t), &bytesRead, ADC_MAX_DELAY);
// calc average
int64_t adcSumValue = 0;
for (size_t i = 0; i < 1024; i++) {
adcSumValue += i2sReadBuffer[i] & 0xfff;
adcSumValue += adcReadBuffer[i] & 0xfff;
}
int adcAvgValue = adcSumValue / 1024;
printf("adc average val: %d\n", adcAvgValue);
@@ -275,19 +283,19 @@ static void i2s_adc_test(void)
}
}
}
free(i2sReadBuffer);
free(adcReadBuffer);
}
static void i2s_adc_release(void)
static void adc_deinit(void)
{
i2s_adc_disable(I2S_NUM_0);
i2s_driver_uninstall(I2S_NUM_0);
adc_digi_stop();
TEST_ESP_OK(adc_digi_deinitialize());
}
TEST_CASE("adc1 and i2s work with wifi","[adc][ignore]")
{
i2s_adc_init();
adc_dma_init();
//init wifi
printf("nvs init\n");
esp_err_t r = nvs_flash_init();
@@ -310,12 +318,12 @@ TEST_CASE("adc1 and i2s work with wifi","[adc][ignore]")
};
TEST_ESP_OK(esp_wifi_set_mode(WIFI_MODE_STA));
TEST_ESP_OK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
i2s_adc_test();
continuous_adc_test();
//now start wifi
printf("wifi start...\n");
TEST_ESP_OK(esp_wifi_start());
//test reading during wifi on
i2s_adc_test();
continuous_adc_test();
//wifi stop again
printf("wifi stop...\n");
@@ -326,8 +334,8 @@ TEST_CASE("adc1 and i2s work with wifi","[adc][ignore]")
event_deinit();
nvs_flash_deinit();
i2s_adc_test();
i2s_adc_release();
continuous_adc_test();
adc_deinit();
printf("test passed...\n");
TEST_IGNORE_MESSAGE("this test case is ignored due to the critical memory leak of esp_netif and event_loop.");
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -19,7 +19,6 @@
#include "soc/soc_caps.h"
#if SOC_DAC_SUPPORTED
#include "driver/i2s.h"
#include "driver/dac.h"
#include "esp_adc_cal.h"

View File

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

View File

@@ -0,0 +1,30 @@
# 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

@@ -0,0 +1,16 @@
set(srcs "test_app_main.c")
if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "test_i2s_controller.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_controller"
"-u test_app_include_i2s_legacy")
endif()

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 (-300)
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(" |___|_____|____/ |_|\\___||___/\\__|\r\n");
unity_run_menu();
}

View File

@@ -0,0 +1,512 @@
/*
* 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_controller.h"
#include "hal/i2s_hal.h"
#include "esp_private/i2s_platform.h"
#if SOC_PCNT_SUPPORTED
#include "driver/pcnt.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 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
#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_controller(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_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_pin);
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_init_channel(tx_handle, &clk_cfg, &slot_cfg));
slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_set_slot(tx_handle, &slot_cfg));
clk_cfg.sample_rate = 44100;
TEST_ESP_OK(i2s_set_clock(tx_handle, &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_channel(tx_handle, &clk_cfg, &slot_cfg));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_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 applied 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_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_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_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_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_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_pin);
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
chan_cfg.id = I2S_NUM_0;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_init_channel(tx_handle, &clk_cfg, &slot_cfg));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_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_gpio_config_t mst_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_gpio_config_t slv_pin = I2S_TEST_SLAVE_DEFAULT_PIN;
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &mst_pin);
mst_chan_cfg.id = I2S_NUM_0;
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_SLAVE, I2S_COMM_MODE_STD, &slv_pin);
slv_chan_cfg.id = I2S_NUM_1;
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
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_channel(tx_handle, &clk_cfg, &slot_cfg));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_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_gpio_config_t mst_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_gpio_config_t slv_pin = I2S_TEST_SLAVE_DEFAULT_PIN;
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &mst_pin);
mst_chan_cfg.id = I2S_NUM_0;
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_SLAVE, I2S_COMM_MODE_STD, &slv_pin);
slv_chan_cfg.id = I2S_NUM_1;
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
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_channel(tx_handle, &clk_cfg, &slot_cfg));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_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_clk_config_t* clk_cfg)
{
TEST_ASSERT_NOT_NULL(rx_chan);
TEST_ASSERT_NOT_NULL(clk_cfg);
/* Prepare configuration for the PCNT unit */
pcnt_config_t pcnt_cfg = {
// Set PCNT input signal and control GPIOs
.pulse_gpio_num = MASTER_WS_IO,
.ctrl_gpio_num = -1,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC, // Count up on the positive edge
.neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge
.lctrl_mode = PCNT_MODE_KEEP,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = (int16_t)0x7fff,
.counter_l_lim = (int16_t)0x8000,
};
TEST_ESP_OK(pcnt_unit_config(&pcnt_cfg));
// 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);
// pcnt_set_filter_value(PCNT_UNIT_0, 10);
pcnt_filter_disable(PCNT_UNIT_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};
int16_t real_pulse = 0;
for (int i = 0; i < 15; i++) {
int16_t expt_pulse = (int16_t)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
clk_cfg->sample_rate = test_freq[i];
TEST_ESP_OK(i2s_set_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_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &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_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_pin);
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
chan_cfg.id = I2S_NUM_0;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_cfg));
i2s_test_common_sample_rate(rx_handle, (i2s_clk_config_t *)&clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_SUPPORTS_APLL
TEST_CASE("I2S APLL clock test", "[i2s]")
{
i2s_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_pin);
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
chan_cfg.id = I2S_NUM_0;
clk_cfg.clk_src = I2S_CLK_APLL;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_cfg));
i2s_test_common_sample_rate(rx_handle, (i2s_clk_config_t *)&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_gpio_config_t i2s_pin = I2S_TEST_MASTER_DEFAULT_PIN;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_CONFIG(I2S_ROLE_MASTER, I2S_COMM_MODE_STD, &i2s_pin);
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO);
slot_cfg.dma_desc_num = 3;
slot_cfg.dma_frame_num = 511;
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_CONFIG(SAMPLE_RATE);
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_init_channel(rx_handle, &clk_cfg, &slot_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]);
clk_cfg.sample_rate = test_freq[i];
TEST_ESP_OK(i2s_set_clock(rx_handle, &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

@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
* SPDX-License-Identifier: Apache-2.0
*/
/**
@@ -11,19 +11,17 @@
* 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 "driver/i2s.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "unity.h"
#include "math.h"
#include "esp_rom_gpio.h"
#if SOC_I2S_SUPPORTED
#include "driver/i2s.h"
#define SAMPLE_RATE (36000)
#define SAMPLE_BITS (16)
@@ -78,7 +76,12 @@
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loopback
// 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)
{
@@ -136,7 +139,7 @@ static void i2s_test_io_config(int mode)
* 1. i2s_driver_install
* 2. i2s_set_pin
*/
TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]")
TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s_legacy]")
{
// dac, adc i2s
i2s_config_t i2s_config = {
@@ -183,7 +186,7 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]")
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_driver_uninstall(I2S_NUM_0));
}
TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]")
TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@@ -256,7 +259,7 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]")
}
#if SOC_I2S_SUPPORTS_TDM
TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s]")
TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@@ -325,7 +328,7 @@ TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s]")
#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]")
TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s_legacy]")
{
// master driver installed and send data
i2s_config_t master_i2s_config = {
@@ -429,7 +432,7 @@ TEST_CASE("I2S write and read test(master tx and slave rx)", "[i2s]")
i2s_driver_uninstall(I2S_NUM_1);
}
TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]")
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 = {
@@ -535,7 +538,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]")
}
#endif
TEST_CASE("I2S memory leaking test", "[i2s]")
TEST_CASE("I2S memory leaking test", "[i2s_legacy]")
{
i2s_config_t master_i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX,
@@ -585,7 +588,7 @@ TEST_CASE("I2S memory leaking test", "[i2s]")
* 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]")
TEST_CASE("I2S APLL clock variation test", "[i2s_legacy]")
{
i2s_pin_config_t pin_config = {
.mck_io_num = -1,
@@ -643,7 +646,7 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]")
#if SOC_I2S_SUPPORTS_ADC
/* Only ESP32 need I2S adc/dac test */
TEST_CASE("I2S adc test", "[i2s]")
TEST_CASE("I2S adc test", "[i2s_legacy]")
{
// init I2S ADC
i2s_config_t i2s_config = {
@@ -673,7 +676,7 @@ TEST_CASE("I2S adc test", "[i2s]")
} else {
gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLUP_ONLY);
}
vTaskDelay(200 / portTICK_PERIOD_MS);
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);
@@ -710,7 +713,7 @@ TEST_CASE("I2S adc test", "[i2s]")
#endif
#if SOC_I2S_SUPPORTS_DAC
TEST_CASE("I2S dac test", "[i2s]")
TEST_CASE("I2S dac test", "[i2s_legacy]")
{
// dac, adc i2s
i2s_config_t i2s_config = {
@@ -733,5 +736,3 @@ TEST_CASE("I2S dac test", "[i2s]")
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
}
#endif
#endif //SOC_I2S_SUPPORTED

View File

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