forked from espressif/esp-idf
Merge branch 'refactor/i2s_driver_rewrite' into 'master'
🔨 i2s: i2s driver-NG Closes IDF-3714 and IDF-4592 See merge request espressif/esp-idf!15175
This commit is contained in:
@@ -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"
|
||||
"deprecated/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)
|
||||
|
@@ -305,4 +305,27 @@ 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 legacy I2S driver.
|
||||
|
||||
config I2S_ENABLE_DEBUG_LOG
|
||||
bool "Enable I2S debug log"
|
||||
default n
|
||||
help
|
||||
Wether to enable the debug log message for I2S driver.
|
||||
Note that, this option only controls the I2S driver log, will not affect other drivers.
|
||||
endmenu # I2S Configuration
|
||||
|
||||
endmenu # Driver configurations
|
||||
|
@@ -37,8 +37,8 @@
|
||||
#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 "driver/i2s_types.h"
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "esp_private/i2s_platform.h"
|
||||
#endif
|
||||
@@ -259,7 +259,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 +541,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;
|
||||
|
@@ -4,147 +4,36 @@
|
||||
* 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_std.h", "driver/i2s_pdm.h" and ""driver/i2s_tdm.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 "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
|
||||
*
|
252
components/driver/deprecated/driver/i2s_types_legacy.h
Normal file
252
components/driver/deprecated/driver/i2s_types_legacy.h
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-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"
|
||||
#include "driver/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 event queue types
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_EVENT_DMA_ERROR, /*!< I2S DMA has no next descriptor for sending or receiving */
|
||||
I2S_EVENT_TX_DONE, /*!< I2S DMA finished sending one DMA buffer */
|
||||
I2S_EVENT_RX_DONE, /*!< I2S DMA finished receiving one DMA buffer */
|
||||
I2S_EVENT_TX_Q_OVF, /*!< I2S DMA sending queue overflowed, the oldest data has been overwritten by the new data in the DMA buffer */
|
||||
I2S_EVENT_RX_Q_OVF, /*!< I2S DMA receive queue overflowed, the oldest data has been overwritten by the new data in the DMA buffer */
|
||||
} i2s_event_type_t;
|
||||
|
||||
/**
|
||||
* @brief Event structure used in I2S event queue
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_event_type_t type; /*!< I2S event type */
|
||||
size_t size; /*!< I2S data size for I2S_DATA event*/
|
||||
} i2s_event_t;
|
||||
|
||||
/**
|
||||
* @brief I2S GPIO pins configuration
|
||||
*/
|
||||
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
|
@@ -12,18 +12,23 @@
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
|
||||
#include "soc/lldesc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "driver/i2s_types_legacy.h"
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "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
|
||||
@@ -39,12 +44,9 @@
|
||||
#include "esp_pm.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "esp_private/i2s_platform.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "I2S";
|
||||
static const char *TAG = "i2s(legacy)";
|
||||
|
||||
#define I2S_ENTER_CRITICAL_ISR(i2s_num) portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num])
|
||||
#define I2S_EXIT_CRITICAL_ISR(i2s_num) portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num])
|
||||
@@ -57,6 +59,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,20 +123,30 @@ 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;
|
||||
|
||||
// Record the component name that using I2S peripheral
|
||||
static const char *comp_using_i2s[SOC_I2S_NUM] = {[0 ... SOC_I2S_NUM - 1] = NULL};
|
||||
|
||||
// Global I2S object pointer
|
||||
static i2s_obj_t *p_i2s[SOC_I2S_NUM] = {
|
||||
[0 ... SOC_I2S_NUM - 1] = NULL,
|
||||
};
|
||||
static portMUX_TYPE i2s_platform_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// Global spin lock for all i2s controllers
|
||||
static portMUX_TYPE i2s_spinlock[SOC_I2S_NUM] = {
|
||||
[0 ... SOC_I2S_NUM - 1] = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED,
|
||||
};
|
||||
|
||||
|
||||
__attribute__((weak)) esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name);
|
||||
|
||||
__attribute__((weak)) esp_err_t i2s_platform_release_occupation(int id);
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
I2S DMA operation
|
||||
-------------------------------------------------------------*/
|
||||
@@ -399,7 +428,7 @@ static void i2s_rx_stop(i2s_port_t i2s_num)
|
||||
|
||||
esp_err_t i2s_start(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
//start DMA link
|
||||
I2S_ENTER_CRITICAL(i2s_num);
|
||||
|
||||
@@ -420,7 +449,7 @@ esp_err_t i2s_start(i2s_port_t i2s_num)
|
||||
|
||||
esp_err_t i2s_stop(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
I2S_ENTER_CRITICAL(i2s_num);
|
||||
#if !SOC_GDMA_SUPPORTED
|
||||
esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle);
|
||||
@@ -443,13 +472,13 @@ 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 */
|
||||
uint32_t bytes_per_frame = bytes_per_sample * p_i2s[i2s_num]->active_slot;
|
||||
p_i2s[i2s_num]->dma_frame_num = (p_i2s[i2s_num]->dma_frame_num * bytes_per_frame > I2S_DMA_BUFFER_MAX_SIZE) ?
|
||||
I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame : p_i2s[i2s_num]->dma_frame_num;
|
||||
I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame : p_i2s[i2s_num]->dma_frame_num;
|
||||
return p_i2s[i2s_num]->dma_frame_num * bytes_per_frame;
|
||||
}
|
||||
|
||||
@@ -483,7 +512,6 @@ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj)
|
||||
ESP_GOTO_ON_FALSE(dma_obj->buf[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma buffer");
|
||||
/* Initialize DMA buffer to 0 */
|
||||
memset(dma_obj->buf[cnt], 0, dma_obj->buf_size);
|
||||
ESP_LOGD(TAG, "Addr[%d] = %d", cnt, (int)dma_obj->buf[cnt]);
|
||||
|
||||
/* Allocate DMA descpriptor */
|
||||
dma_obj->desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), MALLOC_CAP_DMA);
|
||||
@@ -505,7 +533,7 @@ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj)
|
||||
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
|
||||
i2s_ll_rx_set_eof_num(p_i2s[i2s_num]->hal.dev, dma_obj->buf_size);
|
||||
}
|
||||
ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_desc_num=%d", dma_obj->buf_size, buf_cnt);
|
||||
ESP_LOGD(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_desc_num=%d", dma_obj->buf_size, buf_cnt);
|
||||
return ESP_OK;
|
||||
err:
|
||||
/* Delete DMA buffer if failed to allocate memory */
|
||||
@@ -551,7 +579,7 @@ static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma)
|
||||
/* Free DMA structure */
|
||||
free(*dma);
|
||||
*dma = NULL;
|
||||
ESP_LOGI(TAG, "DMA queue destroyed");
|
||||
ESP_LOGD(TAG, "DMA queue destroyed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -616,27 +644,27 @@ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint3
|
||||
if (ret == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq);
|
||||
}
|
||||
ESP_LOGI(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
|
||||
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
|
||||
/* In APLL mode, there is no sclk but only mclk, so return 0 here to indicate APLL mode */
|
||||
return real_freq;
|
||||
}
|
||||
return I2S_LL_BASE_CLK;
|
||||
#else
|
||||
if (use_apll) {
|
||||
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source");
|
||||
ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_SRC_DEFAULT as default clock source");
|
||||
}
|
||||
return I2S_LL_BASE_CLK;
|
||||
#endif
|
||||
}
|
||||
|
||||
#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;
|
||||
@@ -644,7 +672,7 @@ static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_clock_info_t *clk_
|
||||
clk_info->bclk_div = slot_bits;
|
||||
/* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = bclk * bclk_div */
|
||||
clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ?
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
/* Calculate bclk_div = mclk / bclk */
|
||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
||||
/* Get I2S system clock by config source clock */
|
||||
@@ -661,9 +689,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;
|
||||
@@ -673,7 +701,7 @@ static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_clock_info_t *clk_i
|
||||
clk_info->bclk_div = 8;
|
||||
/* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate_hz * multiple */
|
||||
clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ?
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
/* Calculate bclk_div = mclk / bclk */
|
||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
||||
/* Get I2S system clock by config source clock */
|
||||
@@ -690,9 +718,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);
|
||||
@@ -700,7 +728,7 @@ static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_clock_info_t *clk_i
|
||||
clk_info->bclk_div = 8;
|
||||
/* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate_hz * multiple */
|
||||
clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ?
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div;
|
||||
/* Calculate bclk_div = mclk / bclk */
|
||||
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
|
||||
/* Get I2S system clock by config source clock */
|
||||
@@ -715,10 +743,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 +773,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
|
||||
@@ -780,33 +808,11 @@ static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_clock_info_t *clk_i
|
||||
/*-------------------------------------------------------------
|
||||
I2S configuration
|
||||
-------------------------------------------------------------*/
|
||||
#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;
|
||||
for (int i = 0; chan_mask; i++, chan_mask >>= 1) {
|
||||
if (chan_mask & 0x01) {
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_ADC_DAC
|
||||
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 +851,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);
|
||||
@@ -878,7 +884,7 @@ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel)
|
||||
|
||||
esp_err_t i2s_adc_enable(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet");
|
||||
ESP_RETURN_ON_FALSE(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX),
|
||||
ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled");
|
||||
@@ -891,7 +897,7 @@ esp_err_t i2s_adc_enable(i2s_port_t i2s_num)
|
||||
|
||||
esp_err_t i2s_adc_disable(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet");
|
||||
ESP_RETURN_ON_FALSE(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX),
|
||||
ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled");
|
||||
@@ -906,7 +912,7 @@ static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, const i2s_config_t *
|
||||
{
|
||||
/* Step 1: Check the validity of input parameters */
|
||||
/* Check the validity of i2s device number */
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE(p_i2s[i2s_num] == NULL, ESP_ERR_INVALID_STATE, TAG, "this i2s port is in use");
|
||||
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL");
|
||||
/* Check the size of DMA buffer */
|
||||
@@ -958,33 +964,33 @@ 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 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
|
||||
#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
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#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 +1008,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);
|
||||
@@ -1015,14 +1021,14 @@ static void i2s_set_clock_legacy(i2s_port_t i2s_num)
|
||||
|
||||
float i2s_get_clk(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg;
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
i2s_clk_config_t *clk_cfg = &p_i2s[i2s_num]->clk_cfg;
|
||||
return (float)clk_cfg->sample_rate_hz;
|
||||
}
|
||||
|
||||
esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S%d has not installed yet", i2s_num);
|
||||
|
||||
/* Acquire the lock before stop i2s, otherwise reading/writing operation will stuck on receiving the message queue from interrupt */
|
||||
@@ -1036,8 +1042,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;
|
||||
@@ -1047,15 +1053,28 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_
|
||||
ESP_RETURN_ON_FALSE((slot_cfg->slot_bit_width % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per channel");
|
||||
ESP_RETURN_ON_FALSE(((int)slot_cfg->slot_bit_width <= (int)I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample");
|
||||
slot_cfg->slot_mode = ((ch & 0xFFFF) == I2S_CHANNEL_MONO) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
|
||||
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) {
|
||||
if (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) {
|
||||
if (slot_cfg->std.slot_mask == I2S_STD_SLOT_LEFT_RIGHT) {
|
||||
slot_cfg->std.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
// Enable right first to get correct data sequence
|
||||
slot_cfg->std.ws_pol = !slot_cfg->std.ws_pol;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
slot_cfg->std.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
|
||||
}
|
||||
}
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
|
||||
uint32_t slot_mask = ch >> 16;
|
||||
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
|
||||
{
|
||||
@@ -1101,12 +1120,12 @@ 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;
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
i2s_hal_slot_config_t *slot_cfg = &p_i2s[i2s_num]->slot_cfg;
|
||||
uint32_t mask = 0;
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) {
|
||||
mask = ((i2s_tdm_slot_config_t *)slot_cfg)->slot_mask;;
|
||||
mask = slot_cfg->tdm.slot_mask;
|
||||
}
|
||||
#endif
|
||||
return i2s_set_clk(i2s_num, rate, slot_cfg->data_bit_width, slot_cfg->slot_mode | (mask << 16));
|
||||
@@ -1154,13 +1173,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 +1189,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
|
||||
|
||||
@@ -1223,7 +1238,7 @@ static void i2s_mode_identify(i2s_port_t i2s_num, const i2s_config_t *i2s_config
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
if (i2s_config->channel_format == I2S_CHANNEL_FMT_MULTIPLE) {
|
||||
p_i2s[i2s_num]->mode = I2S_COMM_MODE_TDM;
|
||||
p_i2s[i2s_num]->mode = I2S_COMM_MODE_TDM;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
||||
|
||||
@@ -1237,95 +1252,78 @@ 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 ?
|
||||
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;
|
||||
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;
|
||||
|
||||
p_i2s[i2s_num]->slot_cfg.slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ?
|
||||
I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO;
|
||||
CLK_CFG().sample_rate_hz = i2s_config->sample_rate;
|
||||
CLK_CFG().mclk_multiple = i2s_config->mclk_multiple == 0 ? I2S_MCLK_MULTIPLE_256 : i2s_config->mclk_multiple;
|
||||
CLK_CFG().clk_src = I2S_CLK_SRC_DEFAULT;
|
||||
p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk;
|
||||
p_i2s[i2s_num]->use_apll = false;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
clk_cfg.clk_src = i2s_config->use_apll ? I2S_CLK_APLL : I2S_CLK_D2CLK;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
CLK_CFG().clk_src = i2s_config->use_apll ? I2S_CLK_SRC_APLL : I2S_CLK_SRC_DEFAULT;
|
||||
p_i2s[i2s_num]->use_apll = i2s_config->use_apll;
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
#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;
|
||||
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
|
||||
} else if (i2s_config->channel_format == I2S_CHANNEL_FMT_ALL_LEFT ||
|
||||
i2s_config->channel_format == I2S_CHANNEL_FMT_ONLY_LEFT) {
|
||||
std_slot->slot_sel = I2S_STD_SLOT_ONLY_LEFT;
|
||||
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_LEFT;
|
||||
} else {
|
||||
std_slot->slot_sel = I2S_STD_SLOT_ONLY_RIGHT;
|
||||
SLOT_CFG(std).slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
|
||||
}
|
||||
if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) {
|
||||
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 = true;
|
||||
#elif SOC_I2S_HW_VERSION_2
|
||||
SLOT_CFG(std).left_align = i2s_config->left_align;
|
||||
SLOT_CFG(std).big_endian = i2s_config->big_edin;
|
||||
SLOT_CFG(std).bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect
|
||||
#endif // SOC_I2S_HW_VERSION_1
|
||||
|
||||
/* 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;
|
||||
#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;
|
||||
#endif // SOC_I2S_HW_VERSION_2
|
||||
p_i2s[i2s_num]->slot_cfg = pdm_tx_slot;
|
||||
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
|
||||
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
|
||||
|
||||
/* 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 +1331,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,60 +1342,48 @@ 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 = 0; // I2S_TDM_AUTO_WS_WIDTH
|
||||
p_i2s[i2s_num]->slot_cfg.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) {
|
||||
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) {
|
||||
SLOT_CFG(tdm).bit_shift = true;
|
||||
SLOT_CFG(tdm).ws_width = p_i2s[i2s_num]->slot_cfg.slot_bit_width;
|
||||
SLOT_CFG(tdm).ws_pol = true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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(SLOT_CFG(tdm).slot_mask);
|
||||
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;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_ADC_DAC
|
||||
|
||||
#undef SLOT_CFG
|
||||
#undef CLK_CFG
|
||||
|
||||
finish:
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -1443,16 +1418,16 @@ static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag)
|
||||
}
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) {
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
if (p_i2s[i2s_num]->dir & I2S_DIR_TX) {
|
||||
i2s_hal_pdm_enable_tx_channel(&(p_i2s[i2s_num]->hal));
|
||||
}
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
if (p_i2s[i2s_num]->dir & I2S_DIR_RX) {
|
||||
i2s_hal_pdm_enable_rx_channel(&(p_i2s[i2s_num]->hal));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
@@ -1509,7 +1484,7 @@ static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag)
|
||||
|
||||
esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE(i2s_num < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num);
|
||||
i2s_obj_t *obj = p_i2s[i2s_num];
|
||||
i2s_stop(i2s_num);
|
||||
@@ -1554,10 +1529,10 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
|
||||
if (obj->use_apll) {
|
||||
// switch back to PLL clock source
|
||||
if (obj->dir & I2S_DIR_TX) {
|
||||
i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_D2CLK);
|
||||
i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_SRC_DEFAULT);
|
||||
}
|
||||
if (obj->dir & I2S_DIR_RX) {
|
||||
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_D2CLK);
|
||||
i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_SRC_DEFAULT);
|
||||
}
|
||||
periph_rtc_apll_release();
|
||||
}
|
||||
@@ -1578,15 +1553,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;
|
||||
@@ -1595,6 +1562,9 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
|
||||
|
||||
esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
/* Step 1: Check the validity of input parameters */
|
||||
@@ -1603,11 +1573,12 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config,
|
||||
/* Step 2: Allocate driver object and register to platform */
|
||||
i2s_obj_t *i2s_obj = calloc(1, sizeof(i2s_obj_t));
|
||||
ESP_RETURN_ON_FALSE(i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver");
|
||||
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;
|
||||
}
|
||||
p_i2s[i2s_num] = i2s_obj;
|
||||
i2s_hal_init(&i2s_obj->hal, i2s_num);
|
||||
|
||||
/* Step 3: Store and assign configarations */
|
||||
@@ -1625,7 +1596,7 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config,
|
||||
i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t));
|
||||
ESP_GOTO_ON_FALSE(i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed");
|
||||
*((QueueHandle_t *) i2s_queue) = i2s_obj->i2s_queue;
|
||||
ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(i2s_obj->i2s_queue));
|
||||
ESP_LOGD(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(i2s_obj->i2s_queue));
|
||||
} else {
|
||||
i2s_obj->i2s_queue = NULL;
|
||||
}
|
||||
@@ -1647,7 +1618,7 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by
|
||||
char *src_byte;
|
||||
size_t bytes_can_write;
|
||||
*bytes_written = 0;
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled");
|
||||
xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
@@ -1690,7 +1661,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz
|
||||
int aim_bytes;
|
||||
int zero_bytes;
|
||||
*bytes_written = 0;
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((size > 0), ESP_ERR_INVALID_ARG, TAG, "size must greater than zero");
|
||||
ESP_RETURN_ON_FALSE((aim_bits >= src_bits), ESP_ERR_INVALID_ARG, TAG, "aim_bits mustn't be less than src_bits");
|
||||
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled");
|
||||
@@ -1749,11 +1720,11 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz
|
||||
esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait)
|
||||
{
|
||||
char *data_ptr;;
|
||||
char*dest_byte;
|
||||
char *dest_byte;
|
||||
int bytes_can_read;
|
||||
*bytes_read = 0;
|
||||
dest_byte = (char *)dest;
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "RX mode is not enabled");
|
||||
xSemaphoreTake(p_i2s[i2s_num]->rx->mux, portMAX_DELAY);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
@@ -1832,13 +1803,13 @@ static esp_err_t i2s_check_set_mclk(i2s_port_t i2s_num, gpio_num_t gpio_num)
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid");
|
||||
gpio_matrix_out_check_and_set(gpio_num, i2s_periph_signal[i2s_num].mck_out_sig, 0, 0);
|
||||
#endif
|
||||
ESP_LOGI(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num);
|
||||
ESP_LOGD(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
uint32_t buf_cnt = p_i2s[i2s_num]->dma_desc_num;
|
||||
|
||||
/* Clear I2S RX DMA buffer */
|
||||
@@ -1866,7 +1837,7 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num)
|
||||
|
||||
esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
ESP_RETURN_ON_FALSE((i2s_num < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
|
||||
if (pin == NULL) {
|
||||
#if SOC_I2S_SUPPORTS_DAC
|
||||
return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
@@ -1914,30 +1885,47 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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]) {
|
||||
ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
|
||||
portENTER_CRITICAL(&i2s_spinlock[id]);
|
||||
if (!comp_using_i2s[id]) {
|
||||
ret = ESP_OK;
|
||||
p_i2s[port_id] = driver_obj;
|
||||
periph_module_enable(i2s_periph_signal[port_id].module);
|
||||
comp_using_i2s[id] = comp_name;
|
||||
periph_module_enable(i2s_periph_signal[id].module);
|
||||
i2s_ll_enable_clock(I2S_LL_GET_HW(id));
|
||||
}
|
||||
portEXIT_CRITICAL(&i2s_platform_spinlock);
|
||||
portEXIT_CRITICAL(&i2s_spinlock[id]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_priv_deregister_object(int port_id)
|
||||
esp_err_t i2s_platform_release_occupation(int 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]) {
|
||||
ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id");
|
||||
portENTER_CRITICAL(&i2s_spinlock[id]);
|
||||
if (comp_using_i2s[id]) {
|
||||
ret = ESP_OK;
|
||||
p_i2s[port_id] = NULL;
|
||||
periph_module_disable(i2s_periph_signal[port_id].module);
|
||||
comp_using_i2s[id] = NULL;
|
||||
/* Disable module clock */
|
||||
periph_module_disable(i2s_periph_signal[id].module);
|
||||
i2s_ll_disable_clock(I2S_LL_GET_HW(id));
|
||||
}
|
||||
portEXIT_CRITICAL(&i2s_platform_spinlock);
|
||||
portEXIT_CRITICAL(&i2s_spinlock[id]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function will be called during start up, to check that pulse_cnt driver is not running along with the legacy i2s driver
|
||||
*/
|
||||
static __attribute__((constructor)) void check_i2s_driver_conflict(void)
|
||||
{
|
||||
extern __attribute__((weak)) esp_err_t i2s_del_channel(void *handle);
|
||||
/* If the new I2S driver is linked, the weak function will point to the actual function in the new driver, otherwise it is NULL*/
|
||||
if ((void *)i2s_del_channel != NULL) {
|
||||
ESP_EARLY_LOGE(TAG, "CONFLICT! The new i2s driver can't work along with the legacy i2s driver");
|
||||
abort();
|
||||
}
|
||||
ESP_EARLY_LOGW(TAG, "legacy i2s driver is deprecated, please migrate to use driver/i2s_std.h, driver/i2s_pdm.h or driver/i2s_tdm.h");
|
||||
}
|
1142
components/driver/i2s/i2s_common.c
Normal file
1142
components/driver/i2s/i2s_common.c
Normal file
File diff suppressed because it is too large
Load Diff
571
components/driver/i2s/i2s_pdm.c
Normal file
571
components/driver/i2s/i2s_pdm.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* 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 "sdkconfig.h"
|
||||
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#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_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
|
||||
#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->controller->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);
|
||||
|
||||
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(&g_i2s.spinlock);
|
||||
/* Set clock configurations in HAL*/
|
||||
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: clock configuration */
|
||||
memcpy(&(pdm_tx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_tx_clk_config_t));
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
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->controller->hal.dev, handle->controller->full_duplex);
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply PDM format */
|
||||
bool is_slave = handle->role == I2S_ROLE_SLAVE;
|
||||
i2s_hal_pdm_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: slot configuration */
|
||||
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
|
||||
memcpy(&(pdm_tx_cfg->slot_cfg), slot_cfg, sizeof(i2s_pdm_tx_slot_config_t));
|
||||
|
||||
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->controller->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, false);
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + slave" mode, select TX signal index for ws and bck */
|
||||
if (!handle->controller->full_duplex) {
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
|
||||
}
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
/* Update the mode info: gpio configuration */
|
||||
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
|
||||
memcpy(&(pdm_tx_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_pdm_tx_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a TX handle");
|
||||
ESP_RETURN_ON_FALSE(handle->controller->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
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");
|
||||
handle->mode = I2S_COMM_MODE_PDM;
|
||||
/* Allocate memory for storing the configurations of PDM tx mode */
|
||||
if (handle->mode_info) {
|
||||
free(handle->mode_info);
|
||||
}
|
||||
handle->mode_info = calloc(1, sizeof(i2s_pdm_tx_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, &pdm_tx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
|
||||
/* i2s_set_pdm_tx_slot should be called before i2s_set_pdm_tx_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_slot(handle, &pdm_tx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is APLL */
|
||||
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
#endif
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_clock(handle, &pdm_tx_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
|
||||
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
|
||||
|
||||
i2s_ll_tx_enable_pdm(handle->controller->hal.dev);
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
|
||||
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
|
||||
#endif
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
|
||||
#endif
|
||||
|
||||
/* Initialization finished, mark state as ready */
|
||||
handle->state = I2S_CHAN_STATE_READY;
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
|
||||
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 disabled before reconfiguring the clock");
|
||||
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)handle->mode_info;
|
||||
ESP_GOTO_ON_FALSE(pdm_tx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && pdm_tx_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
|
||||
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_release();
|
||||
handle->apll_en = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
// Create/Re-create power management lock
|
||||
if (pdm_tx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
|
||||
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 disabled 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");
|
||||
|
||||
/* 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_channel_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
|
||||
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 disabled before reconfiguring the gpio");
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_tx_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
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_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
|
||||
#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->controller->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);
|
||||
|
||||
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(&g_i2s.spinlock);
|
||||
/* Set clock configurations in HAL*/
|
||||
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: clock configuration */
|
||||
memcpy(&(pdm_rx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_rx_clk_config_t));
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
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->controller->hal.dev, handle->controller->full_duplex);
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply PDM format */
|
||||
bool is_slave = (handle->role == I2S_ROLE_SLAVE) | handle->controller->full_duplex;
|
||||
i2s_hal_pdm_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: slot configuration */
|
||||
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
|
||||
memcpy(&(pdm_rx_cfg->slot_cfg), slot_cfg, sizeof(i2s_pdm_rx_slot_config_t));
|
||||
|
||||
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->controller->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, false);
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.clk_inv);
|
||||
} else {
|
||||
if (!handle->controller->full_duplex) {
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->clk, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.clk_inv);
|
||||
}
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
/* Update the mode info: gpio configuration */
|
||||
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
|
||||
memcpy(&(pdm_rx_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_pdm_rx_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_RX, ESP_ERR_INVALID_ARG, TAG, "This channel handle is not a RX handle");
|
||||
ESP_RETURN_ON_FALSE(handle->controller->id == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "This channel handle is registered on I2S1, but PDM is only supported on I2S0");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
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");
|
||||
handle->mode = I2S_COMM_MODE_PDM;
|
||||
/* Allocate memory for storing the configurations of PDM rx mode */
|
||||
if (handle->mode_info) {
|
||||
free(handle->mode_info);
|
||||
}
|
||||
handle->mode_info = calloc(1, sizeof(i2s_pdm_rx_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_gpio(handle, &pdm_rx_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
|
||||
/* i2s_set_pdm_rx_slot should be called before i2s_set_pdm_rx_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_slot(handle, &pdm_rx_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is APLL */
|
||||
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
#endif
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_clock(handle, &pdm_rx_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
|
||||
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
|
||||
|
||||
i2s_ll_rx_enable_pdm(handle->controller->hal.dev);
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
|
||||
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
|
||||
#endif
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
|
||||
#endif
|
||||
|
||||
/* Initialization finished, mark state as ready */
|
||||
handle->state = I2S_CHAN_STATE_READY;
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
|
||||
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 disabled before reconfiguring the clock");
|
||||
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)handle->mode_info;
|
||||
ESP_GOTO_ON_FALSE(pdm_rx_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && pdm_rx_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
|
||||
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_release();
|
||||
handle->apll_en = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
// Create/Re-create power management lock
|
||||
if (pdm_rx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
|
||||
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 disabled 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");
|
||||
|
||||
/* 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_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
|
||||
|
||||
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 disabled before reconfiguring the gpio");
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_pdm_rx_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_RX
|
209
components/driver/i2s/i2s_private.h
Normal file
209
components/driver/i2s/i2s_private.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 "driver/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, "input parameter '"#p"' is NULL")
|
||||
|
||||
/**
|
||||
* @brief i2s channel state for checking if the operation in under right driver state
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CHAN_STATE_REGISTER, /*!< i2s channel is registered (not initialized) */
|
||||
I2S_CHAN_STATE_READY, /*!< i2s channel is disabled (initialized) */
|
||||
I2S_CHAN_STATE_RUNNING, /*!< i2s channel is idling (initialized and enabled) */
|
||||
} 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 handle */
|
||||
#else
|
||||
intr_handle_t dma_chan; /*!< interrupt channel handle */
|
||||
#endif
|
||||
uint32_t desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */
|
||||
uint32_t frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */
|
||||
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*/
|
||||
} i2s_controller_t;
|
||||
|
||||
struct i2s_channel_t {
|
||||
/* Channel basic information */
|
||||
i2s_controller_t *controller; /*!< Parent pointer to controller object */
|
||||
i2s_comm_mode_t mode; /*!< i2s channel communication mode */
|
||||
i2s_role_t role; /*!< i2s role */
|
||||
i2s_dir_t dir; /*!< i2s channel direction */
|
||||
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 semaphore for the channel operations */
|
||||
SemaphoreHandle_t binary; /*!< Binary semaphore for writing / reading / enabling / disabling */
|
||||
#if CONFIG_PM_ENABLE
|
||||
esp_pm_lock_handle_t pm_lock; /*!< Power management lock, to avoid apb clock frequency changes while i2s is working */
|
||||
#endif
|
||||
#if CONFIG_I2S_ISR_IRAM_SAFE
|
||||
StaticSemaphore_t *mutex_struct; /*!< Static mutex struct */
|
||||
StaticSemaphore_t *binary_struct; /*!< Static binary struct */
|
||||
StaticQueue_t *msg_que_struct; /*!< Static message queue struct */
|
||||
void *msg_que_storage; /*!< Static message queue storage */
|
||||
#endif
|
||||
QueueHandle_t msg_queue; /*!< Message queue handler, used for transporting data between interrupt and read/write task */
|
||||
i2s_event_callbacks_t callbacks; /*!< Callback functions */
|
||||
void *user_data; /*!< User data for callback functions */
|
||||
void (*start)(i2s_chan_handle_t); /*!< start tx/rx channel */
|
||||
void (*stop)(i2s_chan_handle_t); /*!< stop tx/rx channel */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief i2s platform level configurations
|
||||
* @note All i2s controllers' resources are involved
|
||||
*/
|
||||
typedef struct {
|
||||
portMUX_TYPE spinlock; /*!< Platform level lock */
|
||||
i2s_controller_t *controller[SOC_I2S_NUM]; /*!< Controller object */
|
||||
const char *comp_name[SOC_I2S_NUM]; /*!< The component name that occupied i2s controller */
|
||||
} i2s_platform_t;
|
||||
|
||||
extern i2s_platform_t g_i2s;
|
||||
|
||||
/**
|
||||
* @brief Initialize I2S DMA interrupt
|
||||
*
|
||||
* @param handle I2S channel handle
|
||||
* @param intr_flag I2S interrupt flags, `ESP_INTR_FLAG_XXX` defined in `esp_intr_alloc.h`
|
||||
* @return
|
||||
* - ESP_OK Initialize interrupt success
|
||||
* - ESP_ERR_INVALID_ARG Wrong port id or NULL pointer
|
||||
*/
|
||||
esp_err_t i2s_init_dma_intr(i2s_chan_handle_t handle, int intr_flag);
|
||||
|
||||
/**
|
||||
* @brief Free I2S DMA descriptor and DMA buffer
|
||||
*
|
||||
* @param handle I2S channel handle
|
||||
* @return
|
||||
* - ESP_OK Free success
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer
|
||||
*/
|
||||
esp_err_t i2s_free_dma_desc(i2s_chan_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for I2S DMA descriptor and DMA buffer
|
||||
*
|
||||
* @param handle I2S channel handle
|
||||
* @param num Number of DMA descriptors
|
||||
* @param bufsize The DMA buffer size
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Allocate memory success
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or bufsize is too big
|
||||
* - ESP_ERR_NO_MEM No memmory for DMA descriptor and DMA buffer
|
||||
*/
|
||||
esp_err_t i2s_alloc_dma_desc(i2s_chan_handle_t handle, uint32_t num, uint32_t bufsize);
|
||||
|
||||
/**
|
||||
* @brief Get DMA buffer size
|
||||
*
|
||||
* @param handle I2S channel handle
|
||||
* @param data_bit_width Data bit width in one slot
|
||||
* @param dma_frame_num Frame number in one DMA buffer
|
||||
*
|
||||
* @return
|
||||
* - DMA buffer size
|
||||
*/
|
||||
uint32_t i2s_get_buf_size(i2s_chan_handle_t handle, uint32_t data_bit_width, uint32_t dma_frame_num);
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/**
|
||||
* @brief Set mclk frequency and get the actuall APLL frequency
|
||||
*
|
||||
* @param mclk_freq_hz Expected mclk frequenct in Hz
|
||||
* @return
|
||||
* - Actuall APLL frequency
|
||||
*/
|
||||
uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq_hz);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Check gpio validity and attach to corresponding signal
|
||||
*
|
||||
* @param gpio GPIO number
|
||||
* @param signal_idx Signal index
|
||||
* @param is_input Is input gpio
|
||||
* @param is_invert Is invert gpio
|
||||
*/
|
||||
void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input, bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Check gpio validity and output mclk signal
|
||||
*
|
||||
* @param id I2S port id
|
||||
* @param gpio_num GPIO number
|
||||
* @param is_apll Is using APLL as clock source
|
||||
* @param is_invert Is invert the GPIO
|
||||
* @return
|
||||
* - ESP_OK Set mclk output gpio success
|
||||
* - ESP_ERR_INVALID_ARG Invalid GPIO number
|
||||
*/
|
||||
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Attach data out signal and data in signal to a same gpio
|
||||
*
|
||||
* @param gpio GPIO number
|
||||
* @param out_sig_idx Data out signal index
|
||||
* @param in_sig_idx Data in signal index
|
||||
*/
|
||||
void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_sig_idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
355
components/driver/i2s/i2s_std.c
Normal file
355
components/driver/i2s/i2s_std.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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 "sdkconfig.h"
|
||||
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#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_SRC_APLL) ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
|
||||
#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);
|
||||
|
||||
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(&g_i2s.spinlock);
|
||||
/* Set clock configurations in HAL*/
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
} else {
|
||||
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
}
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: clock configuration */
|
||||
memcpy(&(std_cfg->clk_cfg), clk_cfg, sizeof(i2s_std_clk_config_t));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
|
||||
{
|
||||
/* Check configuration validity */
|
||||
ESP_RETURN_ON_FALSE((slot_cfg->slot_mode == I2S_SLOT_MODE_STEREO) || (slot_cfg->slot_mask != I2S_STD_SLOT_LEFT_RIGHT),
|
||||
ESP_ERR_INVALID_ARG, TAG, "Can't select both left and right slot in mono mode");
|
||||
|
||||
/* Update the total slot num and active slot num */
|
||||
handle->total_slot = 2;
|
||||
handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2;
|
||||
|
||||
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->controller->full_duplex) {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
||||
/* Since bck and ws are shared, only tx or rx can be master
|
||||
Force to set rx as slave to avoid conflict of clock signal */
|
||||
if (handle->dir == I2S_DIR_RX) {
|
||||
is_slave = true;
|
||||
}
|
||||
} else {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply STD format */
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_hal_std_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
} else {
|
||||
i2s_hal_std_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
}
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: slot configuration */
|
||||
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
|
||||
memcpy(&(std_cfg->slot_cfg), slot_cfg, sizeof(i2s_std_slot_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
int id = handle->controller->id;
|
||||
|
||||
/* Check validity of selected pins */
|
||||
ESP_RETURN_ON_FALSE((gpio_cfg->bclk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->bclk)),
|
||||
ESP_ERR_INVALID_ARG, TAG, "bclk invalid");
|
||||
ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)),
|
||||
ESP_ERR_INVALID_ARG, TAG, "ws invalid");
|
||||
i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info);
|
||||
|
||||
/* Loopback if dout = din */
|
||||
if (gpio_cfg->dout != -1 &&
|
||||
gpio_cfg->dout == gpio_cfg->din) {
|
||||
i2s_gpio_loopback_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, i2s_periph_signal[id].data_in_sig);
|
||||
} else if (handle->dir == I2S_DIR_TX) {
|
||||
/* Set data output GPIO */
|
||||
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
|
||||
} else {
|
||||
/* Set data input GPIO */
|
||||
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
|
||||
}
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + slave" mode, select TX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
} else {
|
||||
/* mclk only available in master mode */
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
bool is_apll = std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL;
|
||||
#else
|
||||
bool is_apll = false;
|
||||
#endif
|
||||
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, is_apll, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
|
||||
/* For "rx + master" mode, select RX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
}
|
||||
/* Update the mode info: gpio configuration */
|
||||
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
xSemaphoreTake(handle->mutex, portMAX_DELAY);
|
||||
handle->mode = I2S_COMM_MODE_STD;
|
||||
/* Allocate memory for storing the configurations of standard mode */
|
||||
if (handle->mode_info) {
|
||||
free(handle->mode_info);
|
||||
}
|
||||
handle->mode_info = calloc(1, sizeof(i2s_std_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
|
||||
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
|
||||
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is APLL */
|
||||
if (std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
#endif
|
||||
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");
|
||||
#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->controller->hal.dev);
|
||||
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
|
||||
} else {
|
||||
i2s_ll_rx_enable_std(handle->controller->hal.dev);
|
||||
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
|
||||
#endif
|
||||
|
||||
/* Initialization finished, mark state as ready */
|
||||
handle->state = I2S_CHAN_STATE_READY;
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
|
||||
|
||||
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 disabled before reconfiguring the clock");
|
||||
|
||||
i2s_std_config_t *std_cfg = (i2s_std_config_t *)handle->mode_info;
|
||||
ESP_GOTO_ON_FALSE(std_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && std_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
|
||||
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_release();
|
||||
handle->apll_en = false;
|
||||
}
|
||||
#endif
|
||||
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
// Create/Re-create power management lock
|
||||
if (std_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
|
||||
|
||||
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 disabled 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");
|
||||
|
||||
/* 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_channel_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
|
||||
|
||||
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 disabled before reconfiguring the gpio");
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
352
components/driver/i2s/i2s_tdm.c
Normal file
352
components/driver/i2s/i2s_tdm.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* 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 "sdkconfig.h"
|
||||
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#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_SRC_APLL ? i2s_set_get_apll_freq(clk_info->mclk) : I2S_LL_BASE_CLK;
|
||||
#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);
|
||||
|
||||
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(&g_i2s.spinlock);
|
||||
/* Set clock configurations in HAL*/
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
} else {
|
||||
i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src);
|
||||
}
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: clock configuration */
|
||||
memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_config_t));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg)
|
||||
{
|
||||
/* 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
|
||||
|
||||
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->controller->full_duplex) {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
|
||||
/* Since bck and ws are shared, only tx or rx can be master
|
||||
Force to set rx as slave to avoid conflict of clock signal */
|
||||
if (handle->dir == I2S_DIR_RX) {
|
||||
is_slave = true;
|
||||
}
|
||||
} else {
|
||||
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&g_i2s.spinlock);
|
||||
/* Configure the hardware to apply TDM format */
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_hal_tdm_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
} else {
|
||||
i2s_hal_tdm_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg);
|
||||
}
|
||||
portEXIT_CRITICAL(&g_i2s.spinlock);
|
||||
|
||||
/* Update the mode info: slot configuration */
|
||||
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
|
||||
memcpy(&(tdm_cfg->slot_cfg), slot_cfg, sizeof(i2s_tdm_slot_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
int id = handle->controller->id;
|
||||
|
||||
/* Check validity of selected pins */
|
||||
ESP_RETURN_ON_FALSE((gpio_cfg->bclk == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->bclk)),
|
||||
ESP_ERR_INVALID_ARG, TAG, "bclk invalid");
|
||||
ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)),
|
||||
ESP_ERR_INVALID_ARG, TAG, "ws invalid");
|
||||
/* Loopback if dout = din */
|
||||
if (gpio_cfg->dout != -1 &&
|
||||
gpio_cfg->dout == gpio_cfg->din) {
|
||||
i2s_gpio_loopback_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, i2s_periph_signal[id].data_in_sig);
|
||||
} else if (handle->dir == I2S_DIR_TX) {
|
||||
/* Set data output GPIO */
|
||||
i2s_gpio_check_and_set(gpio_cfg->dout, i2s_periph_signal[id].data_out_sig, false, false);
|
||||
} else {
|
||||
/* Set data input GPIO */
|
||||
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
|
||||
}
|
||||
|
||||
if (handle->role == I2S_ROLE_SLAVE) {
|
||||
/* For "tx + slave" mode, select TX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
} else {
|
||||
/* mclk only available in master mode */
|
||||
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, false, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
|
||||
/* For "rx + master" mode, select RX signal index for ws and bck */
|
||||
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
|
||||
#endif
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
|
||||
} else {
|
||||
i2s_gpio_check_and_set(gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
|
||||
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
|
||||
}
|
||||
}
|
||||
/* Update the mode info: gpio configuration */
|
||||
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
|
||||
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
|
||||
{
|
||||
#if CONFIG_I2S_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
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");
|
||||
handle->mode = I2S_COMM_MODE_TDM;
|
||||
/* Allocate memory for storing the configurations of TDM mode */
|
||||
if (handle->mode_info) {
|
||||
free(handle->mode_info);
|
||||
}
|
||||
handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t));
|
||||
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
|
||||
/* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is APLL */
|
||||
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
#endif
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
|
||||
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, ESP_INTR_FLAG_LEVEL1), err, TAG, "initialize dma interrupt failed");
|
||||
|
||||
#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->controller->hal.dev);
|
||||
i2s_ll_tx_enable_clock(handle->controller->hal.dev);
|
||||
} else {
|
||||
i2s_ll_rx_enable_tdm(handle->controller->hal.dev);
|
||||
i2s_ll_rx_enable_clock(handle->controller->hal.dev);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
|
||||
#endif
|
||||
|
||||
/* Initialization finished, mark state as ready */
|
||||
handle->state = I2S_CHAN_STATE_READY;
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, clk_cfg);
|
||||
|
||||
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 disabled before reconfiguring the clock");
|
||||
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)handle->mode_info;
|
||||
ESP_GOTO_ON_FALSE(tdm_cfg, ESP_ERR_INVALID_STATE, err, TAG, "initialization not complete");
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
/* Enable APLL and acquire its lock when the clock source is changed to APLL */
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL && clk_cfg->clk_cfg.clk_src != I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_acquire();
|
||||
handle->apll_en = true;
|
||||
}
|
||||
/* Disable APLL and release its lock when clock source is changed to 160M_PLL */
|
||||
if (clk_cfg->clk_src != I2S_CLK_SRC_APLL && clk_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_release();
|
||||
handle->apll_en = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, clk_cfg), err, TAG, "update clock failed");
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
// Create/Re-create power management lock
|
||||
if (tdm_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
|
||||
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
|
||||
pm_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), err, TAG, "I2S pm lock create failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, slot_cfg);
|
||||
|
||||
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 disabled 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");
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* Reset queue */
|
||||
xQueueReset(handle->msg_queue);
|
||||
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg)
|
||||
{
|
||||
I2S_NULL_POINTER_CHECK(TAG, handle);
|
||||
I2S_NULL_POINTER_CHECK(TAG, gpio_cfg);
|
||||
|
||||
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 disabled before reconfiguring the gpio");
|
||||
|
||||
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, gpio_cfg), err, TAG, "set i2s standard slot failed");
|
||||
xSemaphoreGive(handle->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ret;
|
||||
}
|
212
components/driver/include/driver/i2s_common.h
Normal file
212
components/driver/include/driver/i2s_common.h
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/i2s_types.h"
|
||||
#include "hal/i2s_types.h"
|
||||
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief get default I2S property
|
||||
*/
|
||||
#define I2S_CHANNEL_DEFAULT_CONFIG(i2s_num, i2s_role) { \
|
||||
.id = i2s_num, \
|
||||
.role = i2s_role, \
|
||||
.dma_desc_num = 3, \
|
||||
.dma_frame_num = 500, \
|
||||
.auto_clear = false, \
|
||||
}
|
||||
|
||||
#define I2S_GPIO_UNUSED GPIO_NUM_NC /*!< Used in i2s_gpio_config_t for signals which are not used */
|
||||
|
||||
/**
|
||||
* @brief Group of I2S callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
* @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
|
||||
* The variables used in the function should be in the SRAM as well.
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_isr_callback_t on_recv; /**< Callback of data received event, only for rx channel
|
||||
* The event data includes DMA buffer address and size that just finished receiving data
|
||||
*/
|
||||
i2s_isr_callback_t on_recv_q_ovf; /**< Callback of receiving queue overflowed event, only for rx channel
|
||||
* The event data includes buffer size that has been overwritten
|
||||
*/
|
||||
i2s_isr_callback_t on_sent; /**< Callback of data sent event, only for tx channel
|
||||
* The event data includes DMA buffer address and size that just finished sending data
|
||||
*/
|
||||
i2s_isr_callback_t on_send_q_ovf; /**< Callback of sending queue overflowed evnet, only for tx channel
|
||||
* The event data includes buffer size that has been overwritten
|
||||
*/
|
||||
} i2s_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief I2S controller channel configuration
|
||||
*/
|
||||
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 buffer, i2s will always send zero automatically if no data to send */
|
||||
|
||||
} i2s_chan_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S channel information
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_port_t id; /*!< I2S port id */
|
||||
i2s_role_t role; /*!< I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE */
|
||||
i2s_dir_t dir; /*!< I2S channel direction */
|
||||
i2s_comm_mode_t mode; /*!< I2S channel communication mode */
|
||||
i2s_chan_handle_t pair_chan; /*!< I2S pair channel handle in duplex mode, always NULL in simplex mode */
|
||||
} i2s_chan_info_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate new I2S channel(s)
|
||||
* @note The new created I2S channel handle will be REGISTERED state after it is allocated successfully.
|
||||
* @note When the port id in channel configuration is I2S_NUM_AUTO, driver will allocate I2S port automatically
|
||||
* on one of the i2s controller, otherwise driver will try to allocate the new channel on the selected port.
|
||||
* @note If both tx_handle and rx_handle are not NULL, it means this I2S controller will work at full-duplex mode,
|
||||
* the rx and tx channels will be allocated on a same I2S port in this case.
|
||||
* Note that some configurations of tx/rx channel are shared on ESP32 and ESP32S2,
|
||||
* so please make sure they are working at same condition and under same status(start/stop).
|
||||
* Currently, full-duplex mode can't guarantee tx/rx channels write/read synchronously,
|
||||
* they can only share the clock signals for now.
|
||||
* @note If tx_handle OR rx_handle is NULL, it means this I2S controller will work at simplex mode.
|
||||
* For ESP32 and ESP32S2, the whole I2S controller (i.e. both rx and tx channel) will be occupied,
|
||||
* even if only one of rx or tx channel is registered.
|
||||
* For the other targets, another channel on this controller will still available.
|
||||
*
|
||||
* @param[in] chan_cfg I2S controller channel configurations
|
||||
* @param[out] ret_tx_handle I2S channel handler used for managing the sending channel(optional)
|
||||
* @param[out] ret_rx_handle I2S channel handler used for managing the receiving channel(optional)
|
||||
* @return
|
||||
* - ESP_OK Allocate new channel(s) success
|
||||
* - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or illegal parameter in i2s_chan_config_t
|
||||
* - ESP_ERR_NOT_FOUND No available I2S channel found
|
||||
*/
|
||||
esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *ret_tx_handle, i2s_chan_handle_t *ret_rx_handle);
|
||||
|
||||
/**
|
||||
* @brief Delete the i2s channel
|
||||
* @note Only allowed to be called when the i2s channel is at REGISTERED or READY state (i.e., it should stop before deleting it).
|
||||
* @note Resource will be free automatically if all channels in one port are deleted
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* - ESP_OK Delete successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer
|
||||
*/
|
||||
esp_err_t i2s_del_channel(i2s_chan_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get I2S channel information
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[out] chan_info I2S channel basic information
|
||||
* @return
|
||||
* - ESP_OK Get i2s channel information success
|
||||
* - ESP_ERR_NOT_FOUND The input handle doesn't match any registered I2S channels, it may not an i2s channel handle or not available any more
|
||||
* - ESP_ERR_INVALID_ARG The input handle or chan_info pointer is NULL
|
||||
*/
|
||||
esp_err_t i2s_channel_get_info(i2s_chan_handle_t handle, i2s_chan_info_t *chan_info);
|
||||
|
||||
/**
|
||||
* @brief Enable the i2s channel
|
||||
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
|
||||
* the channel will enter RUNNING state once it is enabled successfully.
|
||||
* @note Enbale the channel can start the I2S communication on hardware. It will start outputting bclk and ws signal.
|
||||
* For mclk signal, it will start to output when initialization is finished
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* - ESP_OK Start successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer
|
||||
* - ESP_ERR_INVALID_STATE This channel has not initialized or already started
|
||||
*/
|
||||
esp_err_t i2s_channel_enable(i2s_chan_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Disable the i2s channel
|
||||
* @note Only allowed to be called when the channel state is READY / RUNNING, (i.e., channel has been initialized)
|
||||
* the channel will enter READY state once it is disabled successfully.
|
||||
* @note Disable the channel can stop the I2S communication on hardware. It will stop bclk and ws signal but not mclk signal
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @return
|
||||
* - ESP_OK Stop successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer
|
||||
* - ESP_ERR_INVALID_STATE This channel has not stated
|
||||
*/
|
||||
esp_err_t i2s_channel_disable(i2s_chan_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief I2S write data
|
||||
* @note Only allowed to be called when the channel state is RUNNING, (i.e., tx channel has been started and is not writing now)
|
||||
* but the RUNNING only stands for the software state, it doesn't mean there is no the signal transporting on line.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] src The pointer of sent data buffer
|
||||
* @param[in] size Max data buffer length
|
||||
* @param[out] bytes_written Byte number that actually be sent
|
||||
* @param[in] timeout_ms Max block time
|
||||
* @return
|
||||
* - ESP_OK Write successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or this handle is not tx handle
|
||||
* - ESP_ERR_TIMEOUT Writing timeout, no writing event received from ISR within ticks_to_wait
|
||||
* - ESP_ERR_INVALID_STATE I2S is not ready to write
|
||||
*/
|
||||
esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief I2S read data
|
||||
* @note Only allowed to be called when the channel state is RUNNING
|
||||
* but the RUNNING only stands for the software state, it doesn't mean there is no the signal transporting on line.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] dest The pointer of receiving data buffer
|
||||
* @param[in] size Max data buffer length
|
||||
* @param[out] bytes_read Byte number that actually be read
|
||||
* @param[in] timeout_ms Max block time
|
||||
* @return
|
||||
* - ESP_OK Read successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or this handle is not rx handle
|
||||
* - ESP_ERR_TIMEOUT Reading timeout, no reading event received from ISR within ticks_to_wait
|
||||
* - ESP_ERR_INVALID_STATE I2S is not ready to read
|
||||
*/
|
||||
esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for I2S channel
|
||||
*
|
||||
* @note Only allowed to be called when the channel state is REGISTARED / READY, (i.e., before channel starts)
|
||||
* @note User can deregister a previously registered callback by calling this function and setting the callback member in the `callbacks` structure to NULL.
|
||||
* @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
|
||||
* The variables used in the function should be in the SRAM as well. The `user_data` should also reside in SRAM or internal RAM as well.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] callbacks Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTARED or READY
|
||||
*/
|
||||
esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
354
components/driver/include/driver/i2s_pdm.h
Normal file
354
components/driver/include/driver/i2s_pdm.h
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "driver/i2s_common.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) { \
|
||||
.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_SRC_DEFAULT, \
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
|
||||
.dn_sample_mode = I2S_PDM_DSR_8S \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2S slot configuration for pdm rx mode
|
||||
*/
|
||||
typedef struct {
|
||||
/* General fields */
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief I2S clock configuration for pdm rx mode
|
||||
*/
|
||||
typedef struct {
|
||||
/* General fields */
|
||||
uint32_t sample_rate_hz; /*!< I2S sample rate */
|
||||
i2s_clock_src_t clk_src; /*!< Choose clock source */
|
||||
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
|
||||
/* Particular fields */
|
||||
i2s_pdm_dsr_t dn_sample_mode; /*!< Down-sampling rate mode */
|
||||
} i2s_pdm_rx_clk_config_t;
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
struct {
|
||||
uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */
|
||||
} invert_flags; /*!< GPIO pin invert flags */
|
||||
} i2s_pdm_rx_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S PDM RX mode major configuration that including clock/slot/gpio configuration
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_pdm_rx_clk_config_t clk_cfg; /*!< PDM RX clock configurations, can be genertated by macro I2S_PDM_RX_CLK_DEFAULT_CONFIG */
|
||||
i2s_pdm_rx_slot_config_t slot_cfg; /*!< PDM RX slot configurations, can be genertated by macro I2S_PDM_RX_SLOT_DEFAULT_CONFIG */
|
||||
i2s_pdm_rx_gpio_config_t gpio_cfg; /*!< PDM RX slot configurations, specified by user */
|
||||
} i2s_pdm_rx_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize i2s channel to PDM RX mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
*
|
||||
* @param[in] handle I2S rx channel handler
|
||||
* @param[in] pdm_rx_cfg Configurations for PDM RX mode, including clock, slot and gpio
|
||||
* The clock configuration can be generated by the helper macro `I2S_PDM_RX_CLK_DEFAULT_CONFIG`
|
||||
* The slot configuration can be generated by the helper macro `I2S_PDM_RX_SLOT_DEFAULT_CONFIG`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Initialize successfully
|
||||
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||
* - ESP_ERR_INVALID_STATE This channel is not registered
|
||||
*/
|
||||
esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_rx_config_t *pdm_rx_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S clock for PDM RX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S rx channel handler
|
||||
* @param[in] clk_cfg PDM RX mode clock configuration, can be generated by `I2S_PDM_RX_CLK_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_pdm_rx_clk_config_t *clk_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S slot for PDM RX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S rx channel handler
|
||||
* @param[in] slot_cfg PDM RX mode slot configuration, can be generated by `I2S_PDM_RX_SLOT_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_NO_MEM No memory for DMA buffer
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_rx_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S gpio for PDM RX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM RX mode, i.e., 'i2s_channel_init_pdm_rx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S rx channel handler
|
||||
* @param[in] gpio_cfg PDM RX mode gpio configuration, specified by user
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_rx_gpio(i2s_chan_handle_t handle, const i2s_pdm_rx_gpio_config_t *gpio_cfg);
|
||||
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_RX
|
||||
|
||||
|
||||
#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) { \
|
||||
.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) { \
|
||||
.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, \
|
||||
}
|
||||
#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 adopted.
|
||||
* @param rate sample rate
|
||||
*/
|
||||
#define I2S_PDM_TX_CLK_DEFAULT_CONFIG(rate) { \
|
||||
.sample_rate_hz = rate, \
|
||||
.clk_src = I2S_CLK_SRC_DEFAULT, \
|
||||
.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_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;
|
||||
|
||||
/**
|
||||
* @brief I2S clock configuration for pdm tx mode
|
||||
*/
|
||||
typedef struct {
|
||||
/* General fields */
|
||||
uint32_t sample_rate_hz; /*!< I2S sample rate */
|
||||
i2s_clock_src_t clk_src; /*!< Choose clock source */
|
||||
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */
|
||||
/* Particular fields */
|
||||
uint32_t up_sample_fp; /*!< Up-sampling param fp */
|
||||
uint32_t up_sample_fs; /*!< Up-sampling param fs */
|
||||
} 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 */
|
||||
struct {
|
||||
uint32_t clk_inv: 1; /*!< Set 1 to invert the clk output */
|
||||
} invert_flags; /*!< GPIO pin invert flags */
|
||||
} i2s_pdm_tx_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S PDM TX mode major configuration that including clock/slot/gpio configuration
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_pdm_tx_clk_config_t clk_cfg; /*!< PDM TX clock configurations, can be genertated by macro I2S_PDM_TX_CLK_DEFAULT_CONFIG */
|
||||
i2s_pdm_tx_slot_config_t slot_cfg; /*!< PDM TX slot configurations, can be genertated by macro I2S_PDM_TX_SLOT_DEFAULT_CONFIG */
|
||||
i2s_pdm_tx_gpio_config_t gpio_cfg; /*!< PDM TX gpio configurations, specified by user */
|
||||
} i2s_pdm_tx_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize i2s channel to PDM TX mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
*
|
||||
* @param[in] handle I2S tx channel handler
|
||||
* @param[in] pdm_tx_cfg Configurations for PDM TX mode, including clock, slot and gpio
|
||||
* The clock configuration can be generated by the helper macro `I2S_PDM_TX_CLK_DEFAULT_CONFIG`
|
||||
* The slot configuration can be generated by the helper macro `I2S_PDM_TX_SLOT_DEFAULT_CONFIG`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Initialize successfully
|
||||
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||
* - ESP_ERR_INVALID_STATE This channel is not registered
|
||||
*/
|
||||
esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_tx_config_t *pdm_tx_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S clock for PDM TX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S tx channel handler
|
||||
* @param[in] clk_cfg PDM TX mode clock configuration, can be generated by `I2S_PDM_TX_CLK_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_pdm_tx_clk_config_t *clk_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S slot for PDM TX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S tx channel handler
|
||||
* @param[in] slot_cfg PDM TX mode slot configuration, can be generated by `I2S_PDM_TX_SLOT_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_NO_MEM No memory for DMA buffer
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_tx_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S gpio for PDM TX mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to PDM TX mode, i.e., 'i2s_channel_init_pdm_tx_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S tx channel handler
|
||||
* @param[in] gpio_cfg PDM TX mode gpio configuration, specified by user
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not PDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_pdm_tx_gpio(i2s_chan_handle_t handle, const i2s_pdm_tx_gpio_config_t *gpio_cfg);
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_TX
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
274
components/driver/include/driver/i2s_std.h
Normal file
274
components/driver/include/driver/i2s_std.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "driver/i2s_common.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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = bits_per_sample, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = true, \
|
||||
.msb_right = true, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = 1, \
|
||||
.ws_pol = true, \
|
||||
.bit_shift = true, \
|
||||
.msb_right = true, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = bits_per_sample, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = false, \
|
||||
.msb_right = true, \
|
||||
}
|
||||
|
||||
#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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = bits_per_sample, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = true, \
|
||||
.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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = 1, \
|
||||
.ws_pol = true, \
|
||||
.bit_shift = true, \
|
||||
.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, \
|
||||
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
|
||||
I2S_STD_SLOT_ONLY_LEFT : I2S_STD_SLOT_LEFT_RIGHT, \
|
||||
.ws_width = bits_per_sample, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = false, \
|
||||
.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_SRC_DEFAULT, \
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 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 */
|
||||
i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */
|
||||
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
|
||||
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
|
||||
bool bit_shift; /*!< Set to enbale bit shift in Philip mode */
|
||||
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief 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 */
|
||||
struct {
|
||||
uint32_t mclk_inv: 1; /*!< Set 1 to invert the mclk output */
|
||||
uint32_t bclk_inv: 1; /*!< Set 1 to invert the bclk input/output */
|
||||
uint32_t ws_inv: 1; /*!< Set 1 to invert the ws input/output */
|
||||
} invert_flags; /*!< GPIO pin invert flags */
|
||||
} i2s_std_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S standard mode major configuration that including clock/slot/gpio configuration
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_std_clk_config_t clk_cfg; /*!< Standard mode clock configuration, can be generated by macro I2S_STD_CLK_DEFAULT_CONFIG */
|
||||
i2s_std_slot_config_t slot_cfg; /*!< Standard mode slot configuration, can be generated by macros I2S_STD_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIP/MSB/PCM */
|
||||
i2s_std_gpio_config_t gpio_cfg; /*!< Standard mode gpio configuration, specified by user */
|
||||
} i2s_std_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize i2s channel to standard mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] std_cfg Configurations for standard mode, including clock, slot and gpio
|
||||
* The clock configuration can be generated by the helper macro `I2S_STD_CLK_DEFAULT_CONFIG`
|
||||
* The slot configuration can be generated by the helper macro `I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG`,
|
||||
* `I2S_STD_PCM_SLOT_DEFAULT_CONFIG` or `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Initialize successfully
|
||||
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||
* - ESP_ERR_INVALID_STATE This channel is not registered
|
||||
*/
|
||||
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S clock for standard mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] clk_cfg Standard mode clock configuration, can be generated by `I2S_STD_CLK_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S slot for standard mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] slot_cfg Standard mode slot configuration, can be generated by `I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG`,
|
||||
* `I2S_STD_PCM_SLOT_DEFAULT_CONFIG` and `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`.
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_NO_MEM No memory for DMA buffer
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S gpio for standard mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to standard mode, i.e., 'i2s_channel_init_std_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] gpio_cfg Standard mode gpio configuration, specified by user
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
252
components/driver/include/driver/i2s_tdm.h
Normal file
252
components/driver/include/driver/i2s_tdm.h
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "driver/i2s_common.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) { \
|
||||
.data_bit_width = (bits_per_sample), \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
|
||||
.slot_mode = mono_or_stereo, \
|
||||
.slot_mask = (mask), \
|
||||
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = true, \
|
||||
.left_align = false, \
|
||||
.big_endian = false, \
|
||||
.bit_order_lsb = false, \
|
||||
.skip_mask = false, \
|
||||
.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) { \
|
||||
.data_bit_width = (bits_per_sample), \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
|
||||
.slot_mode = mono_or_stereo, \
|
||||
.slot_mask = (mask), \
|
||||
.ws_width = I2S_TDM_AUTO_WS_WIDTH, \
|
||||
.ws_pol = false, \
|
||||
.bit_shift = false, \
|
||||
.left_align = false, \
|
||||
.big_endian = false, \
|
||||
.bit_order_lsb = false, \
|
||||
.skip_mask = false ,\
|
||||
.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) { \
|
||||
.data_bit_width = (bits_per_sample), \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
|
||||
.slot_mode = mono_or_stereo, \
|
||||
.slot_mask = (mask), \
|
||||
.ws_width = 1, \
|
||||
.ws_pol = true, \
|
||||
.bit_shift = true, \
|
||||
.left_align = false, \
|
||||
.big_endian = false, \
|
||||
.bit_order_lsb = false, \
|
||||
.skip_mask = false, \
|
||||
.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) { \
|
||||
.data_bit_width = (bits_per_sample), \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
|
||||
.slot_mode = mono_or_stereo, \
|
||||
.slot_mask = (mask), \
|
||||
.ws_width = (bits_per_sample), \
|
||||
.ws_pol = true, \
|
||||
.bit_shift = true, \
|
||||
.left_align = false, \
|
||||
.big_endian = false, \
|
||||
.bit_order_lsb = false, \
|
||||
.skip_mask = false, \
|
||||
.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_SRC_DEFAULT, \
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2S slot configuration for tdm 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 */
|
||||
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
|
||||
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
|
||||
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
|
||||
bool bit_shift; /*!< Set true to enable bit shift in Philip mode */
|
||||
|
||||
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 */
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief 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 */
|
||||
struct {
|
||||
uint32_t mclk_inv: 1; /*!< Set 1 to invert the mclk output */
|
||||
uint32_t bclk_inv: 1; /*!< Set 1 to invert the bclk input/output */
|
||||
uint32_t ws_inv: 1; /*!< Set 1 to invert the ws input/output */
|
||||
} invert_flags; /*!< GPIO pin invert flags */
|
||||
} i2s_tdm_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S TDM mode major configuration that including clock/slot/gpio configuration
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_tdm_clk_config_t clk_cfg; /*!< TDM mode clock configuration, can be generated by macro I2S_TDM_CLK_DEFAULT_CONFIG */
|
||||
i2s_tdm_slot_config_t slot_cfg; /*!< TDM mode slot configuration, can be generated by macros I2S_TDM_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIP/MSB/PCM_SHORT/PCM_LONG */
|
||||
i2s_tdm_gpio_config_t gpio_cfg; /*!< TDM mode gpio configuration, specified by user */
|
||||
} i2s_tdm_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize i2s channel to TDM mode
|
||||
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
|
||||
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] tdm_cfg Configurations for TDM mode, including clock, slot and gpio
|
||||
* The clock configuration can be generated by the helper macro `I2S_TDM_CLK_DEFAULT_CONFIG`
|
||||
* The slot configuration can be generated by the helper macro `I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG`,
|
||||
* `I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG`, `I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG` or `I2S_TDM_MSB_SLOT_DEFAULT_CONFIG`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Initialize successfully
|
||||
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||
* - ESP_ERR_INVALID_STATE This channel is not registered
|
||||
*/
|
||||
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S clock for TDM mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] clk_cfg Standard mode clock configuration, can be generated by `I2S_TDM_CLK_DEFAULT_CONFIG`
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_config_t *clk_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S slot for TDM mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] slot_cfg Standard mode slot configuration, can be generated by `I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG`,
|
||||
* `I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG`, `I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG` or `I2S_TDM_MSB_SLOT_DEFAULT_CONFIG`.
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_NO_MEM No memory for DMA buffer
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_tdm_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the I2S gpio for TDM mode
|
||||
* @note Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started
|
||||
* this function won't change the state. 'i2s_channel_disable' should be called before calling this function if i2s has started.
|
||||
* @note The input channel handle has to be initialized to TDM mode, i.e., 'i2s_channel_init_tdm_mode' has been called before reconfigring
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] gpio_cfg Standard mode gpio configuration, specified by user
|
||||
* @return
|
||||
* - ESP_OK Set clock successfully
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not TDM mode
|
||||
* - ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
|
||||
*/
|
||||
esp_err_t i2s_channel_reconfig_tdm_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_config_t *gpio_cfg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
79
components/driver/include/driver/i2s_types.h
Normal file
79
components/driver/include/driver/i2s_types.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2S controller port number, the max port number is (SOC_I2S_NUM -1).
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_NUM_0 = 0, /*!< I2S controller port 0 */
|
||||
#if SOC_I2S_NUM > 1
|
||||
I2S_NUM_1 = 1, /*!< I2S controller port 1 */
|
||||
#endif
|
||||
I2S_NUM_AUTO, /*!< Select whichever port is available */
|
||||
} i2s_port_t;
|
||||
|
||||
/**
|
||||
* @brief I2S controller communication mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_COMM_MODE_STD, /*!< I2S controller using standard communication mode, support philip/MSB/PCM format */
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
I2S_COMM_MODE_PDM, /*!< I2S controller using PDM communication mode, support PDM output or input */
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
I2S_COMM_MODE_TDM, /*!< I2S controller using TDM communication mode, support up to 16 slots per frame */
|
||||
#endif
|
||||
I2S_COMM_MODE_NONE, /*!< Unspecified I2S controller mode */
|
||||
} i2s_comm_mode_t;
|
||||
|
||||
/**
|
||||
* @brief The multiple of mclk to sample rate
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_MCLK_MULTIPLE_128 = 128, /*!< mclk = sample_rate * 128 */
|
||||
I2S_MCLK_MULTIPLE_256 = 256, /*!< mclk = sample_rate * 256 */
|
||||
I2S_MCLK_MULTIPLE_384 = 384, /*!< mclk = sample_rate * 384 */
|
||||
} i2s_mclk_multiple_t;
|
||||
|
||||
/**
|
||||
* @brief Event structure used in I2S event queue
|
||||
*/
|
||||
typedef struct {
|
||||
void *data; /**< The pointer of DMA buffer that just finished sending or receiving for `on_recv` and `on_sent` callback
|
||||
* NULL for `on_recv_q_ovf` and `on_send_q_ovf` callback
|
||||
*/
|
||||
size_t size; /**< The buffer size of DMA buffer when success to send or receive,
|
||||
* also the buffer size that dropped when queue overflow.
|
||||
* It is related to the dma_frame_num and data_bit_width, typically it is fixed when data_bit_width is not changed.
|
||||
*/
|
||||
} i2s_event_data_t;
|
||||
|
||||
typedef struct i2s_channel_t *i2s_chan_handle_t; /*!< i2s channel handle, the control unit of the i2s driver*/
|
||||
|
||||
/**
|
||||
* @brief I2S event callback
|
||||
* @param[in] handle I2S channel handle, created from `i2s_new_channel()`
|
||||
* @param[in] event I2S event data
|
||||
* @param[in] user_ctx User registered context, passed from `i2s_channel_register_event_callback()`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*i2s_isr_callback_t)(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -16,32 +16,40 @@ 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);
|
||||
|
||||
/**
|
||||
* @brief This function is only used for getting DMA buffer offset in `test_i2s_iram.c`
|
||||
*
|
||||
* @return
|
||||
* - The offset of DMA buffers in the `i2s_chan_handle_t` struct (unit: bytes)
|
||||
*/
|
||||
size_t i2s_platform_get_dma_buffer_offset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -219,36 +219,45 @@ 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));
|
||||
size_t bytesRead;
|
||||
uint16_t *adcReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
|
||||
uint32_t bytesRead;
|
||||
for (int loop = 0; loop < 10; loop++) {
|
||||
for (int level = 0; level <= 1; level++) {
|
||||
if (level == 0) {
|
||||
@@ -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.");
|
||||
}
|
||||
|
@@ -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"
|
||||
|
||||
|
18
components/driver/test_apps/i2s_test_apps/i2s/CMakeLists.txt
Normal file
18
components/driver/test_apps/i2s_test_apps/i2s/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2s_test)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/i2s_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
2
components/driver/test_apps/i2s_test_apps/i2s/README.md
Normal file
2
components/driver/test_apps/i2s_test_apps/i2s/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
@@ -0,0 +1,7 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_i2s.c"
|
||||
"test_i2s_iram.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_INCLUDE_DIRS "../../"
|
||||
WHOLE_ARCHIVE)
|
@@ -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();
|
||||
}
|
855
components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c
Normal file
855
components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c
Normal file
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "unity.h"
|
||||
#include "math.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
#include "driver/i2s_pdm.h"
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
#include "driver/i2s_tdm.h"
|
||||
#endif
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "esp_private/i2s_platform.h"
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
#include "driver/pulse_cnt.h"
|
||||
#include "soc/pcnt_periph.h"
|
||||
#endif
|
||||
|
||||
#include "test_inc/test_i2s.h"
|
||||
|
||||
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
|
||||
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
|
||||
#define I2S_TEST_MODE_LOOPBACK 2
|
||||
|
||||
#define I2S_TEST_MASTER_DEFAULT_PIN { \
|
||||
.mclk = MASTER_MCK_IO, \
|
||||
.bclk = MASTER_BCK_IO, \
|
||||
.ws = MASTER_WS_IO, \
|
||||
.dout = DATA_OUT_IO, \
|
||||
.din = DATA_IN_IO, \
|
||||
.invert_flags = { \
|
||||
.mclk_inv = false, \
|
||||
.bclk_inv = false, \
|
||||
.ws_inv = false, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
|
||||
.mclk = -1, \
|
||||
.bclk = SLAVE_BCK_IO, \
|
||||
.ws = SLAVE_WS_IO, \
|
||||
.dout = DATA_OUT_IO, \
|
||||
.din = DATA_IN_IO, \
|
||||
.invert_flags = { \
|
||||
.mclk_inv = false, \
|
||||
.bclk_inv = false, \
|
||||
.ws_inv = false, \
|
||||
}, \
|
||||
}
|
||||
|
||||
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
|
||||
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
|
||||
static void i2s_test_io_config(int mode)
|
||||
{
|
||||
// Connect internal signals using IO matrix.
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_BCK_IO], PIN_FUNC_GPIO);
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[DATA_OUT_IO], PIN_FUNC_GPIO);
|
||||
|
||||
gpio_set_direction(MASTER_BCK_IO, GPIO_MODE_INPUT_OUTPUT);
|
||||
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
|
||||
gpio_set_direction(DATA_OUT_IO, GPIO_MODE_INPUT_OUTPUT);
|
||||
|
||||
switch (mode) {
|
||||
#if SOC_I2S_NUM > 1
|
||||
case I2S_TEST_MODE_SLAVE_TO_MASTER: {
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2S_TEST_MODE_MASTER_TO_SLAVE: {
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case I2S_TEST_MODE_LOOPBACK: {
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
TEST_FAIL_MESSAGE("error: mode not supported");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan)
|
||||
{
|
||||
#define I2S_SEND_BUF_LEN 100
|
||||
#define I2S_RECV_BUF_LEN 10000
|
||||
|
||||
size_t bytes_write = 0;
|
||||
size_t bytes_read = 0;
|
||||
|
||||
bool is_success = false;
|
||||
|
||||
uint8_t *send_buf = (uint8_t *)calloc(I2S_SEND_BUF_LEN, sizeof(uint8_t));
|
||||
TEST_ASSERT_NOT_NULL(send_buf);
|
||||
uint8_t *recv_buf = (uint8_t *)calloc(I2S_RECV_BUF_LEN, sizeof(uint8_t));
|
||||
TEST_ASSERT_NOT_NULL(recv_buf);
|
||||
|
||||
for (int i = 0; i < I2S_SEND_BUF_LEN; i++) {
|
||||
send_buf[i] = i + 1;
|
||||
}
|
||||
|
||||
// write data to slave
|
||||
TEST_ESP_OK(i2s_channel_write(tx_chan, send_buf, I2S_SEND_BUF_LEN, &bytes_write, 1000));
|
||||
TEST_ESP_OK(i2s_channel_read(rx_chan, recv_buf, I2S_RECV_BUF_LEN, &bytes_read, 1000));
|
||||
TEST_ASSERT_EQUAL_INT32(I2S_SEND_BUF_LEN, bytes_write);
|
||||
TEST_ASSERT_EQUAL_INT32(I2S_RECV_BUF_LEN, bytes_read);
|
||||
// test the read data right or not
|
||||
for (int i = 0, j = 0; i < (I2S_RECV_BUF_LEN - I2S_SEND_BUF_LEN); i++) {
|
||||
if (recv_buf[i] == 1) {
|
||||
for (j = 1; (j < I2S_SEND_BUF_LEN) && (recv_buf[i+j] == j + 1); j++) {}
|
||||
if (j == I2S_SEND_BUF_LEN) {
|
||||
is_success = true;
|
||||
goto finish;
|
||||
}
|
||||
i += j;
|
||||
}
|
||||
}
|
||||
finish:
|
||||
free(send_buf);
|
||||
free(recv_buf);
|
||||
TEST_ASSERT(is_success);
|
||||
}
|
||||
|
||||
// To check if the software logic of I2S driver is correct
|
||||
TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
|
||||
{
|
||||
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
i2s_chan_info_t chan_info;
|
||||
|
||||
/* TX channel basic test */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_NONE);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_STD);
|
||||
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
|
||||
std_cfg.clk_cfg.sample_rate_hz = 44100;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ASSERT(i2s_channel_get_info(tx_handle, &chan_info) == ESP_ERR_NOT_FOUND);
|
||||
|
||||
/* Duplex channel basic test */
|
||||
chan_cfg.id = I2S_NUM_0; // Specify port id to I2S port 0
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
TEST_ASSERT(chan_info.pair_chan == rx_handle);
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
/* Repeat to check if a same port can be allocated again */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
/* Hold the occupation */
|
||||
TEST_ESP_OK(i2s_platform_acquire_occupation(I2S_NUM_0, "test_i2s"));
|
||||
TEST_ASSERT(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle) == ESP_ERR_NOT_FOUND);
|
||||
TEST_ESP_OK(i2s_platform_release_occupation(I2S_NUM_0));
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
static volatile bool task_run_flag;
|
||||
|
||||
static void i2s_read_task(void *args) {
|
||||
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)args;
|
||||
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
|
||||
TEST_ASSERT(recv_buf);
|
||||
size_t recv_size = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t cnt = 1;
|
||||
|
||||
while (task_run_flag) {
|
||||
ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("Read timeout count: %d\n", cnt++);
|
||||
}
|
||||
}
|
||||
|
||||
free(recv_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void i2s_write_task(void *args) {
|
||||
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
|
||||
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
|
||||
TEST_ASSERT(send_buf);
|
||||
size_t send_size = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t cnt = 1;
|
||||
|
||||
while (task_run_flag) {
|
||||
ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("Write timeout count: %d\n", cnt++);
|
||||
}
|
||||
}
|
||||
|
||||
free(send_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void i2s_reconfig_task(void *args) {
|
||||
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
|
||||
i2s_chan_info_t chan_info;
|
||||
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
|
||||
i2s_chan_handle_t rx_handle = chan_info.pair_chan;
|
||||
int cnt = 1;
|
||||
|
||||
while (task_run_flag) {
|
||||
/* Reconfig the slot while reading / writing */
|
||||
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
printf("[%d] Reconfiguring the slot...\n", cnt);
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &slot_cfg));
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &slot_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
/* Reconfig the clock while reading / writing */
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE / 2);
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
printf("[%d] Reconfiguring the clock...\n", cnt);
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &clk_cfg));
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &clk_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
/* Reconfig the gpio while reading / writing */
|
||||
i2s_std_gpio_config_t gpio_cfg = {
|
||||
.mclk = MASTER_MCK_IO,
|
||||
.bclk = MASTER_WS_IO,
|
||||
.ws = MASTER_BCK_IO,
|
||||
.dout = DATA_IN_IO,
|
||||
.din = DATA_IN_IO,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
printf("[%d] Reconfiguring the gpio...\n", cnt);
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(tx_handle, &gpio_cfg));
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(rx_handle, &gpio_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_thread_concurrent_safety_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = MASTER_MCK_IO,
|
||||
.bclk = MASTER_BCK_IO,
|
||||
.ws = MASTER_WS_IO,
|
||||
.dout = DATA_OUT_IO,
|
||||
.din = DATA_OUT_IO,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
/* Enable the channels before creating reading/writing task*/
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
task_run_flag = true;
|
||||
/* reading task to keep reading */
|
||||
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL);
|
||||
/* writing task to keep writing */
|
||||
xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL);
|
||||
/* reconfig taks to reconfigurate the settings every 200 ms */
|
||||
xTaskCreate(i2s_reconfig_task, "i2s_reconfig_task", 4096, tx_handle, 6, NULL);
|
||||
|
||||
/* Wait 3 seconds to see if any failures occur */
|
||||
vTaskDelay(pdMS_TO_TICKS(4000));
|
||||
|
||||
/* Stop those three tasks */
|
||||
task_run_flag = false;
|
||||
|
||||
/* Wait for the three thread deleted */
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
/* Disable the channels, they will keep waiting until the current reading / writing finished */
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
/* Delete the channels */
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
static uint32_t get_start_index(uint16_t *buf, uint32_t len, uint32_t start_val)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buf[i] == start_val) {
|
||||
printf("%d %d %d %d %d %d %d %d\n",
|
||||
buf[i], buf[i+1], buf[i+2], buf[i+3],
|
||||
buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test mono and stereo mode of I2S by loopback
|
||||
* @note Only rx channel distinguish left mono and right mono, tx channel does not
|
||||
* @note 1. Check switch mono/stereo by 'i2s_set_clk'
|
||||
* 2. Check rx right mono and left mono (requiring tx works in stereo mode)
|
||||
* 3. Check tx mono (requiring rx works in stereo mode)
|
||||
*/
|
||||
TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
|
||||
{
|
||||
#define WRITE_BUF_LEN 2000
|
||||
#define READ_BUF_LEN 4000
|
||||
#define RETEY_TIMES 3
|
||||
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = 8;
|
||||
chan_cfg.dma_frame_num = 128;
|
||||
i2s_std_config_t tx_std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
// In stereo mode
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = MASTER_MCK_IO,
|
||||
.bclk = MASTER_BCK_IO,
|
||||
.ws = MASTER_WS_IO,
|
||||
.dout = DATA_OUT_IO,
|
||||
.din = DATA_OUT_IO,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_std_config_t rx_std_cfg = tx_std_cfg;
|
||||
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
|
||||
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
|
||||
|
||||
/* TX channel basic test */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &rx_std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &tx_std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
uint16_t *w_buf = calloc(1, WRITE_BUF_LEN);
|
||||
uint16_t *r_buf = calloc(1, READ_BUF_LEN);
|
||||
size_t w_bytes = 0;
|
||||
size_t r_bytes = 0;
|
||||
uint32_t index = 0;
|
||||
uint32_t retry = 0;
|
||||
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
|
||||
w_buf[n] = n%100;
|
||||
}
|
||||
|
||||
/* rx right mono test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x01[R] 0x03[R] ... */
|
||||
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 50); retry++) {
|
||||
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* The data of tx/rx channels are flipped on ESP32 */
|
||||
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
|
||||
int16_t temp = r_buf[n];
|
||||
r_buf[n] = r_buf[n+1];
|
||||
r_buf[n+1] = temp;
|
||||
}
|
||||
#endif
|
||||
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
|
||||
}
|
||||
printf("Data start index: %d\n", index);
|
||||
TEST_ASSERT(index < READ_BUF_LEN / 2 - 50);
|
||||
for (int16_t j = 1; j < 100; j += 2) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j);
|
||||
}
|
||||
printf("rx right mono test passed\n");
|
||||
|
||||
/* rx left mono test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x00[R] 0x02[R] ... */
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_LEFT;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 50); retry++) {
|
||||
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* The data of tx/rx channels are flipped on ESP32 */
|
||||
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
|
||||
int16_t temp = r_buf[n];
|
||||
r_buf[n] = r_buf[n+1];
|
||||
r_buf[n+1] = temp;
|
||||
}
|
||||
#endif
|
||||
index = get_start_index(r_buf, READ_BUF_LEN / 2, 2);
|
||||
}
|
||||
printf("Data start index: %d\n", index);
|
||||
TEST_ASSERT(index < READ_BUF_LEN / 2 - 50);
|
||||
for (int16_t j = 2; j < 100; j += 2) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j);
|
||||
}
|
||||
printf("rx left mono test passed\n");
|
||||
|
||||
/* tx/rx stereo test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;
|
||||
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT_RIGHT;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 100); retry++) {
|
||||
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
|
||||
}
|
||||
|
||||
printf("Data start index: %d\n", index);
|
||||
TEST_ASSERT(index < READ_BUF_LEN / 2 - 100);
|
||||
for (int16_t j = 1; j < 100; j ++) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[index++], j); // receive all number
|
||||
}
|
||||
printf("tx/rx stereo test passed\n");
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32 // the 16 bit channel sequence on ESP32 is incorrect
|
||||
/* tx mono rx stereo test
|
||||
* tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ...
|
||||
* rx receive: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ... */
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
i2s_std_slot_config_t std_slot = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_slot));
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
for (retry = 0, index = READ_BUF_LEN / 2; (retry < RETEY_TIMES) && (index >= READ_BUF_LEN / 2 - 100); retry++) {
|
||||
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
index = get_start_index(r_buf, READ_BUF_LEN / 2, 1);
|
||||
}
|
||||
printf("Data start index: %d\n", index);
|
||||
TEST_ASSERT(index < READ_BUF_LEN / 2 - 200);
|
||||
for (int16_t j = 1; j < 100; j ++) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[index], j);
|
||||
index += 2;
|
||||
}
|
||||
printf("tx mono rx stereo test passed\n");
|
||||
#endif
|
||||
|
||||
free(w_buf);
|
||||
free(r_buf);
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_memory_leak_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
/* The first operation will always take some memory */
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
|
||||
std_cfg.clk_cfg.sample_rate_hz = 44100;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
|
||||
int memory_left = esp_get_free_heap_size();
|
||||
printf("\r\nHeap size before: %d\n", memory_left);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
|
||||
std_cfg.clk_cfg.sample_rate_hz = 44100;
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
TEST_ASSERT(memory_left == esp_get_free_heap_size());
|
||||
}
|
||||
printf("\r\nHeap size after: %d\n", esp_get_free_heap_size());
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_loopback_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
i2s_test_io_config(I2S_TEST_MODE_LOOPBACK);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
i2s_read_write_test(tx_handle, rx_handle);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
#if SOC_I2S_NUM > 1
|
||||
TEST_CASE("I2S_master_write_slave_read_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
|
||||
|
||||
i2s_std_config_t std_mst_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
i2s_std_config_t std_slv_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_mst_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_slv_cfg));
|
||||
i2s_test_io_config(I2S_TEST_MODE_MASTER_TO_SLAVE);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
i2s_read_write_test(tx_handle, rx_handle);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_master_read_slave_write_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
|
||||
i2s_std_config_t std_mst_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
i2s_std_config_t std_slv_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_slv_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_mst_cfg));
|
||||
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
|
||||
i2s_read_write_test(tx_handle, rx_handle);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*------------------------------ Clock Test --------------------------------*/
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
#define TEST_I2S_PERIOD_MS 100
|
||||
static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_config_t* clk_cfg)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(rx_chan);
|
||||
TEST_ASSERT_NOT_NULL(clk_cfg);
|
||||
|
||||
/* Prepare configuration for the PCNT unit */
|
||||
pcnt_unit_handle_t pcnt_unit = NULL;
|
||||
pcnt_channel_handle_t pcnt_chan = NULL;
|
||||
|
||||
pcnt_unit_config_t unit_config = {
|
||||
.high_limit = (int16_t)0x7fff,
|
||||
.low_limit = (int16_t)0x8000,
|
||||
};
|
||||
pcnt_chan_config_t chan_config = {
|
||||
.edge_gpio_num = MASTER_WS_IO,
|
||||
.level_gpio_num = -1,
|
||||
};
|
||||
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
|
||||
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
|
||||
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
|
||||
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
|
||||
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
|
||||
|
||||
// Reconfig GPIO signal
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
|
||||
|
||||
// Test common sample rate
|
||||
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 64000, 88200, 96000,
|
||||
128000, 144000, 196000};
|
||||
int real_pulse = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int expt_pulse = (int)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
|
||||
clk_cfg->sample_rate_hz = test_freq[i];
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_chan, clk_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_chan));
|
||||
vTaskDelay(1); // Waiting for hardware totally started
|
||||
// pcnt will count the pulse number on WS signal in 100ms
|
||||
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
|
||||
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
|
||||
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
|
||||
printf("[%d Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_chan));
|
||||
// Check if the error between real pulse number and expected pulse number is within 1%
|
||||
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
|
||||
}
|
||||
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
|
||||
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_default_PLL_clock_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
|
||||
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
TEST_CASE("I2S_APLL_clock_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
|
||||
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
#endif // SOC_PCNT_SUPPORTED
|
||||
|
||||
static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
|
||||
{
|
||||
int *cnt = (int *)user_ctx;
|
||||
(*cnt)++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("I2S_package_lost_test", "[i2s]")
|
||||
{
|
||||
/* Steps of calculate appropriate parameters of I2S buffer:
|
||||
* Known by user: sample_rate = 144k, data_bit_width = 32, slot_num = 2, polling_cycle = 10 ms
|
||||
* 1. dma_buffer_size = dma_frame_num * slot_num * data_bit_width / 8 <= 4092
|
||||
* dma_frame_num <= 511, dma_frame_num is as big as possible.
|
||||
* interrupt_interval = dma_frame_num / sample_rate = 3.549 ms
|
||||
* 2. dma_desc_num > polling_cycle / interrupt_interval = cell(2.818) = 3
|
||||
* 3. recv_buffer_size > dma_desc_num * dma_buffer_size = 3 * 4092 = 12276 bytes */
|
||||
#define TEST_RECV_BUF_LEN 12276
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = 3;
|
||||
chan_cfg.dma_frame_num = 511;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
i2s_event_callbacks_t cbs = {
|
||||
.on_recv = NULL,
|
||||
.on_recv_q_ovf = i2s_rx_queue_overflow_callback,
|
||||
.on_sent = NULL,
|
||||
.on_send_q_ovf = NULL,
|
||||
};
|
||||
int count = 0;
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(rx_handle, &cbs, &count));
|
||||
|
||||
uint32_t test_freq[] = {16000, 32000, 48000, 64000, 96000, 128000, 144000};
|
||||
uint32_t test_num = sizeof(test_freq) / sizeof(uint32_t);
|
||||
uint8_t *data = (uint8_t *)calloc(TEST_RECV_BUF_LEN, sizeof(uint8_t));
|
||||
size_t bytes_read = 0;
|
||||
int i;
|
||||
for (i = 0; i < test_num; i++) {
|
||||
printf("Testing %d Hz sample rate\n", test_freq[i]);
|
||||
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
|
||||
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
|
||||
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &std_cfg.clk_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
for (int j = 0; j < 10; j++) {
|
||||
TEST_ESP_OK(i2s_channel_read(rx_handle, (void *)data, TEST_RECV_BUF_LEN, &bytes_read, portMAX_DELAY));
|
||||
// To simulate 10ms delay caused by other statements like data process
|
||||
vTaskDelay(1);
|
||||
}
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
if (count > 0) {
|
||||
printf("package lost detected at %d Hz\n", test_freq[i]);
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
finish:
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
free(data);
|
||||
// Test failed if package lost within 96000
|
||||
TEST_ASSERT(i == test_num);
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_private/i2s_platform.h"
|
||||
#include "test_inc/test_i2s.h"
|
||||
|
||||
#if CONFIG_I2S_ISR_IRAM_SAFE
|
||||
|
||||
#define GET_DMA_BUFFERS_BY_OFFSET(base_addr, offset) (uint8_t **)(*((uint32_t *)base_addr + offset))
|
||||
|
||||
static bool IRAM_ATTR test_i2s_tx_done_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
|
||||
{
|
||||
int *is_triggered = (int *)user_ctx;
|
||||
if (*(uint8_t *)(event->data) != 0) {
|
||||
*is_triggered = 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR test_i2s_iram_write(i2s_chan_handle_t tx_handle)
|
||||
{
|
||||
// Get the DMA buf pointer via the offset of 'bufs' field in i2s_channel_t struct
|
||||
size_t offset = i2s_platform_get_dma_buffer_offset() / sizeof(uint32_t); // Get the offset and transfer to unit 'uint32_t' (i.e. 4 bytes)
|
||||
uint8_t **dma_bufs = GET_DMA_BUFFERS_BY_OFFSET(tx_handle, offset);
|
||||
|
||||
// disable cache and non-iram ISR handlers
|
||||
spi_flash_guard_get()->start();
|
||||
// write data into dma buffer directly, the data in dma buffer will be sent automatically
|
||||
for (int i=0; i < 100; i++) {
|
||||
dma_bufs[0][i] = i + 1;
|
||||
}
|
||||
// enable cache and non-iram ISR handlers
|
||||
spi_flash_guard_get()->end();
|
||||
}
|
||||
|
||||
TEST_CASE("i2s_iram_interrupt_safe", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_chan = NULL;
|
||||
i2s_chan_handle_t rx_chan = NULL;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = 6;
|
||||
chan_cfg.dma_frame_num = 200;
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = MASTER_MCK_IO,
|
||||
.bclk = MASTER_BCK_IO,
|
||||
.ws = MASTER_WS_IO,
|
||||
.dout = DATA_OUT_IO,
|
||||
.din = DATA_OUT_IO, // same gpio to loopback
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
|
||||
int is_triggerred = 0;
|
||||
i2s_event_callbacks_t cbs = {
|
||||
.on_recv = NULL,
|
||||
.on_recv_q_ovf = NULL,
|
||||
.on_sent = test_i2s_tx_done_callback,
|
||||
.on_send_q_ovf = NULL,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx_chan, &cbs, &is_triggerred));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_chan));
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_chan));
|
||||
|
||||
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
|
||||
TEST_ASSERT(recv_buf != NULL);
|
||||
size_t r_bytes;
|
||||
int i = 0;
|
||||
test_i2s_iram_write(tx_chan);
|
||||
for (int retry = 0; retry < 3; retry++) {
|
||||
i2s_channel_read(rx_chan, recv_buf, 2000, &r_bytes, pdMS_TO_TICKS(1000));
|
||||
for (i = 0; i < 2000 - 100; i++) {
|
||||
if (recv_buf[i] != 0) {
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
finish:
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_chan));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_chan));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_chan));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_chan));
|
||||
|
||||
TEST_ASSERT(i < (2000 - 100));
|
||||
for (int j = 1; j <= 100; j++) {
|
||||
TEST_ASSERT_EQUAL_UINT8(recv_buf[i++], j);
|
||||
}
|
||||
TEST_ASSERT(is_triggerred);
|
||||
free(recv_buf);
|
||||
}
|
||||
|
||||
#endif // CONFIG_PCNT_ISR_IRAM_SAFE
|
24
components/driver/test_apps/i2s_test_apps/i2s/pytest_i2s.py
Normal file
24
components/driver/test_apps/i2s_test_apps/i2s/pytest_i2s.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'iram_safe',
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_i2s(dut: Dut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output()
|
@@ -0,0 +1,5 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_I2S_ISR_IRAM_SAFE=y
|
||||
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
@@ -0,0 +1,5 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
@@ -0,0 +1,5 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2s_adc_dac_test)
|
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
@@ -0,0 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_i2s_adc.c"
|
||||
"test_i2s_dac.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
WHOLE_ARCHIVE)
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in I2S driver, the threadhold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ___ ____ ____ _____ _
|
||||
// |_ _|___ \/ ___| |_ _|__ ___| |_
|
||||
// | | __) \___ \ | |/ _ \/ __| __|
|
||||
// | | / __/ ___) | | | __/\__ \ |_
|
||||
// |___|_____|____/ |_|\___||___/\__|
|
||||
|
||||
printf(" ___ ____ ____ _____ _ \r\n");
|
||||
printf(" |_ _|___ \\/ ___| |_ _|__ ___| |_ \r\n");
|
||||
printf(" | | __) \\___ \\ | |/ _ \\/ __| __|\r\n");
|
||||
printf(" | | / __/ ___) | | | __/\\__ \\ |_ \r\n");
|
||||
printf(" |___|_____|____/ |_|\\___||___/\\__|(ADC/DAC)\r\n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -20,15 +20,14 @@
|
||||
#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"
|
||||
#include "soc/adc_periph.h"
|
||||
|
||||
/*
|
||||
* ADC DMA testcase
|
||||
*/
|
||||
#include "driver/i2s.h"
|
||||
#include "test/test_common_adc.h"
|
||||
|
||||
//i2s number
|
||||
#define EXAMPLE_I2S_NUM (0)
|
||||
@@ -47,6 +46,100 @@
|
||||
//I2S built-in ADC channel
|
||||
#define I2S_ADC_CHANNEL ADC1_CHANNEL_4
|
||||
|
||||
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
|
||||
|
||||
static void adc_fake_tie_middle(adc_unit_t adc_unit, adc_channel_t channel)
|
||||
{
|
||||
gpio_num_t gpio_num = 0;
|
||||
if (adc_unit == ADC_UNIT_1) {
|
||||
gpio_num = ADC_GET_IO_NUM(0, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
|
||||
}
|
||||
if (adc_unit == ADC_UNIT_2) {
|
||||
gpio_num = ADC_GET_IO_NUM(1, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void adc_fake_tie_high(adc_unit_t adc_unit, adc_channel_t channel)
|
||||
{
|
||||
gpio_num_t gpio_num = 0;
|
||||
if (adc_unit == ADC_UNIT_1) {
|
||||
gpio_num = ADC_GET_IO_NUM(0, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
|
||||
}
|
||||
if (adc_unit == ADC_UNIT_2) {
|
||||
gpio_num = ADC_GET_IO_NUM(1, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void adc_fake_tie_low(adc_unit_t adc_unit, adc_channel_t channel)
|
||||
{
|
||||
gpio_num_t gpio_num = 0;
|
||||
if (adc_unit == ADC_UNIT_1) {
|
||||
gpio_num = ADC_GET_IO_NUM(0, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
|
||||
}
|
||||
if (adc_unit == ADC_UNIT_2) {
|
||||
gpio_num = ADC_GET_IO_NUM(1, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
|
||||
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void adc_io_normal(adc_unit_t adc_unit, adc_channel_t channel)
|
||||
{
|
||||
gpio_num_t gpio_num = 0;
|
||||
if (adc_unit == ADC_UNIT_1) {
|
||||
gpio_num = ADC_GET_IO_NUM(0, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
|
||||
}
|
||||
if (adc_unit == ADC_UNIT_2) {
|
||||
gpio_num = ADC_GET_IO_NUM(1, channel);
|
||||
TEST_ESP_OK(rtc_gpio_init(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
|
||||
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
|
||||
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
|
||||
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2S ADC/DAC mode init.
|
||||
*/
|
||||
@@ -125,7 +218,7 @@ static void adc_dma_read(uint8_t *buf, int length)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ADC DMA read", "[adc dma]")
|
||||
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));
|
@@ -20,7 +20,6 @@
|
||||
#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"
|
||||
@@ -132,7 +131,7 @@ 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]")
|
||||
TEST_CASE("DAC_DMA_output", "[dac]")
|
||||
{
|
||||
size_t bytes_written;
|
||||
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
|
@@ -0,0 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_i2s_adc_dac(dut: Dut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output()
|
@@ -0,0 +1,9 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_I2S_ISR_IRAM_SAFE=y
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
@@ -0,0 +1,5 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2s_legacy_test)
|
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
@@ -0,0 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_legacy_i2s.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_INCLUDE_DIRS "../../"
|
||||
WHOLE_ARCHIVE)
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in I2S driver, the threadhold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ___ ____ ____ _____ _
|
||||
// |_ _|___ \/ ___| |_ _|__ ___| |_
|
||||
// | | __) \___ \ | |/ _ \/ __| __|
|
||||
// | | / __/ ___) | | | __/\__ \ |_
|
||||
// |___|_____|____/ |_|\___||___/\__|
|
||||
|
||||
printf(" ___ ____ ____ _____ _ \r\n");
|
||||
printf(" |_ _|___ \\/ ___| |_ _|__ ___| |_ \r\n");
|
||||
printf(" | | __) \\___ \\ | |/ _ \\/ __| __|\r\n");
|
||||
printf(" | | / __/ ___) | | | __/\\__ \\ |_ \r\n");
|
||||
printf(" |___|_____|____/ |_|\\___||___/\\__| (legacy)\r\n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
@@ -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,74 +11,34 @@
|
||||
* 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 "hal/i2s_hal.h"
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "unity.h"
|
||||
#include "math.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#if SOC_I2S_SUPPORTED
|
||||
#include "driver/i2s.h"
|
||||
|
||||
#define SAMPLE_RATE (36000)
|
||||
#define SAMPLE_BITS (16)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define MASTER_BCK_IO 15
|
||||
#define MASTER_WS_IO 25
|
||||
#define SLAVE_BCK_IO 19
|
||||
#define SLAVE_WS_IO 26
|
||||
#define DATA_IN_IO 21
|
||||
#define DATA_OUT_IO 22
|
||||
#define ADC1_CHANNEL_4_IO 32
|
||||
#define I2S0_DATA_OUT_IDX I2S0O_DATA_OUT23_IDX
|
||||
#define I2S0_DATA_IN_IDX I2S0I_DATA_IN15_IDX
|
||||
#define I2S1_DATA_OUT_IDX I2S1O_DATA_OUT23_IDX
|
||||
#define I2S1_DATA_IN_IDX I2S1I_DATA_IN15_IDX
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define MASTER_BCK_IO 15
|
||||
#define MASTER_WS_IO 28
|
||||
#define SLAVE_BCK_IO 19
|
||||
#define SLAVE_WS_IO 26
|
||||
#define DATA_IN_IO 21
|
||||
#define DATA_OUT_IO 20
|
||||
#define I2S0_DATA_OUT_IDX I2S0O_DATA_OUT23_IDX
|
||||
#define I2S0_DATA_IN_IDX I2S0I_DATA_IN15_IDX
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
// TODO: change pins
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 14
|
||||
#define SLAVE_WS_IO 15
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#define I2S0_DATA_OUT_IDX I2SO_SD_OUT_IDX
|
||||
#define I2S0_DATA_IN_IDX I2SI_SD_IN_IDX
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 14
|
||||
#define SLAVE_WS_IO 15
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#define I2S0_DATA_OUT_IDX I2S0O_SD_OUT_IDX
|
||||
#define I2S0_DATA_IN_IDX I2S0I_SD_IN_IDX
|
||||
#define I2S1_DATA_OUT_IDX I2S1O_SD_OUT_IDX
|
||||
#define I2S1_DATA_IN_IDX I2S1I_SD_IN_IDX
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
#include "driver/pulse_cnt.h"
|
||||
#include "soc/pcnt_periph.h"
|
||||
#endif
|
||||
|
||||
#include "test_inc/test_i2s.h"
|
||||
|
||||
#define PERCENT_DIFF 0.0001
|
||||
|
||||
#define I2S_TEST_MODE_SLAVE_TO_MAXTER 0
|
||||
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
|
||||
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
|
||||
#define I2S_TEST_MODE_LOOPBACK 2
|
||||
|
||||
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loopback
|
||||
// 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)
|
||||
{
|
||||
@@ -93,33 +53,33 @@ static void i2s_test_io_config(int mode)
|
||||
|
||||
switch (mode) {
|
||||
#if SOC_I2S_NUM > 1
|
||||
case I2S_TEST_MODE_SLAVE_TO_MAXTER: {
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0I_BCK_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1O_BCK_IN_IDX, 0);
|
||||
case I2S_TEST_MODE_SLAVE_TO_MASTER: {
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0I_WS_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1O_WS_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S1_DATA_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0_DATA_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2S_TEST_MODE_MASTER_TO_SLAVE: {
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, I2S0O_BCK_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, I2S1I_BCK_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, I2S0O_WS_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, I2S1I_WS_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0_DATA_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S1_DATA_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case I2S_TEST_MODE_LOOPBACK: {
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, I2S0_DATA_OUT_IDX, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, I2S0_DATA_IN_IDX, 0);
|
||||
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -130,13 +90,81 @@ static void i2s_test_io_config(int mode)
|
||||
}
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
#define ADC1_CHANNEL_4_IO 32
|
||||
/* Only ESP32 need I2S adc/dac test */
|
||||
TEST_CASE("I2S_adc_test", "[i2s_legacy]")
|
||||
{
|
||||
// init I2S ADC
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = SAMPLE_BITS,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.intr_alloc_flags = 0,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 1024,
|
||||
.use_apll = 0,
|
||||
};
|
||||
// install and start I2S driver
|
||||
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
|
||||
// init ADC pad
|
||||
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_4);
|
||||
// enable adc sampling, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11 hard-coded in adc_i2s_mode_init
|
||||
i2s_adc_enable(I2S_NUM_0);
|
||||
// init read buffer
|
||||
uint16_t *i2sReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
|
||||
size_t bytesRead;
|
||||
|
||||
for (int loop = 0; loop < 10; loop++) {
|
||||
for (int level = 0; level <= 1; level++) {
|
||||
if (level == 0) {
|
||||
gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLDOWN_ONLY);
|
||||
} else {
|
||||
gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLUP_ONLY);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
// read data from adc, will block until buffer is full
|
||||
i2s_read(I2S_NUM_0, (void *)i2sReadBuffer, 1024 * sizeof(uint16_t), &bytesRead, portMAX_DELAY);
|
||||
|
||||
// calc average
|
||||
int64_t adcSumValue = 0;
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
adcSumValue += i2sReadBuffer[i] & 0xfff;
|
||||
}
|
||||
int adcAvgValue = adcSumValue / 1024;
|
||||
printf("adc average val: %d\n", adcAvgValue);
|
||||
|
||||
if (level == 0) {
|
||||
if (adcAvgValue > 100) {
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
TEST_ASSERT_LESS_THAN(100, adcAvgValue);
|
||||
}
|
||||
} else {
|
||||
if (adcAvgValue < 4000) {
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
TEST_ASSERT_GREATER_THAN(4000, adcAvgValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* i2s initialize test
|
||||
* 1. i2s_driver_install
|
||||
* 2. i2s_set_pin
|
||||
*/
|
||||
TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]")
|
||||
TEST_CASE("I2S_basic_driver_installation_uninstallation_and_settings_test", "[i2s_legacy]")
|
||||
{
|
||||
// dac, adc i2s
|
||||
i2s_config_t i2s_config = {
|
||||
@@ -174,7 +202,7 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]")
|
||||
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
|
||||
|
||||
//error param test
|
||||
TEST_ASSERT(i2s_driver_install(I2S_NUM_MAX, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
|
||||
TEST_ASSERT(i2s_driver_install(SOC_I2S_NUM, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
|
||||
TEST_ASSERT(i2s_driver_install(I2S_NUM_0, NULL, 0, NULL) == ESP_ERR_INVALID_ARG);
|
||||
i2s_config.dma_desc_num = 1;
|
||||
TEST_ASSERT(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG);
|
||||
@@ -183,15 +211,24 @@ 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]")
|
||||
/**
|
||||
* @brief Test mono and stereo mode of I2S by loopback
|
||||
* @note Only rx channel distinguish left mono and right mono, tx channel does not
|
||||
* @note 1. Check switch mono/stereo by 'i2s_set_clk'
|
||||
* 2. Check rx right mono and left mono (requiring tx works in stereo mode)
|
||||
* 3. Check tx mono (requiring rx works in stereo mode)
|
||||
*/
|
||||
TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s_legacy]")
|
||||
{
|
||||
#define WRITE_BUF_LEN 2000
|
||||
#define READ_BUF_LEN 4000
|
||||
// master driver installed and send data
|
||||
i2s_config_t master_i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX,
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = SAMPLE_BITS,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 100,
|
||||
.use_apll = 0,
|
||||
@@ -212,51 +249,152 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]")
|
||||
.data_out_num = DATA_OUT_IO,
|
||||
.data_in_num = DATA_IN_IO
|
||||
};
|
||||
/* Install I2S in duplex mode */
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
|
||||
/* Config TX as stereo channel directly, because legacy driver can't support config tx&rx separately */
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
|
||||
#else
|
||||
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
|
||||
#endif
|
||||
i2s_ll_tx_enable_mono_mode(&I2S0, false);
|
||||
|
||||
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
|
||||
i2s_test_io_config(I2S_TEST_MODE_LOOPBACK);
|
||||
printf("\r\nheap size: %d\n", esp_get_free_heap_size());
|
||||
|
||||
uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400);
|
||||
size_t i2s_bytes_write = 0;
|
||||
size_t bytes_read = 0;
|
||||
int length = 0;
|
||||
uint8_t *i2s_read_buff = (uint8_t *)malloc(sizeof(uint8_t) * 10000);
|
||||
TEST_ESP_OK(i2s_start(I2S_NUM_0));
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
data_wr[i] = i + 1;
|
||||
uint16_t *w_buf = calloc(1, WRITE_BUF_LEN);
|
||||
uint16_t *r_buf = calloc(1, READ_BUF_LEN);
|
||||
size_t w_bytes = 0;
|
||||
size_t r_bytes = 0;
|
||||
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
|
||||
w_buf[n] = n%100;
|
||||
}
|
||||
int flag = 0; // break loop flag
|
||||
int end_position = 0;
|
||||
// write data to slave
|
||||
i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t) * 400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS);
|
||||
while (!flag) {
|
||||
if (length >= 10000 - 500) {
|
||||
/* rx right mono test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x01[R] 0x03[R] ... */
|
||||
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* The data of tx/rx channels are flipped on ESP32 */
|
||||
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
|
||||
int16_t temp = r_buf[n];
|
||||
r_buf[n] = r_buf[n+1];
|
||||
r_buf[n+1] = temp;
|
||||
}
|
||||
#endif
|
||||
int i = 0;
|
||||
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
|
||||
if (r_buf[i] == 1) {
|
||||
printf("%d %d %d %d\n%d %d %d %d\n",
|
||||
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
|
||||
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
|
||||
break;
|
||||
}
|
||||
i2s_read(I2S_NUM_0, i2s_read_buff + length, sizeof(uint8_t) * 500, &bytes_read, 1000 / portMAX_DELAY);
|
||||
if (bytes_read > 0) {
|
||||
for (int i = length; i < length + bytes_read; i++) {
|
||||
if (i2s_read_buff[i] == 100) {
|
||||
flag = 1;
|
||||
end_position = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Data start index: %d\n", i);
|
||||
TEST_ASSERT(i < READ_BUF_LEN / 2 - 50);
|
||||
for (int16_t j = 1; j < 100; j += 2) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
|
||||
}
|
||||
printf("rx right mono test passed\n");
|
||||
|
||||
/* tx/rx stereo test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */
|
||||
TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, SAMPLE_BITS, I2S_CHANNEL_STEREO));
|
||||
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
|
||||
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
|
||||
if (r_buf[i] == 1) {
|
||||
printf("%d %d %d %d\n%d %d %d %d\n",
|
||||
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
|
||||
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
|
||||
break;
|
||||
}
|
||||
length = length + bytes_read;
|
||||
}
|
||||
// test the read data right or not
|
||||
for (int i = end_position - 99; i <= end_position; i++) {
|
||||
TEST_ASSERT_EQUAL_UINT8((i - end_position + 100), *(i2s_read_buff + i));
|
||||
printf("Data start index: %d\n", i);
|
||||
TEST_ASSERT(i < READ_BUF_LEN / 2 - 100);
|
||||
TEST_ASSERT(i % 2);
|
||||
for (int16_t j = 1; j < 100; j ++) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); // receive all number
|
||||
}
|
||||
free(data_wr);
|
||||
free(i2s_read_buff);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
printf("tx/rx stereo test passed\n");
|
||||
|
||||
/* tx mono rx right mono test
|
||||
* tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ...
|
||||
* rx receive: 0x01[R] 0x02[R] ... */
|
||||
TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO));
|
||||
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
|
||||
for (i = 0; i < READ_BUF_LEN / 2; i++) {
|
||||
if (r_buf[i] == 1) {
|
||||
printf("%d %d %d %d\n%d %d %d %d\n",
|
||||
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
|
||||
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("Data start index: %d\n", i);
|
||||
TEST_ASSERT(i < READ_BUF_LEN / 2 - 100);
|
||||
for (int16_t j = 1; j < 100; j ++) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
|
||||
}
|
||||
printf("tx/rx mono test passed\n");
|
||||
|
||||
/* Reinstalling I2S to test rx left mono */
|
||||
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
|
||||
master_i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_stop(I2S_NUM_0));
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT, true);
|
||||
#else
|
||||
i2s_ll_tx_select_slot(&I2S0, I2S_STD_SLOT_LEFT_RIGHT);
|
||||
#endif
|
||||
i2s_ll_tx_enable_mono_mode(&I2S0, false);
|
||||
|
||||
TEST_ESP_OK(i2s_start(I2S_NUM_0));
|
||||
|
||||
/* rx left mono test
|
||||
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
|
||||
* rx receive: 0x00[R] 0x02[R] ... */
|
||||
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
|
||||
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* The data of tx/rx channels are flipped on ESP32 */
|
||||
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
|
||||
int16_t temp = r_buf[n];
|
||||
r_buf[n] = r_buf[n+1];
|
||||
r_buf[n+1] = temp;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; (i < READ_BUF_LEN / 2); i++) {
|
||||
if (r_buf[i] == 2) {
|
||||
printf("%d %d %d %d\n%d %d %d %d\n",
|
||||
r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3],
|
||||
r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("Data start index: %d\n", i);
|
||||
TEST_ASSERT(i < READ_BUF_LEN / 2 - 50);
|
||||
for (int16_t j = 2; j < 100; j += 2) {
|
||||
TEST_ASSERT_EQUAL_INT16(r_buf[i++], j);
|
||||
}
|
||||
printf("rx left mono test passed\n");
|
||||
|
||||
free(w_buf);
|
||||
free(r_buf);
|
||||
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
TEST_CASE("I2S TDM Loopback test(master tx and rx)", "[i2s]")
|
||||
TEST_CASE("I2S_TDM_loopback_test_with_master_tx_and_rx", "[i2s_legacy]")
|
||||
{
|
||||
// master driver installed and send data
|
||||
i2s_config_t master_i2s_config = {
|
||||
@@ -325,7 +463,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_with_master_tx_and_slave_rx", "[i2s_legacy]")
|
||||
{
|
||||
// master driver installed and send data
|
||||
i2s_config_t master_i2s_config = {
|
||||
@@ -429,7 +567,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 = {
|
||||
@@ -460,7 +598,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]")
|
||||
};
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
|
||||
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MAXTER);
|
||||
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
|
||||
printf("\r\nheap size: %d\n", esp_get_free_heap_size());
|
||||
|
||||
i2s_config_t slave_i2s_config = {
|
||||
@@ -492,7 +630,7 @@ TEST_CASE("I2S write and read test(master rx and slave tx)", "[i2s]")
|
||||
// slave driver installed and receive data
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_1, &slave_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_set_pin(I2S_NUM_1, &slave_pin_config));
|
||||
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MAXTER);
|
||||
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
|
||||
printf("\r\nheap size: %d\n", esp_get_free_heap_size());
|
||||
|
||||
uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400);
|
||||
@@ -535,7 +673,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 +723,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,
|
||||
@@ -641,76 +779,8 @@ TEST_CASE("I2S APLL clock variation test", "[i2s]")
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_ADC
|
||||
/* Only ESP32 need I2S adc/dac test */
|
||||
TEST_CASE("I2S adc test", "[i2s]")
|
||||
{
|
||||
// init I2S ADC
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = SAMPLE_BITS,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.intr_alloc_flags = 0,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 1024,
|
||||
.use_apll = 0,
|
||||
};
|
||||
// install and start I2S driver
|
||||
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
|
||||
// init ADC pad
|
||||
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_4);
|
||||
// enable adc sampling, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11 hard-coded in adc_i2s_mode_init
|
||||
i2s_adc_enable(I2S_NUM_0);
|
||||
// init read buffer
|
||||
uint16_t *i2sReadBuffer = (uint16_t *)calloc(1024, sizeof(uint16_t));
|
||||
size_t bytesRead;
|
||||
|
||||
for (int loop = 0; loop < 10; loop++) {
|
||||
for (int level = 0; level <= 1; level++) {
|
||||
if (level == 0) {
|
||||
gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLDOWN_ONLY);
|
||||
} else {
|
||||
gpio_set_pull_mode(ADC1_CHANNEL_4_IO, GPIO_PULLUP_ONLY);
|
||||
}
|
||||
vTaskDelay(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);
|
||||
|
||||
// calc average
|
||||
int64_t adcSumValue = 0;
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
adcSumValue += i2sReadBuffer[i] & 0xfff;
|
||||
}
|
||||
int adcAvgValue = adcSumValue / 1024;
|
||||
printf("adc average val: %d\n", adcAvgValue);
|
||||
|
||||
if (level == 0) {
|
||||
if (adcAvgValue > 100) {
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
TEST_ASSERT_LESS_THAN(100, adcAvgValue);
|
||||
}
|
||||
} else {
|
||||
if (adcAvgValue < 4000) {
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
TEST_ASSERT_GREATER_THAN(4000, adcAvgValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i2s_adc_disable(I2S_NUM_0);
|
||||
free(i2sReadBuffer);
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_DAC
|
||||
TEST_CASE("I2S dac test", "[i2s]")
|
||||
TEST_CASE("I2S_dac_test", "[i2s_legacy]")
|
||||
{
|
||||
// dac, adc i2s
|
||||
i2s_config_t i2s_config = {
|
||||
@@ -734,4 +804,111 @@ TEST_CASE("I2S dac test", "[i2s]")
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //SOC_I2S_SUPPORTED
|
||||
/*------------------------------ Clock Test --------------------------------*/
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
|
||||
#define TEST_I2S_PERIOD_MS 100
|
||||
|
||||
static void i2s_test_common_sample_rate(i2s_port_t id)
|
||||
{
|
||||
/* Prepare configuration for the PCNT unit */
|
||||
pcnt_unit_handle_t pcnt_unit = NULL;
|
||||
pcnt_channel_handle_t pcnt_chan = NULL;
|
||||
|
||||
pcnt_unit_config_t unit_config = {
|
||||
.high_limit = (int16_t)0x7fff,
|
||||
.low_limit = (int16_t)0x8000,
|
||||
};
|
||||
pcnt_chan_config_t chan_config = {
|
||||
.edge_gpio_num = MASTER_WS_IO,
|
||||
.level_gpio_num = -1,
|
||||
};
|
||||
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
|
||||
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
|
||||
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
|
||||
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
|
||||
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
|
||||
|
||||
// Reconfig GPIO signal
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
|
||||
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
|
||||
|
||||
// Test common sample rate
|
||||
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 64000, 88200, 96000,
|
||||
128000, 144000, 196000};
|
||||
int real_pulse = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int expt_pulse = (int16_t)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
|
||||
TEST_ESP_OK(i2s_set_clk(id, test_freq[i], SAMPLE_BITS, I2S_CHANNEL_STEREO));
|
||||
vTaskDelay(1); // Waiting for hardware totally started
|
||||
// pcnt will count the pulse number on WS signal in 100ms
|
||||
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
|
||||
vTaskDelay(pdMS_TO_TICKS(TEST_I2S_PERIOD_MS));
|
||||
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
|
||||
printf("[%d Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
|
||||
// Check if the error between real pulse number and expected pulse number is within 1%
|
||||
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
|
||||
}
|
||||
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
|
||||
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
|
||||
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S clock freqency test", "[i2s_legacy]")
|
||||
{
|
||||
// master driver installed and send data
|
||||
i2s_config_t master_i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = SAMPLE_BITS,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 100,
|
||||
.use_apll = 0,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1,
|
||||
.total_chan = 2,
|
||||
.left_align = false,
|
||||
.big_edin = false,
|
||||
.bit_order_msb = false,
|
||||
.skip_msk = false
|
||||
#endif
|
||||
};
|
||||
i2s_pin_config_t master_pin_config = {
|
||||
.mck_io_num = -1,
|
||||
.bck_io_num = MASTER_BCK_IO,
|
||||
.ws_io_num = MASTER_WS_IO,
|
||||
.data_out_num = DATA_OUT_IO,
|
||||
.data_in_num = -1
|
||||
};
|
||||
/* Non-APLL test */
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
|
||||
|
||||
i2s_test_common_sample_rate(I2S_NUM_0);
|
||||
|
||||
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
|
||||
|
||||
/* APLL test */
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
master_i2s_config.use_apll = true;
|
||||
master_i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_256;
|
||||
|
||||
TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL));
|
||||
TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config));
|
||||
|
||||
i2s_test_common_sample_rate(I2S_NUM_0);
|
||||
TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SOC_PCNT_SUPPORTED
|
@@ -0,0 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_legacy_i2s(dut: Dut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output()
|
@@ -0,0 +1,6 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_I2S_ISR_IRAM_SAFE=y
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RATE (48000)
|
||||
#define SAMPLE_BITS (16)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define MASTER_MCK_IO 0
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 21
|
||||
#define SLAVE_WS_IO 22
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define MASTER_MCK_IO 0
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 14
|
||||
#define SLAVE_WS_IO 15
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define MASTER_MCK_IO 0
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 14
|
||||
#define SLAVE_WS_IO 15
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define MASTER_MCK_IO 0
|
||||
#define MASTER_BCK_IO 4
|
||||
#define MASTER_WS_IO 5
|
||||
#define SLAVE_BCK_IO 14
|
||||
#define SLAVE_WS_IO 15
|
||||
#define DATA_IN_IO 19
|
||||
#define DATA_OUT_IO 18
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -121,4 +121,4 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif // SOC_I2S_SUPPORTS_APLL
|
||||
#endif // SOC_CLK_APLL_SUPPORTED
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -152,7 +152,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
|
||||
int bus_id = -1;
|
||||
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
|
||||
if (i2s_priv_register_object(bus, i) == ESP_OK) {
|
||||
if (i2s_platform_acquire_occupation(i, "esp_lcd_panel_io_i2s") == ESP_OK) {
|
||||
bus_id = i;
|
||||
break;
|
||||
}
|
||||
@@ -185,7 +185,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
i2s_ll_tx_bypass_pcm(bus->hal.dev, true);
|
||||
i2s_ll_tx_set_slave_mod(bus->hal.dev, false);
|
||||
i2s_ll_tx_set_bits_mod(bus->hal.dev, bus_config->bus_width);
|
||||
i2s_ll_tx_select_slot(bus->hal.dev, I2S_STD_SLOT_ONLY_LEFT); // mono
|
||||
i2s_ll_tx_select_slot(bus->hal.dev, I2S_STD_SLOT_ONLY_LEFT, false); // mono
|
||||
bus->bus_width = bus_config->bus_width;
|
||||
i2s_ll_tx_enable_right_first(bus->hal.dev, true);
|
||||
#if SOC_I2S_SUPPORTS_DMA_EQUAL
|
||||
@@ -214,7 +214,7 @@ err:
|
||||
esp_intr_free(bus->intr);
|
||||
}
|
||||
if (bus->bus_id >= 0) {
|
||||
i2s_priv_deregister_object(bus->bus_id);
|
||||
i2s_platform_release_occupation(bus->bus_id);
|
||||
}
|
||||
if (bus->format_buffer) {
|
||||
free(bus->format_buffer);
|
||||
@@ -233,7 +233,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
|
||||
ESP_GOTO_ON_FALSE(bus, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty");
|
||||
int bus_id = bus->bus_id;
|
||||
i2s_priv_deregister_object(bus_id);
|
||||
i2s_platform_release_occupation(bus_id);
|
||||
esp_intr_free(bus->intr);
|
||||
if (bus->pm_lock) {
|
||||
esp_pm_lock_delete(bus->pm_lock);
|
||||
@@ -593,9 +593,9 @@ static esp_err_t i2s_lcd_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
|
||||
switch (src) {
|
||||
case LCD_CLK_SRC_PLL160M:
|
||||
bus->resolution_hz = 160000000 / LCD_PERIPH_CLOCK_PRE_SCALE;
|
||||
i2s_ll_tx_clk_set_src(bus->hal.dev, I2S_CLK_D2CLK);
|
||||
i2s_ll_tx_clk_set_src(bus->hal.dev, I2S_CLK_SRC_PLL_160M);
|
||||
#if CONFIG_PM_ENABLE
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_bus_lcd", &bus->pm_lock);
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_controller_lcd", &bus->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
|
||||
ESP_LOGD(TAG, "installed ESP_PM_APB_FREQ_MAX lock");
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "test_i80_board.h"
|
||||
|
||||
#if SOC_I2S_LCD_I80_VARIANT
|
||||
#include "driver/i2s.h"
|
||||
#include "driver/i2s_std.h"
|
||||
|
||||
TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
|
||||
{
|
||||
@@ -42,17 +42,11 @@ TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
|
||||
};
|
||||
TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
|
||||
.sample_rate = 36000,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 60,
|
||||
};
|
||||
|
||||
i2s_chan_handle_t tx_handle = NULL;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
// I2S driver won't be installed as the same I2S port has been used by LCD
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_driver_install(0, &i2s_config, 0, NULL));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
|
||||
}
|
||||
#endif // SOC_I2S_LCD_I80_VARIANT
|
||||
|
@@ -189,7 +189,7 @@ static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t fre
|
||||
adc_ll_digi_controller_clk_div(ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT);
|
||||
adc_ll_digi_clk_sel(0); //use APB
|
||||
#else
|
||||
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_D2CLK); /*!< Clock from PLL_D2_CLK(160M)*/
|
||||
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_SRC_DEFAULT); /*!< Clock from PLL_D2_CLK(160M)*/
|
||||
uint32_t bck = I2S_BASE_CLK / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_B_DEFAULT / ADC_LL_CLKM_DIV_A_DEFAULT) / 2 / freq;
|
||||
i2s_ll_set_raw_mclk_div(hal->dev, ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT);
|
||||
i2s_ll_rx_set_bck_div_num(hal->dev, bck);
|
||||
|
@@ -570,6 +570,16 @@ static inline __attribute__((always_inline)) void gpio_ll_iomux_func_sel(uint32_
|
||||
PIN_FUNC_SELECT(pin_name, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Control the pin in the IOMUX
|
||||
*
|
||||
* @param val Control value
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void gpio_ll_iomux_pin_ctrl(uint32_t val)
|
||||
{
|
||||
WRITE_PERI_REG(PIN_CTRL, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set peripheral output to an GPIO pad through the IOMUX.
|
||||
*
|
||||
|
@@ -19,7 +19,6 @@
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -43,6 +42,9 @@ extern "C" {
|
||||
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
|
||||
#define I2S_INTR_MAX (UINT32_MAX)
|
||||
|
||||
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF
|
||||
#define I2S_LL_RX_EVENT_MASK I2S_LL_EVENT_RX_EOF
|
||||
|
||||
/* I2S clock configuration structure */
|
||||
typedef struct {
|
||||
uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a)
|
||||
@@ -204,6 +206,8 @@ static inline void i2s_ll_tx_reset(i2s_dev_t *hw)
|
||||
{
|
||||
hw->conf.tx_reset = 1;
|
||||
hw->conf.tx_reset = 0;
|
||||
hw->lc_conf.out_rst = 1;
|
||||
hw->lc_conf.out_rst = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,6 +219,8 @@ static inline void i2s_ll_rx_reset(i2s_dev_t *hw)
|
||||
{
|
||||
hw->conf.rx_reset = 1;
|
||||
hw->conf.rx_reset = 0;
|
||||
hw->lc_conf.in_rst = 1;
|
||||
hw->lc_conf.in_rst = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +255,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
//0: disable APLL clock, I2S module will using PLL_D2_CLK(160M) as source clock
|
||||
//1: Enable APLL clock, I2S module will using APLL as source clock
|
||||
hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0;
|
||||
hw->clkm_conf.clka_en = (src == I2S_CLK_SRC_APLL) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,7 +268,7 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
//0: disable APLL clock, I2S module will using PLL_D2_CLK(160M) as source clock
|
||||
//1: Enable APLL clock, I2S module will using APLL as source clock
|
||||
hw->clkm_conf.clka_en = (src == I2S_CLK_APLL) ? 1 : 0;
|
||||
hw->clkm_conf.clka_en = (src == I2S_CLK_SRC_APLL) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,6 +451,14 @@ static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw)
|
||||
return hw->int_st.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get channel interrupt status register address
|
||||
*/
|
||||
static inline volatile void *i2s_ll_get_interrupt_status_reg(i2s_dev_t *hw)
|
||||
{
|
||||
return (volatile void *)(&hw->int_st);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear I2S interrupt status
|
||||
*
|
||||
@@ -735,17 +749,18 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
|
||||
* @brief Set I2S tx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to send data
|
||||
* @param slot_mask select slot to send data
|
||||
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
|
||||
*/
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
|
||||
{
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.tx_chan_mod = 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->conf_chan.tx_chan_mod = 2;
|
||||
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->conf_chan.tx_chan_mod = 0;
|
||||
@@ -759,17 +774,18 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
|
||||
* @brief Set I2S rx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to receive data
|
||||
* @param slot_mask select slot to receive data
|
||||
* @param is_msb_right the slot sequence is affected by msb_right according to TRM
|
||||
*/
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
|
||||
{
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.rx_chan_mod = 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->conf_chan.rx_chan_mod = 2;
|
||||
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->conf_chan.rx_chan_mod = 0;
|
||||
@@ -789,7 +805,6 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
|
||||
{
|
||||
int data_bit = hw->sample_rate_conf.tx_bits_mod;
|
||||
hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
|
||||
hw->conf_chan.tx_chan_mod = mono_ena;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -802,7 +817,6 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
|
||||
{
|
||||
int data_bit = hw->sample_rate_conf.rx_bits_mod;
|
||||
hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
|
||||
hw->conf_chan.rx_chan_mod = mono_ena;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -17,7 +17,7 @@
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -200,7 +200,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
* @brief Set RX source clock
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param src I2S source clock, ESP32-C3 only support `I2S_CLK_D2CLK`
|
||||
* @param src I2S source clock, ESP32-C3 only support `I2S_CLK_SRC_PLL_160M`
|
||||
*/
|
||||
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
@@ -555,21 +555,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
|
||||
* @brief Set I2S tx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to send data
|
||||
* @param slot_mask select slot to send data
|
||||
*/
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x03;
|
||||
@@ -583,21 +583,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
|
||||
* @brief Set I2S rx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to receive data
|
||||
* @param slot_mask select slot to receive data
|
||||
*/
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x03;
|
||||
|
@@ -18,7 +18,7 @@
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -28,7 +28,8 @@ extern "C" {
|
||||
|
||||
#define I2S_LL_TDM_CH_MASK (0xffff)
|
||||
#define I2S_LL_PDM_BCK_FACTOR (64)
|
||||
#define I2S_LL_BASE_CLK (2*APB_CLK_FREQ)
|
||||
// [clk_tree] TODO: replace the following switch table by clk_tree API
|
||||
#define I2S_LL_BASE_CLK (96*1000000)
|
||||
|
||||
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
|
||||
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
|
||||
@@ -201,7 +202,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
* @brief Set RX source clock
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param src I2S source clock, ESP32-H2 only support `I2S_CLK_D2CLK`
|
||||
* @param src I2S source clock, ESP32-H2 only support `I2S_CLK_SRC_PLL_96M` for now
|
||||
*/
|
||||
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
@@ -556,21 +557,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
|
||||
* @brief Set I2S tx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to send data
|
||||
* @param slot_mask select slot to send data
|
||||
*/
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x03;
|
||||
@@ -584,21 +585,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
|
||||
* @brief Set I2S rx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to receive data
|
||||
* @param slot_mask select slot to receive data
|
||||
*/
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x03;
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -41,6 +41,9 @@ extern "C" {
|
||||
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
|
||||
#define I2S_INTR_MAX (UINT32_MAX)
|
||||
|
||||
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF
|
||||
#define I2S_LL_RX_EVENT_MASK I2S_LL_EVENT_RX_EOF
|
||||
|
||||
/* I2S clock configuration structure */
|
||||
typedef struct {
|
||||
uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a)
|
||||
@@ -247,7 +250,7 @@ static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw)
|
||||
*/
|
||||
static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2;
|
||||
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +261,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
*/
|
||||
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
{
|
||||
hw->clkm_conf.clk_sel = (src == I2S_CLK_APLL) ? 1 : 2;
|
||||
hw->clkm_conf.clk_sel = (src == I2S_CLK_SRC_APLL) ? 1 : 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -441,6 +444,14 @@ static inline uint32_t i2s_ll_get_intr_status(i2s_dev_t *hw)
|
||||
return hw->int_st.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get DMA interrupt status register address
|
||||
*/
|
||||
static inline volatile void *i2s_ll_get_interrupt_status_reg(i2s_dev_t *hw)
|
||||
{
|
||||
return (volatile void *)(&hw->int_st);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear I2S interrupt status
|
||||
*
|
||||
@@ -829,17 +840,17 @@ static inline void i2s_ll_rx_enable_msb_shift(i2s_dev_t *hw, bool msb_shift_enab
|
||||
* @brief Set I2S tx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to send data
|
||||
* @param slot_mask select slot to send data
|
||||
*/
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
|
||||
{
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.tx_chan_mod = 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->conf_chan.tx_chan_mod = 2;
|
||||
hw->conf_chan.tx_chan_mod = is_msb_right ? 1 : 2;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.tx_chan_mod = is_msb_right ? 2 : 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->conf_chan.tx_chan_mod = 0;
|
||||
@@ -853,17 +864,17 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
|
||||
* @brief Set I2S rx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to receive data
|
||||
* @param slot_mask select slot to receive data
|
||||
*/
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask, bool is_msb_right)
|
||||
{
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.rx_chan_mod = 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->conf_chan.rx_chan_mod = 2;
|
||||
hw->conf_chan.rx_chan_mod = is_msb_right ? 1 : 2;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->conf_chan.rx_chan_mod = is_msb_right ? 2 : 1;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->conf_chan.rx_chan_mod = 0;
|
||||
@@ -906,7 +917,6 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
|
||||
int data_bit = hw->sample_rate_conf.tx_bits_mod;
|
||||
hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
|
||||
hw->conf.tx_dma_equal = mono_ena;
|
||||
hw->conf_chan.tx_chan_mod = mono_ena;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -920,7 +930,6 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena)
|
||||
int data_bit = hw->sample_rate_conf.rx_bits_mod;
|
||||
hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena;
|
||||
hw->conf.rx_dma_equal = mono_ena;
|
||||
hw->conf_chan.rx_chan_mod = mono_ena;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -17,7 +17,7 @@
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -190,7 +190,7 @@ static inline void i2s_ll_rx_reset_fifo(i2s_dev_t *hw)
|
||||
* @brief Set TX source clock
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK`
|
||||
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_SRC_PLL_160M`
|
||||
* TX and RX share the same clock setting
|
||||
*/
|
||||
static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
@@ -202,7 +202,7 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
* @brief Set RX source clock
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_D2CLK`
|
||||
* @param src I2S source clock, ESP32-S3 only support `I2S_CLK_SRC_PLL_160M`
|
||||
* TX and RX share the same clock setting
|
||||
*/
|
||||
static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src)
|
||||
@@ -558,21 +558,21 @@ static inline void i2s_ll_rx_set_active_chan_mask(i2s_dev_t *hw, uint32_t chan_m
|
||||
* @brief Set I2S tx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to send data
|
||||
* @param slot_mask select slot to send data
|
||||
*/
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->tx_tdm_ctrl.tx_tdm_tot_chan_num = 1; // tx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->tx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x01;
|
||||
hw->tx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->tx_tdm_ctrl.val |= 0x03;
|
||||
@@ -586,21 +586,21 @@ static inline void i2s_ll_tx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_
|
||||
* @brief Set I2S rx chan mode
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param slot_sel select slot to receive data
|
||||
* @param slot_mask select slot to receive data
|
||||
*/
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_sel_t slot_sel)
|
||||
static inline void i2s_ll_rx_select_slot(i2s_dev_t *hw, i2s_std_slot_mask_t slot_mask)
|
||||
{
|
||||
/* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot
|
||||
* Otherwise always enable the first two slots */
|
||||
hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; // rx_tdm_tot_chan_num = 2 slots - 1 = 1
|
||||
hw->rx_tdm_ctrl.val &= ~I2S_LL_TDM_CH_MASK;
|
||||
switch (slot_sel)
|
||||
switch (slot_mask)
|
||||
{
|
||||
case I2S_STD_SLOT_ONLY_LEFT:
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
break;
|
||||
case I2S_STD_SLOT_ONLY_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x01;
|
||||
hw->rx_tdm_ctrl.val |= 0x02;
|
||||
break;
|
||||
case I2S_STD_SLOT_LEFT_RIGHT:
|
||||
hw->rx_tdm_ctrl.val |= 0x03;
|
||||
|
@@ -10,10 +10,6 @@
|
||||
#include "soc/soc.h"
|
||||
#include "hal/i2s_hal.h"
|
||||
|
||||
#include "hal/i2s_std.h"
|
||||
#include "hal/i2s_pdm.h"
|
||||
#include "hal/i2s_tdm.h"
|
||||
|
||||
#if SOC_I2S_HW_VERSION_2 && SOC_I2S_SUPPORTS_PDM_TX
|
||||
/* PDM tx high pass filter cut-off frequency and coeffecients list
|
||||
* [0]: cut-off frequency; [1]: param0; [2]: param5 */
|
||||
@@ -24,7 +20,8 @@ static const float cut_off_coef[21][3] = {
|
||||
{104, 2, 4}, {92, 4, 4}, {91.5, 2, 7},
|
||||
{81, 4, 5}, {77.2, 3, 7}, {69, 5, 5},
|
||||
{63, 4, 7}, {58, 5, 6}, {49, 5, 7},
|
||||
{46, 6, 6}, {35.5, 6, 7}, {23.3, 7, 7}};
|
||||
{46, 6, 6}, {35.5, 6, 7}, {23.3, 7, 7}
|
||||
};
|
||||
#endif
|
||||
|
||||
void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
|
||||
@@ -33,7 +30,7 @@ void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
|
||||
hal->dev = I2S_LL_GET_HW(port_id);
|
||||
}
|
||||
|
||||
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src)
|
||||
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src)
|
||||
{
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_tx_enable_clock(hal->dev);
|
||||
@@ -44,7 +41,7 @@ void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_in
|
||||
i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div);
|
||||
}
|
||||
|
||||
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src)
|
||||
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src)
|
||||
{
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_rx_enable_clock(hal->dev);
|
||||
@@ -58,55 +55,56 @@ void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_in
|
||||
/*-------------------------------------------------------------------------
|
||||
| STD Specific Slot Configurations |
|
||||
-------------------------------------------------------------------------*/
|
||||
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_std_slot_config_t *slot_cfg = (i2s_std_slot_config_t*)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
i2s_ll_tx_reset(hal->dev);
|
||||
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
|
||||
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
|
||||
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
|
||||
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->bit_shift);
|
||||
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->ws_width);
|
||||
i2s_ll_tx_select_slot(hal->dev, slot_cfg->slot_sel);
|
||||
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
|
||||
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->msb_right);
|
||||
i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
|
||||
// According to the test, the behavior of tx_msb_right is opposite with TRM, TRM is wrong?
|
||||
i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
|
||||
i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
|
||||
/* Should always enable fifo */
|
||||
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
|
||||
#elif SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_tx_select_slot(hal->dev, slot_cfg->std.slot_mask);
|
||||
i2s_ll_tx_set_half_sample_bit(hal->dev, slot_bit_width);
|
||||
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb);
|
||||
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->left_align);
|
||||
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->big_endian);
|
||||
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
|
||||
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
|
||||
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->std.left_align);
|
||||
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->std.big_endian);
|
||||
#endif
|
||||
}
|
||||
|
||||
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_std_slot_config_t *slot_cfg = (i2s_std_slot_config_t*)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
i2s_ll_rx_reset(hal->dev);
|
||||
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
|
||||
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
|
||||
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
|
||||
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->bit_shift);
|
||||
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->ws_width);
|
||||
i2s_ll_rx_select_slot(hal->dev, slot_cfg->slot_sel);
|
||||
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->std.bit_shift);
|
||||
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->std.ws_width);
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->msb_right);
|
||||
i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask, slot_cfg->std.msb_right);
|
||||
i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->std.msb_right);
|
||||
i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->std.ws_pol);
|
||||
/* Should always enable fifo */
|
||||
i2s_ll_rx_force_enable_fifo_mod(hal->dev, true);
|
||||
#elif SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_rx_select_slot(hal->dev, slot_cfg->std.slot_mask);
|
||||
i2s_ll_rx_set_half_sample_bit(hal->dev, slot_bit_width);
|
||||
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb);
|
||||
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->left_align);
|
||||
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->big_endian);
|
||||
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->std.ws_pol);
|
||||
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->std.bit_order_lsb);
|
||||
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->std.left_align);
|
||||
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->std.big_endian);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -124,9 +122,8 @@ void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal)
|
||||
| PDM Specific Slot Configurations |
|
||||
-------------------------------------------------------------------------*/
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_pdm_tx_slot_config_t *slot_cfg = (i2s_pdm_tx_slot_config_t *)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
i2s_ll_tx_reset(hal->dev);
|
||||
@@ -134,21 +131,21 @@ void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_sl
|
||||
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
|
||||
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
|
||||
|
||||
i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->sd_prescale);
|
||||
i2s_ll_tx_set_pdm_hp_scale(hal->dev, slot_cfg->hp_scale);
|
||||
i2s_ll_tx_set_pdm_lp_scale(hal->dev, slot_cfg->lp_scale);
|
||||
i2s_ll_tx_set_pdm_sinc_scale(hal->dev, slot_cfg->sinc_scale);
|
||||
i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->sd_scale);
|
||||
i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->pdm_tx.sd_prescale);
|
||||
i2s_ll_tx_set_pdm_hp_scale(hal->dev, slot_cfg->pdm_tx.hp_scale);
|
||||
i2s_ll_tx_set_pdm_lp_scale(hal->dev, slot_cfg->pdm_tx.lp_scale);
|
||||
i2s_ll_tx_set_pdm_sinc_scale(hal->dev, slot_cfg->pdm_tx.sinc_scale);
|
||||
i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->pdm_tx.sd_scale);
|
||||
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_ll_tx_force_enable_fifo_mod(hal->dev, true);
|
||||
#elif SOC_I2S_HW_VERSION_2
|
||||
/* Still need to enable the first 2 TDM channel mask to get the correct number of frame */
|
||||
i2s_ll_tx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1);
|
||||
i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->hp_en);
|
||||
i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->pdm_tx.hp_en);
|
||||
uint8_t cnt = 0;
|
||||
float min = 1000;
|
||||
float expt_cut_off = slot_cfg->hp_cut_off_freq_hz;
|
||||
float expt_cut_off = slot_cfg->pdm_tx.hp_cut_off_freq_hz;
|
||||
/* Find the closest cut-off frequency and its coefficients */
|
||||
for (int i = 0; i < 21; i++) {
|
||||
float tmp = cut_off_coef[i][0] < expt_cut_off ? expt_cut_off - cut_off_coef[i][0] : cut_off_coef[i][0] - expt_cut_off;
|
||||
@@ -159,9 +156,9 @@ void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_sl
|
||||
}
|
||||
i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, cut_off_coef[cnt][1]);
|
||||
i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, cut_off_coef[cnt][2]);
|
||||
i2s_ll_tx_enable_pdm_sd_codec(hal->dev, slot_cfg->sd_en);
|
||||
i2s_ll_tx_set_pdm_sd_dither(hal->dev, slot_cfg->sd_dither);
|
||||
i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->sd_dither2);
|
||||
i2s_ll_tx_enable_pdm_sd_codec(hal->dev, slot_cfg->pdm_tx.sd_en);
|
||||
i2s_ll_tx_set_pdm_sd_dither(hal->dev, slot_cfg->pdm_tx.sd_dither);
|
||||
i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->pdm_tx.sd_dither2);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -172,9 +169,8 @@ void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal)
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_pdm_rx_slot_config_t *slot_cfg = (i2s_pdm_rx_slot_config_t *)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
i2s_ll_rx_reset(hal->dev);
|
||||
@@ -199,74 +195,72 @@ void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal)
|
||||
| TDM Specific Slot Configurations |
|
||||
-------------------------------------------------------------------------*/
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_tdm_slot_config_t *slot_cfg = (i2s_tdm_slot_config_t*)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
uint32_t cnt;
|
||||
uint32_t msk = slot_cfg->slot_mask;
|
||||
uint32_t msk = slot_cfg->tdm.slot_mask;
|
||||
/* Get the maximum slot number */
|
||||
cnt = 32 - __builtin_clz(msk);
|
||||
/* There should be at least 2 slots in total even for mono mode */
|
||||
cnt = cnt < 2 ? 2 : cnt;
|
||||
uint32_t total_slot = slot_cfg->total_slot > cnt ? slot_cfg->total_slot : cnt;
|
||||
uint32_t total_slot = slot_cfg->tdm.total_slot > cnt ? slot_cfg->tdm.total_slot : cnt;
|
||||
i2s_ll_tx_reset(hal->dev);
|
||||
i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave
|
||||
i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
|
||||
i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
|
||||
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->bit_shift);
|
||||
if (slot_cfg->ws_width == I2S_TDM_AUTO_WS_WIDTH) {
|
||||
i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->tdm.bit_shift);
|
||||
if (slot_cfg->tdm.ws_width == 0) { // 0: I2S_TDM_AUTO_WS_WIDTH
|
||||
i2s_ll_tx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2);
|
||||
} else {
|
||||
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->ws_width);
|
||||
i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->tdm.ws_width);
|
||||
}
|
||||
|
||||
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->tdm.ws_pol);
|
||||
i2s_ll_tx_set_chan_num(hal->dev, total_slot);
|
||||
/* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */
|
||||
i2s_ll_tx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
|
||||
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->slot_mask);
|
||||
i2s_ll_tx_set_skip_mask(hal->dev, slot_cfg->skip_mask);
|
||||
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
|
||||
i2s_ll_tx_set_skip_mask(hal->dev, slot_cfg->tdm.skip_mask);
|
||||
i2s_ll_tx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
|
||||
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb);
|
||||
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->left_align);
|
||||
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->big_endian);
|
||||
i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
|
||||
i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
|
||||
i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);
|
||||
}
|
||||
|
||||
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config)
|
||||
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg)
|
||||
{
|
||||
i2s_tdm_slot_config_t *slot_cfg = (i2s_tdm_slot_config_t*)slot_config;
|
||||
uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ?
|
||||
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
|
||||
uint32_t cnt;
|
||||
uint32_t msk = slot_cfg->slot_mask;
|
||||
uint32_t msk = slot_cfg->tdm.slot_mask;
|
||||
for (cnt = 0; msk; cnt++, msk >>= 1);
|
||||
/* Get the maximum slot number */
|
||||
cnt = 32 - __builtin_clz(msk);
|
||||
/* There should be at least 2 slots in total even for mono mode */
|
||||
cnt = cnt < 2 ? 2 : cnt;
|
||||
uint32_t total_slot = slot_cfg->total_slot > cnt ? slot_cfg->total_slot : cnt;
|
||||
uint32_t total_slot = slot_cfg->tdm.total_slot > cnt ? slot_cfg->tdm.total_slot : cnt;
|
||||
i2s_ll_rx_reset(hal->dev);
|
||||
i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave
|
||||
i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width);
|
||||
i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO);
|
||||
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->bit_shift);
|
||||
if (slot_cfg->ws_width == I2S_TDM_AUTO_WS_WIDTH) {
|
||||
i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->tdm.bit_shift);
|
||||
if (slot_cfg->tdm.ws_width == 0) { // 0: I2S_TDM_AUTO_WS_WIDTH
|
||||
i2s_ll_rx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2);
|
||||
} else {
|
||||
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->ws_width);
|
||||
i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->tdm.ws_width);
|
||||
}
|
||||
|
||||
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol);
|
||||
i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->tdm.ws_pol);
|
||||
i2s_ll_rx_set_chan_num(hal->dev, total_slot);
|
||||
/* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */
|
||||
i2s_ll_rx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ?
|
||||
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->slot_mask);
|
||||
I2S_TDM_SLOT0 : (uint32_t)slot_cfg->tdm.slot_mask);
|
||||
i2s_ll_rx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2);
|
||||
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb);
|
||||
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->left_align);
|
||||
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->big_endian);
|
||||
i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->tdm.bit_order_lsb);
|
||||
i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->tdm.left_align);
|
||||
i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->tdm.big_endian);
|
||||
}
|
||||
|
||||
void i2s_hal_tdm_enable_tx_channel(i2s_hal_context_t *hal)
|
||||
|
@@ -17,33 +17,73 @@
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/i2s_types_priv.h"
|
||||
#include "hal/i2s_ll.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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 */
|
||||
} i2s_clk_config_t;
|
||||
|
||||
/**
|
||||
* @brief General slot configuration information
|
||||
* @note It is a general purpose struct, not supposed to be used directly by user
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode that applied) */
|
||||
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 */
|
||||
} i2s_slot_config_t;
|
||||
union {
|
||||
/* STD configurations */
|
||||
struct {
|
||||
i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */
|
||||
uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */
|
||||
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
|
||||
bool bit_shift; /*!< Set to enbale bit shift in Philip mode */
|
||||
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
|
||||
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
|
||||
} std; /*!< Specific configurations for standard mode */
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
/* TDM configurations */
|
||||
struct {
|
||||
i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */
|
||||
uint32_t ws_width; /*!< WS signal width ((i.e. the number of bclk ticks that ws signal is high)) */
|
||||
bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */
|
||||
bool bit_shift; /*!< Set true to enable bit shift in Philip mode */
|
||||
|
||||
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 */
|
||||
uint32_t total_slot; /*!< I2S total number of slots. If it is smaller than the biggest activated channel number, it will be set to this number automatically. */
|
||||
} tdm; /*!< Specific configurations for TDM mode */
|
||||
#endif
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
/* PDM TX configurations */
|
||||
struct {
|
||||
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
|
||||
} pdm_tx; /*!< Specific configurations for PDM TX mode */
|
||||
#endif
|
||||
};
|
||||
|
||||
} i2s_hal_slot_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S clock configuration
|
||||
@@ -54,7 +94,7 @@ typedef struct {
|
||||
uint32_t bclk; /*!< I2S bit clock */
|
||||
uint16_t mclk_div; /*!< I2S master clock division */
|
||||
uint16_t bclk_div; /*!< I2S bit clock division*/
|
||||
} i2s_clock_info_t;
|
||||
} i2s_hal_clock_info_t;
|
||||
|
||||
/**
|
||||
* Context that should be maintained by both the driver and the HAL
|
||||
@@ -67,7 +107,7 @@ typedef struct {
|
||||
* @brief Init I2S hal context
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param port_id The I2S port number, the max port number is (I2S_NUM_MAX -1)
|
||||
* @param port_id The I2S port number, the max port number is (SOC_I2S_NUM -1)
|
||||
*/
|
||||
void i2s_hal_init(i2s_hal_context_t *hal, int port_id);
|
||||
|
||||
@@ -78,7 +118,7 @@ void i2s_hal_init(i2s_hal_context_t *hal, int port_id);
|
||||
* @param clk_info clock information
|
||||
* @param clk_src clock source
|
||||
*/
|
||||
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src);
|
||||
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src);
|
||||
|
||||
/**
|
||||
* @brief Set rx channel clock
|
||||
@@ -87,7 +127,7 @@ void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_in
|
||||
* @param clk_info clock information
|
||||
* @param clk_src clock source
|
||||
*/
|
||||
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src);
|
||||
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
@@ -100,7 +140,7 @@ void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_in
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s standard mode
|
||||
*/
|
||||
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Set rx slot to standard mode
|
||||
@@ -109,7 +149,7 @@ void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_sl
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s standard mode
|
||||
*/
|
||||
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Enable tx channel as standard mode
|
||||
@@ -138,7 +178,7 @@ void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal);
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s pdm tx mode
|
||||
*/
|
||||
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Enable tx channel as pdm mode
|
||||
@@ -156,7 +196,7 @@ void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal);
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s pdm rx mode
|
||||
*/
|
||||
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Enable rx channel as pdm mode
|
||||
@@ -178,7 +218,7 @@ void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal);
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s tdm mode
|
||||
*/
|
||||
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Set rx slot to tdm mode
|
||||
@@ -187,7 +227,7 @@ void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_sl
|
||||
* @param is_slave If is slave role
|
||||
* @param slot_config General slot configuration pointer, but will specified to i2s tdm mode
|
||||
*/
|
||||
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config);
|
||||
void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_hal_slot_config_t *slot_cfg);
|
||||
|
||||
/**
|
||||
* @brief Enable tx channel as tdm mode
|
||||
@@ -261,7 +301,7 @@ void i2s_hal_tdm_enable_rx_channel(i2s_hal_context_t *hal);
|
||||
#define i2s_hal_rx_reset_fifo(hal) i2s_ll_rx_reset_fifo((hal)->dev)
|
||||
|
||||
|
||||
#if !SOC_GDMA_SUPPORTED
|
||||
#if !SOC_I2S_SUPPORTS_GDMA
|
||||
/**
|
||||
* @brief Enable I2S TX DMA
|
||||
*
|
||||
|
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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_DEFAULT, \
|
||||
.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_D2CLK, \
|
||||
.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;
|
||||
|
||||
#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_DEFAULT, \
|
||||
.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_DEFAULT, \
|
||||
.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_D2CLK, \
|
||||
.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;
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_TX
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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) { \
|
||||
.mode = I2S_COMM_MODE_STD, \
|
||||
.data_bit_width = bits_per_sample, \
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \
|
||||
.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_D2CLK, \
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @breif I2S slot configuration for standard 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 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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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_DEFAULT, \
|
||||
.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_DEFAULT, \
|
||||
.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_DEFAULT, \
|
||||
.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_DEFAULT, \
|
||||
.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_D2CLK, \
|
||||
.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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
@@ -10,156 +10,61 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_bit_defs.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2S bit width per sample.
|
||||
* @brief I2S channel slot mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_SLOT_MODE_MONO = 1, /*!< I2S channel slot format mono, transmit same data in all slots for tx mode, only receive the data in the first slots for rx mode. */
|
||||
I2S_SLOT_MODE_STEREO = 2, /*!< I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode. */
|
||||
} i2s_slot_mode_t;
|
||||
|
||||
/**
|
||||
* @brief I2S channel direction
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_DIR_RX = BIT(0), /*!< I2S channel direction RX */
|
||||
I2S_DIR_TX = BIT(1), /*!< I2S channel direction TX */
|
||||
} i2s_dir_t;
|
||||
|
||||
/**
|
||||
* @brief I2S controller role
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_ROLE_MASTER, /*!< I2S controller master role, bclk and ws signal will be set to output */
|
||||
I2S_ROLE_SLAVE /*!< I2S controller slave role, bclk and ws signal will be set to input */
|
||||
} i2s_role_t;
|
||||
|
||||
/**
|
||||
* @brief Available data bit width in one slot
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_DATA_BIT_WIDTH_8BIT = 8, /*!< I2S channel data bit-width: 8 */
|
||||
I2S_DATA_BIT_WIDTH_16BIT = 16, /*!< I2S channel data bit-width: 16 */
|
||||
I2S_DATA_BIT_WIDTH_24BIT = 24, /*!< I2S channel data bit-width: 24 */
|
||||
I2S_DATA_BIT_WIDTH_32BIT = 32, /*!< I2S channel data bit-width: 32 */
|
||||
} i2s_data_bit_width_t;
|
||||
|
||||
/**
|
||||
* @brief Total slot bit width in one slot
|
||||
*
|
||||
*/
|
||||
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;
|
||||
I2S_SLOT_BIT_WIDTH_AUTO = (0), /*!< I2S channel slot bit-width equals to data bit-width */
|
||||
I2S_SLOT_BIT_WIDTH_8BIT = (8), /*!< I2S channel slot bit-width: 8 */
|
||||
I2S_SLOT_BIT_WIDTH_16BIT = (16), /*!< I2S channel slot bit-width: 16 */
|
||||
I2S_SLOT_BIT_WIDTH_24BIT = (24), /*!< I2S channel slot bit-width: 24 */
|
||||
I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */
|
||||
} i2s_slot_bit_width_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;
|
||||
|
||||
/**
|
||||
* @brief I2S source clock
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_D2CLK = 0, /*!< Clock from PLL_D2_CLK(160M)*/
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
I2S_CLK_APLL, /*!< Clock from APLL*/
|
||||
#endif
|
||||
} i2s_clock_src_t;
|
||||
|
||||
/**
|
||||
* @brief The multiple of mclk to sample rate
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_MCLK_MULTIPLE_DEFAULT = 0, /*!< Default value. mclk = sample_rate * 256 */
|
||||
I2S_MCLK_MULTIPLE_128 = 128, /*!< mclk = sample_rate * 128 */
|
||||
I2S_MCLK_MULTIPLE_256 = 256, /*!< mclk = sample_rate * 256 */
|
||||
I2S_MCLK_MULTIPLE_384 = 384, /*!< mclk = sample_rate * 384 */
|
||||
} i2s_mclk_multiple_t;
|
||||
|
||||
#if SOC_I2S_SUPPORTS_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
|
||||
typedef soc_periph_i2s_clk_src_t i2s_clock_src_t; /*!< I2S clock source */
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PCM
|
||||
/**
|
||||
@@ -167,33 +72,75 @@ typedef enum {
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_PCM_DISABLE = 0, /*!< Disable A/U law decopress or compress*/
|
||||
I2S_PCM_DISABLE = 0, /*!< Disable A/U law decompress or compress*/
|
||||
I2S_PCM_A_DECOMPRESS, /*!< A-law decompress*/
|
||||
I2S_PCM_A_COMPRESS, /*!< A-law compress*/
|
||||
I2S_PCM_U_DECOMPRESS, /*!< U-law decompress*/
|
||||
I2S_PCM_U_COMPRESS, /*!< U-law compress*/
|
||||
} i2s_pcm_compress_t;
|
||||
#endif
|
||||
#endif // SOC_I2S_SUPPORTS_PCM
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
/**
|
||||
* @brief I2S PDM RX downsample mode
|
||||
* @brief I2S PDM RX down-sampling mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_PDM_DSR_8S = 0, /*!< downsampling number is 8 for PDM RX mode*/
|
||||
I2S_PDM_DSR_16S, /*!< downsampling number is 16 for PDM RX mode*/
|
||||
I2S_PDM_DSR_MAX,
|
||||
} i2s_pdm_dsr_t;
|
||||
#endif
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_RX
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
/**
|
||||
* @brief pdm tx singnal scaling mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_PDM_SIG_SCALING_DIV_2 = 0, /*!< I2S TX PDM sigmadelta signal scaling: /2 */
|
||||
I2S_PDM_SIG_SCALING_MUL_1 = 1, /*!< I2S TX PDM sigmadelta signal scaling: x1 */
|
||||
I2S_PDM_SIG_SCALING_MUL_2 = 2, /*!< I2S TX PDM sigmadelta signal scaling: x2 */
|
||||
I2S_PDM_SIG_SCALING_MUL_4 = 3, /*!< I2S TX PDM sigmadelta signal scaling: x4 */
|
||||
I2S_PDM_SIG_SCALING_DIV_2 = 0, /*!< I2S TX PDM signal scaling: /2 */
|
||||
I2S_PDM_SIG_SCALING_MUL_1 = 1, /*!< I2S TX PDM signal scaling: x1 */
|
||||
I2S_PDM_SIG_SCALING_MUL_2 = 2, /*!< I2S TX PDM signal scaling: x2 */
|
||||
I2S_PDM_SIG_SCALING_MUL_4 = 3, /*!< I2S TX PDM signal scaling: x4 */
|
||||
} i2s_pdm_sig_scale_t;
|
||||
#endif
|
||||
#endif // SOC_I2S_SUPPORTS_PDM_TX
|
||||
|
||||
/**
|
||||
* @brief I2S slot select in standard mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_STD_SLOT_ONLY_LEFT = BIT(0), /*!< I2S only transmits or receives left slot */
|
||||
I2S_STD_SLOT_ONLY_RIGHT = BIT(1), /*!< I2S only transmits or receives right slot */
|
||||
I2S_STD_SLOT_LEFT_RIGHT = BIT(0) | BIT(1), /*!< I2S transmits or receives both left and right slot */
|
||||
} i2s_std_slot_mask_t;
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
/**
|
||||
* @brief tdm slot number
|
||||
* @note Multiple slots in TDM mode.
|
||||
* For TX module, only the active slot send the audio data, the inactive slot send a constant or will be skipped if 'skip_msk' is set.
|
||||
* For RX module, only receive the audio data in active slots, the data in inactive slots will be ignored.
|
||||
* the bit map of active slot can not exceed (0x1<<total_slot_num).
|
||||
* e.g: slot_mask = (I2S_TDM_SLOT0 | I2S_TDM_SLOT3), here the active slot number is 2 and total_slot is not supposed to be smaller than 4.
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_TDM_SLOT0 = BIT(0), /*!< I2S slot 0 enabled */
|
||||
I2S_TDM_SLOT1 = BIT(1), /*!< I2S slot 1 enabled */
|
||||
I2S_TDM_SLOT2 = BIT(2), /*!< I2S slot 2 enabled */
|
||||
I2S_TDM_SLOT3 = BIT(3), /*!< I2S slot 3 enabled */
|
||||
I2S_TDM_SLOT4 = BIT(4), /*!< I2S slot 4 enabled */
|
||||
I2S_TDM_SLOT5 = BIT(5), /*!< I2S slot 5 enabled */
|
||||
I2S_TDM_SLOT6 = BIT(6), /*!< I2S slot 6 enabled */
|
||||
I2S_TDM_SLOT7 = BIT(7), /*!< I2S slot 7 enabled */
|
||||
I2S_TDM_SLOT8 = BIT(8), /*!< I2S slot 8 enabled */
|
||||
I2S_TDM_SLOT9 = BIT(9), /*!< I2S slot 9 enabled */
|
||||
I2S_TDM_SLOT10 = BIT(10), /*!< I2S slot 10 enabled */
|
||||
I2S_TDM_SLOT11 = BIT(11), /*!< I2S slot 11 enabled */
|
||||
I2S_TDM_SLOT12 = BIT(12), /*!< I2S slot 12 enabled */
|
||||
I2S_TDM_SLOT13 = BIT(13), /*!< I2S slot 13 enabled */
|
||||
I2S_TDM_SLOT14 = BIT(14), /*!< I2S slot 14 enabled */
|
||||
I2S_TDM_SLOT15 = BIT(15), /*!< I2S slot 15 enabled */
|
||||
} i2s_tdm_slot_mask_t;
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "i2s_types_priv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2S controller communication mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_COMM_MODE_STD, /*!< I2S controller using standard communication mode, support philip/MSB/PCM format */
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
I2S_COMM_MODE_PDM, /*!< I2S controller using PDM communication mode, support PDM output or input */
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
I2S_COMM_MODE_TDM, /*!< I2S controller using TDM communication mode, support up to 16 slots per frame */
|
||||
#endif
|
||||
I2S_COMM_MODE_MAX
|
||||
} i2s_comm_mode_t;
|
||||
|
||||
/**
|
||||
* @brief I2S channel slot mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_SLOT_MODE_MONO = 1, /*!< I2S channel slot format mono, transmit same data in all slots for tx mode, only receive the data in the first slots for rx mode. */
|
||||
I2S_SLOT_MODE_STEREO = 2, /*!< I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode. */
|
||||
} i2s_slot_mode_t;
|
||||
|
||||
/**
|
||||
* @brief I2S slot select in standard mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_STD_SLOT_ONLY_LEFT = 0x01, /*!< I2S only transmits or receives left slot */
|
||||
I2S_STD_SLOT_ONLY_RIGHT = 0x02, /*!< I2S only transmits or receives right slot */
|
||||
I2S_STD_SLOT_LEFT_RIGHT = 0x03, /*!< I2S only transmits or receives both left and right slot */
|
||||
} i2s_std_slot_sel_t;
|
||||
|
||||
/**
|
||||
* @brief I2S channel direction
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_DIR_RX = BIT(0), /*!< I2S channel direction RX */
|
||||
I2S_DIR_TX = BIT(1), /*!< I2S channel direction TX */
|
||||
} i2s_dir_t;
|
||||
|
||||
/**
|
||||
* @brief I2S controller role
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_ROLE_MASTER, /*!< I2S controller master role, bclk and ws signal will be set to output */
|
||||
I2S_ROLE_SLAVE /*!< I2S controller slave role, bclk and ws signal will be set to input */
|
||||
} i2s_role_t;
|
||||
|
||||
/**
|
||||
* @brief Available data bit width in one slot
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_DATA_BIT_WIDTH_8BIT = 8, /*!< I2S channel data bit-width: 8 */
|
||||
I2S_DATA_BIT_WIDTH_16BIT = 16, /*!< I2S channel data bit-width: 16 */
|
||||
I2S_DATA_BIT_WIDTH_24BIT = 24, /*!< I2S channel data bit-width: 24 */
|
||||
I2S_DATA_BIT_WIDTH_32BIT = 32, /*!< I2S channel data bit-width: 32 */
|
||||
} i2s_data_bit_width_t;
|
||||
|
||||
/**
|
||||
* @brief Total slot bit width in one slot
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_SLOT_BIT_WIDTH_DEFAULT = (0), /*!< I2S channel slot bit-width equals to data bit-width */
|
||||
I2S_SLOT_BIT_WIDTH_8BIT = (8), /*!< I2S channel slot bit-width: 8 */
|
||||
I2S_SLOT_BIT_WIDTH_16BIT = (16), /*!< I2S channel slot bit-width: 16 */
|
||||
I2S_SLOT_BIT_WIDTH_24BIT = (24), /*!< I2S channel slot bit-width: 24 */
|
||||
I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */
|
||||
} i2s_slot_bit_width_t;
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
/**
|
||||
* @brief tdm slot number
|
||||
* @note There are 16 slots in TDM mode.
|
||||
* For TX module, only the active slot send the audio data, the inactive slot send a constant or will be skipped if 'skip_msk' is set.
|
||||
* For RX module, only receive the audio data in active slots, the data in inactive slots will be ignored.
|
||||
* the bit map of active slot can not exceed (0x1<<total_slot_num).
|
||||
* e.g: slot_mask = (I2S_TDM_SLOT0 | I2S_TDM_SLOT3), here the active slot number is 2 and total_slot is not supposed to be smaller than 4.
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_TDM_SLOT0 = BIT(0), /*!< I2S slot 0 enabled */
|
||||
I2S_TDM_SLOT1 = BIT(1), /*!< I2S slot 1 enabled */
|
||||
I2S_TDM_SLOT2 = BIT(2), /*!< I2S slot 2 enabled */
|
||||
I2S_TDM_SLOT3 = BIT(3), /*!< I2S slot 3 enabled */
|
||||
I2S_TDM_SLOT4 = BIT(4), /*!< I2S slot 4 enabled */
|
||||
I2S_TDM_SLOT5 = BIT(5), /*!< I2S slot 5 enabled */
|
||||
I2S_TDM_SLOT6 = BIT(6), /*!< I2S slot 6 enabled */
|
||||
I2S_TDM_SLOT7 = BIT(7), /*!< I2S slot 7 enabled */
|
||||
I2S_TDM_SLOT8 = BIT(8), /*!< I2S slot 8 enabled */
|
||||
I2S_TDM_SLOT9 = BIT(9), /*!< I2S slot 9 enabled */
|
||||
I2S_TDM_SLOT10 = BIT(10), /*!< I2S slot 10 enabled */
|
||||
I2S_TDM_SLOT11 = BIT(11), /*!< I2S slot 11 enabled */
|
||||
I2S_TDM_SLOT12 = BIT(12), /*!< I2S slot 12 enabled */
|
||||
I2S_TDM_SLOT13 = BIT(13), /*!< I2S slot 13 enabled */
|
||||
I2S_TDM_SLOT14 = BIT(14), /*!< I2S slot 14 enabled */
|
||||
I2S_TDM_SLOT15 = BIT(15), /*!< I2S slot 15 enabled */
|
||||
} i2s_tdm_slot_mask_t;
|
||||
#endif // SOC_I2S_SUPPORTS_TDM
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -241,6 +241,22 @@ typedef enum {
|
||||
MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< SElect APB as the default clock choice */
|
||||
} soc_periph_mcpwm_capture_clk_src_t;
|
||||
|
||||
///////////////////////////////////////////////////I2S//////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of
|
||||
*/
|
||||
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_APLL}
|
||||
|
||||
/**
|
||||
* @brief I2S clock source enum
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the default source clock */
|
||||
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */
|
||||
I2S_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -202,6 +202,21 @@ typedef enum {
|
||||
UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */
|
||||
} soc_periph_uart_clk_src_legacy_t;
|
||||
|
||||
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of
|
||||
*/
|
||||
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M}
|
||||
|
||||
/**
|
||||
* @brief I2S clock source enum
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
|
||||
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -110,10 +110,11 @@ typedef enum {
|
||||
SOC_MOD_CLK_RTC_FAST, /*!< RTC_FAST_CLK can be sourced from XTAL_D2 or RC_FAST by configuring soc_rtc_fast_clk_src_t */
|
||||
SOC_MOD_CLK_RTC_SLOW, /*!< RTC_SLOW_CLK can be sourced from RC_SLOW, XTAL32K, or RC32K by configuring soc_rtc_slow_clk_src_t */
|
||||
// For digital domain: peripherals, WIFI, BLE
|
||||
SOC_MOD_CLK_AHB, /*!< AHB_CLK sources from CPU with a configurable divider */
|
||||
SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
|
||||
SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 8MHz rc oscillator, passing a clock gating to the peripherals */
|
||||
SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 32MHz crystal */
|
||||
SOC_MOD_CLK_AHB, /*< AHB_CLK sources from CPU with a configurable divider */
|
||||
SOC_MOD_CLK_XTAL32K, /*< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
|
||||
SOC_MOD_CLK_RC_FAST, /*< RC_FAST_CLK comes from the internal 8MHz rc oscillator, passing a clock gating to the peripherals */
|
||||
SOC_MOD_CLK_XTAL, /*< XTAL_CLK comes from the external 32MHz crystal */
|
||||
SOC_MOD_CLK_PLL, /*< PLL_CLK is the output of 32MHz crystal oscillator frequency multiplier, 96MHz */
|
||||
} soc_module_clk_t;
|
||||
|
||||
|
||||
@@ -206,6 +207,21 @@ typedef enum {
|
||||
UART_SCLK_DEFAULT = SOC_MOD_CLK_AHB, /*!< UART source clock default choice is AHB */
|
||||
} soc_periph_uart_clk_src_legacy_t;
|
||||
|
||||
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of
|
||||
*/
|
||||
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL}
|
||||
|
||||
/**
|
||||
* @brief I2S clock source enum
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL, /*!< Select SOC_MOD_CLK_PLL as the default source clock */
|
||||
I2S_CLK_SRC_PLL_96M = SOC_MOD_CLK_PLL, /*!< Select PLL as the source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -219,6 +219,22 @@ typedef enum {
|
||||
UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */
|
||||
} soc_periph_uart_clk_src_legacy_t;
|
||||
|
||||
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of
|
||||
*/
|
||||
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_APLL}
|
||||
|
||||
/**
|
||||
* @brief I2S clock source enum
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
|
||||
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
|
||||
I2S_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -248,6 +248,21 @@ typedef enum {
|
||||
MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< SElect APB as the default clock choice */
|
||||
} soc_periph_mcpwm_capture_clk_src_t;
|
||||
|
||||
///////////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of
|
||||
*/
|
||||
#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M}
|
||||
|
||||
/**
|
||||
* @brief I2S clock source enum
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */
|
||||
I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -197,7 +197,7 @@ typedef volatile struct i2s_dev_s {
|
||||
uint32_t reserved26 : 6; /*Reserved*/
|
||||
};
|
||||
uint32_t val;
|
||||
} tx_pcm2pdm_conf;
|
||||
} tx_pcm2pdm_conf; // Only available on I2S0
|
||||
union {
|
||||
struct {
|
||||
uint32_t tx_pdm_fp : 10; /*I2S TX PDM Fp*/
|
||||
@@ -207,7 +207,7 @@ typedef volatile struct i2s_dev_s {
|
||||
uint32_t reserved26 : 6; /*Reserved*/
|
||||
};
|
||||
uint32_t val;
|
||||
} tx_pcm2pdm_conf1;
|
||||
} tx_pcm2pdm_conf1; // Only available on I2S0
|
||||
uint32_t reserved_48;
|
||||
uint32_t reserved_4c;
|
||||
union {
|
||||
|
BIN
docs/_static/diagrams/i2s/i2s_file_structure.png
vendored
Normal file
BIN
docs/_static/diagrams/i2s/i2s_file_structure.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
docs/_static/diagrams/i2s/i2s_state_machine.png
vendored
Normal file
BIN
docs/_static/diagrams/i2s/i2s_state_machine.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
47
docs/_static/diagrams/i2s/pdm.json
vendored
Normal file
47
docs/_static/diagrams/i2s/pdm.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "PDM Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": ".A.B.C..D.E.F.G.H."
|
||||
},
|
||||
{
|
||||
"name": "CLK",
|
||||
"wave": "10.1.x|.0.1.0.1.x"
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "x2.2.x|.2.2.2.2.x",
|
||||
"data": [
|
||||
"LMSB",
|
||||
"RMSB",
|
||||
"LLSB",
|
||||
"RLSB",
|
||||
"LMSB",
|
||||
"RMSB"
|
||||
],
|
||||
"node": "...L.M..O.P...R.S."
|
||||
},
|
||||
{
|
||||
"node": ".I..........J"
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"A<->B left",
|
||||
"B<->C right",
|
||||
"D<->E left",
|
||||
"E<->F right",
|
||||
"F<->G left",
|
||||
"G<->H right",
|
||||
"I<->J left slot & right slot",
|
||||
"A-I",
|
||||
"B-L",
|
||||
"C-M",
|
||||
"D-O",
|
||||
"E-P",
|
||||
"F-J",
|
||||
"G-R",
|
||||
"H-S"
|
||||
]
|
||||
}
|
52
docs/_static/diagrams/i2s/std_msb.json
vendored
Normal file
52
docs/_static/diagrams/i2s/std_msb.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "Standard MSB Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": "..C.....D",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "..A...B",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..pd.pd.pd.pd"
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "xx0.d0.d1.u1u10",
|
||||
"node": ".H.....I......J",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx2x|2x|2x|2x|2",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": "......K",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "..E.....F.....G",
|
||||
"phase": 0.65
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"C<->D slot_bit_width",
|
||||
"A<->B data_bit_width",
|
||||
"E<->F Left Slot",
|
||||
"F<->G Right Slot",
|
||||
"E-C",
|
||||
"K-B",
|
||||
"F-D",
|
||||
"J-G"
|
||||
]
|
||||
}
|
54
docs/_static/diagrams/i2s/std_pcm.json
vendored
Normal file
54
docs/_static/diagrams/i2s/std_pcm.json
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "Standard PCM Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": "...C.....D",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "...A...B",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..dp.dp.dp.dp."
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "x10d0.d10d0.d10",
|
||||
"node": "..P",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx.2x|2x|2x|2x|2",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": ".......K.......L",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "..ME.....F.....G",
|
||||
"phase": 0.65
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"C<->D slot_bit_width",
|
||||
"A<->B data_bit_width",
|
||||
"M<->E bit shift",
|
||||
"E<->F Left Slot",
|
||||
"F<->G Right Slot",
|
||||
"P-M",
|
||||
"E-C",
|
||||
"K-B",
|
||||
"F-D",
|
||||
"G-L"
|
||||
]
|
||||
}
|
54
docs/_static/diagrams/i2s/std_philip.json
vendored
Normal file
54
docs/_static/diagrams/i2s/std_philip.json
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "Standard Philip Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": "...C.....D",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "...A...B",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..dp.dp.dp.dp."
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "xx0.d0.d1.u1u10.",
|
||||
"node": "..P",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx.2x|2x|2x|2x|2",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": ".......K.......L",
|
||||
"phase": 0.65
|
||||
},
|
||||
{
|
||||
"node": "..ME.....F.....G",
|
||||
"phase": 0.65
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"C<->D slot_bit_width",
|
||||
"A<->B data_bit_width",
|
||||
"M<->E bit shift",
|
||||
"E<->F Left Slot",
|
||||
"F<->G Right Slot",
|
||||
"P-M",
|
||||
"E-C",
|
||||
"K-B",
|
||||
"F-D",
|
||||
"L-G"
|
||||
]
|
||||
}
|
60
docs/_static/diagrams/i2s/tdm_msb.json
vendored
Normal file
60
docs/_static/diagrams/i2s/tdm_msb.json
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "TDM MSB Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": ".E.........F.........G",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..d.p.d.pd.pd.p.d.pd.p"
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "x0dd0.dd0dd1uu1.uu1uu0",
|
||||
"node": ".H...................R",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "x2x|22x|2x|2x|22x|2x|2",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": ".H...K...I.M...N...P.Q",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"node": ".A...B...C.D...J...L.S",
|
||||
"phase": -0.35
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"E<->F Left Slots",
|
||||
"F<->G Right Slots",
|
||||
"A<->B Slot 1",
|
||||
"B<->C Slot 2",
|
||||
"C<->D ...",
|
||||
"D<->J Slot n",
|
||||
"J<->L Slot n+1",
|
||||
"L<->S ...",
|
||||
"A-E",
|
||||
"K-B",
|
||||
"C-I",
|
||||
"D-F",
|
||||
"J-N",
|
||||
"L-P",
|
||||
"Q-G",
|
||||
"S-R"
|
||||
]
|
||||
}
|
57
docs/_static/diagrams/i2s/tdm_pcm_long.json
vendored
Normal file
57
docs/_static/diagrams/i2s/tdm_pcm_long.json
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "TDM PCM (long) Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": ".T...L",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"node": ".UE.........G",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..d.p.d.pd.pd"
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "01u.10d...01u.",
|
||||
"node": ".NH",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx2x|2x|2x|22x",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": "..H..FK.I...M",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"node": "..A...B.C...D...J",
|
||||
"phase": -0.35
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"T<->L one slot pulse",
|
||||
"U<->E bit shift",
|
||||
"E<->G Frame",
|
||||
"A<->B Slot 1",
|
||||
"B<->C ...",
|
||||
"C<->D Slot n",
|
||||
"T-N",
|
||||
"A-E",
|
||||
"K-B",
|
||||
"C-I",
|
||||
"D-M",
|
||||
"D-G",
|
||||
"L-F"
|
||||
]
|
||||
}
|
52
docs/_static/diagrams/i2s/tdm_pcm_short.json
vendored
Normal file
52
docs/_static/diagrams/i2s/tdm_pcm_short.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "TDM PCM (short) Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": ".TE.........G",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..d.p.d.pd.pd"
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "010d......010d",
|
||||
"node": ".NH",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx2x|2x|2x|22x",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": "..H..FK.I...M",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"node": ".UA...B.C...D...J",
|
||||
"phase": -0.35
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"T<->E pulse",
|
||||
"E<->G Frame",
|
||||
"U<->A bit shift",
|
||||
"A<->B Slot 1",
|
||||
"B<->C ...",
|
||||
"C<->D Slot n",
|
||||
"T-U",
|
||||
"A-E",
|
||||
"K-B",
|
||||
"C-I",
|
||||
"D-M",
|
||||
"D-G"
|
||||
]
|
||||
}
|
62
docs/_static/diagrams/i2s/tdm_philip.json
vendored
Normal file
62
docs/_static/diagrams/i2s/tdm_philip.json
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"head": {
|
||||
"text": "TDM Philip Timing Diagram"
|
||||
},
|
||||
"signal": [
|
||||
{
|
||||
"node": "..E.........F.........G",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "BCLK",
|
||||
"wave": "p..d.p.d.pd.pd.p.d.pd.p"
|
||||
},
|
||||
{
|
||||
"name": "WS",
|
||||
"wave": "x0dd0.dd0dd1uu1.uu1uu0.",
|
||||
"node": ".TH...................R",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"name": "DIN / DOUT",
|
||||
"wave": "xx2x|22x|2x|2x|22x|2x|2",
|
||||
"data": [
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB",
|
||||
"LSB",
|
||||
"MSB"
|
||||
],
|
||||
"node": "..H...K...I.M...N...P.Q",
|
||||
"phase": -0.35
|
||||
},
|
||||
{
|
||||
"node": ".UA...B...C.D...J...L.S",
|
||||
"phase": -0.35
|
||||
}
|
||||
],
|
||||
"edge": [
|
||||
"E<->F Left Slots",
|
||||
"F<->G Right Slots",
|
||||
"U<->A bit shift",
|
||||
"A<->B Slot 1",
|
||||
"B<->C Slot 2",
|
||||
"C<->D ...",
|
||||
"D<->J Slot n",
|
||||
"J<->L Slot n+1",
|
||||
"L<->S ...",
|
||||
"A-E",
|
||||
"K-B",
|
||||
"C-I",
|
||||
"D-F",
|
||||
"J-N",
|
||||
"L-P",
|
||||
"Q-G",
|
||||
"S-R",
|
||||
"T-U"
|
||||
]
|
||||
}
|
@@ -67,7 +67,6 @@ INPUT = \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/gpio.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2c.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/ledc.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rtc_io.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/sdio_slave.h \
|
||||
@@ -156,7 +155,6 @@ INPUT = \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/esp_flash_err.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/gpio_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2c_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/lcd_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/ledc_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rtc_io_types.h \
|
||||
|
@@ -1,6 +1,10 @@
|
||||
INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
|
||||
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
|
||||
@@ -11,6 +15,7 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/esp_himem.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
|
||||
|
@@ -1,4 +1,9 @@
|
||||
INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_tdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
|
||||
@@ -7,4 +12,5 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_ds.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_hmac.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
|
||||
|
@@ -1,4 +1,8 @@
|
||||
INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
|
||||
@@ -7,4 +11,5 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_ds.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_hmac.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
|
||||
|
@@ -2,6 +2,9 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
|
||||
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
|
||||
@@ -10,6 +13,7 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_ds.h \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_hmac.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
|
||||
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \
|
||||
|
@@ -1,5 +1,10 @@
|
||||
INPUT += \
|
||||
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_std.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_tdm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
|
||||
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
|
||||
@@ -12,6 +17,7 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_hmac.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -254,4 +254,68 @@ LCD
|
||||
Dedicated GPIO Driver
|
||||
---------------------
|
||||
|
||||
- All of the dedicated GPIO related LL functionsn in ``cpu_ll.h`` have been moved to ``dedic_gpio_cpu_ll.h`` and renamed.
|
||||
- All of the dedicated GPIO related LL functionsn in ``cpu_ll.h`` have been moved to ``dedic_gpio_cpu_ll.h`` and renamed.
|
||||
|
||||
.. only:: SOC_I2S_SUPPORTED
|
||||
|
||||
I2S driver
|
||||
----------
|
||||
|
||||
{I2S_DRIVER_HEADERS:default=":component_file:`driver/include/driver/i2s_std.h`, :component_file:`driver/include/driver/i2s_pdm.h` or :component_file:`driver/include/driver/i2s_tdm.h`", esp32=":component_file:`driver/include/driver/i2s_std.h` or :component_file:`driver/include/driver/i2s_pdm.h`", esp32s2=":component_file:`driver/include/driver/i2s_std.h`"}
|
||||
|
||||
Shortcomings are exposed when supporting all the new features of ESP32-C3 & ESP32-S3 by the old I2S driver, so it is re-designed to make it more compatible and flexible to all the communication modes. New APIs are available by including corresponding mode header files {I2S_DRIVER_HEADERS}. Meanwhile, the old APIs in :component_file:`driver/deprecated/driver/i2s.h` are still supported for backward compatibility. But there will be warnings if you keep using the old APIs in your project, these warnings can be suppressed by the Kconfig option :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN`. Here is the general overview of the current I2S files:
|
||||
|
||||
.. figure:: ../../_static/diagrams/i2s/i2s_file_structure.png
|
||||
:align: center
|
||||
:alt: I2S File Structure
|
||||
|
||||
Breaking changes in Concepts
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- The minimum control unit in new I2S driver will be tx/rx channel instead of a whole I2S controller.
|
||||
|
||||
1. The tx/rx channel in a same I2S controller can be controlled separately, that means they will be initialized, started or stopped separately. Especially for ESP32-C3 and ESP32-S3, tx and rx channels in one controller can be configured to different clocks or modes now, they are able to work in a totally separate way which can help to save the resources of I2S controller. But for ESP32 and ESP32-S2, though their tx/rx can be controlled separately, some hardware resources are still shared by tx and rx, they might affect each other if they are configured to different configurations;
|
||||
2. The channels can be registered to an available I2S controller automatically by setting :cpp:enumerator:`i2s_port_t::I2S_NUM_AUTO` as I2S port id. The driver will help you to search for the available tx/rx channel. Of cause, driver can still support to be installed by a specific port;
|
||||
3. :c:type:`i2s_chan_handle_t` is the handle that used for identifying the I2S channels. All the APIs will require the channel handle, users need to maintain the channel handles by themselves;
|
||||
4. In order to distinguish tx/rx channel and sound channel, now the word 'channel' is only stand for the tx/rx channel in new driver, meanwhile the sound channel will be called 'slot'.
|
||||
|
||||
- I2S communication modes are extracted into three modes.
|
||||
|
||||
1. **Standard mode**: Standard mode always has two slots, it can support Philip, MSB and PCM(short sync) format, please refer to :component_file:`driver/include/driver/i2s_std.h` for details;
|
||||
2. **PDM mode**: PDM mode only support two slots with 16 bits data width, but the configurations of PDM TX and PDM RX are little bit different. For PDM TX, the sample rate can be set by :cpp:member:`i2s_pdm_tx_clk_config_t::sample_rate`, and its clock frequency is depended on the up-sampling configuration. For PDM RX, the sample rate can be set by :cpp:member:`i2s_pdm_rx_clk_config_t::sample_rate`, and its clock frequency is depended on the down-sampling configuration. Please refer to :component_file:`driver/include/driver/i2s_pdm.h` for details;
|
||||
3. **TDM mode**: TDM mode can support upto 16 slots. It can work in Philip, MSB, PCM(short sync) and PCM(long sync) format, please refer to :component_file:`driver/include/driver/i2s_tdm.h` for details;
|
||||
4. When allocating a new channel in a specific mode, must initialize this channel by corresponding function. It is strongly recommended to use the helper macros to generate the default configurations, in case the default values will be changed one day.
|
||||
|
||||
- States and state-machine are adopted in the new I2S driver to avoid APIs called in wrong state.
|
||||
|
||||
- The slot configurations and clock configurations can be configured separately.
|
||||
|
||||
1. Calling :cpp:func:`i2s_channel_init_std_mode`, :cpp:func:`i2s_channel_init_pdm_rx_mode`, :cpp:func:`i2s_channel_init_pdm_tx_mode` or :cpp:func:`i2s_channel_init_tdm_mode` to initialize the slot/clock/gpio_pin configurations;
|
||||
2. Calling :cpp:func:`i2s_channel_reconfig_std_slot`, :cpp:func:`i2s_channel_reconfig_pdm_rx_slot`, :cpp:func:`i2s_channel_reconfig_pdm_tx_slot` or :cpp:func:`i2s_channel_reconfig_tdm_slot` can change the slot configurations after initialization;
|
||||
3. Calling :cpp:func:`i2s_channel_reconfig_std_clock`, :cpp:func:`i2s_channel_reconfig_pdm_rx_clock`, :cpp:func:`i2s_channel_reconfig_pdm_tx_clock` or :cpp:func:`i2s_channel_reconfig_tdm_clock` can change the clock configurations after initialization;
|
||||
4. Calling :cpp:func:`i2s_channel_reconfig_std_gpio`, :cpp:func:`i2s_channel_reconfig_pdm_rx_gpio`, :cpp:func:`i2s_channel_reconfig_pdm_tx_gpio` or :cpp:func:`i2s_channel_reconfig_tdm_gpio` can change the gpio configurations after initialization.
|
||||
|
||||
- ADC and DAC modes are removed. They will only be supported in their own driver and legacy I2S driver.
|
||||
|
||||
- :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` can be aborted by :cpp:func:`i2s_channel_abort_reading_writing` now.
|
||||
|
||||
Breaking Changes in Usage
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To use the new I2S driver, please follow these steps:
|
||||
|
||||
1. Calling :cpp:func:`i2s_new_channel` to aquire the channel handles. We should specify the work role and I2S port in this step. Besides, the tx or rx channel handles will be generated by the driver. Inputting both two tx and rx handles is not necessary but at least one handle is needed. In the case of inputting both two handles, the driver will work at duplex mode, both tx and rx channel will be avaliable on a same port, and they will share the MCLK, BCLK and WS signal. But if only one of the tx or rx handle is inputted, this channel will only work in simplex mode.
|
||||
|
||||
2. Calling :func:`i2s_channel_init_std_mode`, :func:`i2s_channel_init_pdm_rx_mode`, :func:`i2s_channel_init_pdm_tx_mode` or :func:`i2s_channel_init_tdm_mode` to initialize the channel to the specified mode. Corresponding slot, clock and gpio configurations are needed in this step.
|
||||
|
||||
3. (Optional) Calling :cpp:func:`i2s_channel_register_event_callback` to register the ISR event callback functions. I2S events now can be received by the callback function synchronously, instead of from event queue asynchronously.
|
||||
|
||||
4. Calling :cpp:func:`i2s_channel_enable` to start the hardware of I2S channel. In the new driver, I2S won't start automatically after installed anymore, users are supposed to know clearly whether the channel has started or not.
|
||||
|
||||
5. Reading or writing data by :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`. Certainly, only rx channel handle is suppoesd to be inputted in :cpp:func:`i2s_channel_read` and tx channel handle in :cpp:func:`i2s_channel_write`.
|
||||
|
||||
6. (Optional) The slot, clock and gpio configurations can be changed by corresponding 'reconfig' functions, but :cpp:func:`i2s_channel_disable` must be called before updating the configurations.
|
||||
|
||||
7. Calling :cpp:func:`i2s_channel_disable` to stop the hardware of I2S channel.
|
||||
|
||||
8. Calling :cpp:func:`i2s_del_channel` to delete and release the resources of the channel if it is not needed any more, but the channel must be disabled before deleting it.
|
||||
|
@@ -20,7 +20,12 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
|
||||
#include "driver/i2s.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
|
||||
#include "sys/lock.h"
|
||||
|
||||
@@ -82,6 +87,9 @@ static _lock_t s_volume_lock;
|
||||
static TaskHandle_t s_vcs_task_hdl = NULL; /* handle for volume change simulation task */
|
||||
static uint8_t s_volume = 0; /* local volume value */
|
||||
static bool s_volume_notify; /* notify volume change or not */
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_chan_handle_t tx_chan = NULL;
|
||||
#endif
|
||||
|
||||
/********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
@@ -160,13 +168,10 @@ static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *even
|
||||
|
||||
void bt_i2s_driver_install(void)
|
||||
{
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
/* I2S configuration parameters */
|
||||
i2s_config_t i2s_config = {
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
|
||||
#else
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, /* only TX */
|
||||
#endif
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
|
||||
@@ -178,24 +183,43 @@ void bt_i2s_driver_install(void)
|
||||
};
|
||||
|
||||
/* enable I2S */
|
||||
i2s_driver_install(0, &i2s_config, 0, NULL);
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
i2s_set_pin(0, NULL);
|
||||
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
|
||||
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
|
||||
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
|
||||
#else
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.data_in_num = -1 /* not used */
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
i2s_set_pin(0, &pin_config);
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
#endif
|
||||
}
|
||||
|
||||
void bt_i2s_driver_uninstall(void)
|
||||
{
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_driver_uninstall(0);
|
||||
#else
|
||||
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
|
||||
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void volume_set_by_controller(uint8_t volume)
|
||||
@@ -286,8 +310,12 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
||||
} else if (oct0 & (0x01 << 4)) {
|
||||
sample_rate = 48000;
|
||||
}
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_set_clk(0, sample_rate, 16, 2);
|
||||
|
||||
#else
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
|
||||
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
|
||||
#endif
|
||||
ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x",
|
||||
a2d->audio_cfg.mcc.cie.sbc[0],
|
||||
a2d->audio_cfg.mcc.cie.sbc[1],
|
||||
|
@@ -14,7 +14,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "bt_app_core.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
|
||||
#include "driver/i2s.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
/*******************************
|
||||
@@ -38,6 +43,9 @@ static QueueHandle_t s_bt_app_task_queue = NULL; /* handle of work queue */
|
||||
static TaskHandle_t s_bt_app_task_handle = NULL; /* handle of application task */
|
||||
static TaskHandle_t s_bt_i2s_task_handle = NULL; /* handle of I2S task */
|
||||
static RingbufHandle_t s_ringbuf_i2s = NULL; /* handle of ringbuffer for I2S */
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
extern i2s_chan_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
/*******************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
@@ -99,7 +107,11 @@ static void bt_i2s_task_handler(void *arg)
|
||||
/* receive data from ringbuffer and write it to I2S DMA transmit buffer */
|
||||
data = (uint8_t *)xRingbufferReceive(s_ringbuf_i2s, &item_size, (TickType_t)portMAX_DELAY);
|
||||
if (item_size != 0){
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
|
||||
#else
|
||||
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
|
||||
#endif
|
||||
vRingbufferReturnItem(s_ringbuf_i2s, (void *)data);
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
/* device name */
|
||||
#define LOCAL_DEVICE_NAME "ESP_SPEAKER"
|
||||
|
@@ -7,3 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
|
||||
|
@@ -87,37 +87,48 @@ For example, after executing `esp_bt_gap_start_discovery()`, an event of `ESP_BT
|
||||
The main function installs I2S to play the audio. A loudspeaker, additional ADC or hardware requirements and possibly an external I2S codec may be needed.
|
||||
|
||||
```c
|
||||
/* I2S configuration parameters */
|
||||
i2s_config_t i2s_config = {
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
|
||||
#else
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, /* only TX */
|
||||
#endif
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 60,
|
||||
.intr_alloc_flags = 0, /* default interrupt priority */
|
||||
.tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */
|
||||
};
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
/* I2S configuration parameters */
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 60,
|
||||
.intr_alloc_flags = 0, /* default interrupt priority */
|
||||
.tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */
|
||||
};
|
||||
|
||||
/* enable I2S */
|
||||
i2s_driver_install(0, &i2s_config, 0, NULL);
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
i2s_set_pin(0, NULL);
|
||||
#else
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.data_in_num = -1 /* not used */
|
||||
};
|
||||
i2s_set_pin(0, &pin_config);
|
||||
#endif
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
|
||||
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
|
||||
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
|
||||
#else
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
#endif
|
||||
```
|
||||
|
||||
### Paring Parameter Settings
|
||||
|
@@ -21,7 +21,12 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
|
||||
#include "driver/i2s.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
|
||||
#include "sys/lock.h"
|
||||
|
||||
@@ -48,6 +53,9 @@ static _lock_t s_volume_lock;
|
||||
static TaskHandle_t s_vcs_task_hdl = NULL;
|
||||
static uint8_t s_volume = 0;
|
||||
static bool s_volume_notify;
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
extern i2s_chan_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
/* callback for A2DP sink */
|
||||
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
||||
@@ -162,7 +170,12 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
||||
} else if (oct0 & (0x01 << 4)) {
|
||||
sample_rate = 48000;
|
||||
}
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_set_clk(0, sample_rate, 16, 2);
|
||||
#else
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
|
||||
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x",
|
||||
a2d->audio_cfg.mcc.cie.sbc[0],
|
||||
|
@@ -14,8 +14,13 @@
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "bt_app_core.h"
|
||||
#include "driver/i2s.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
|
||||
#include "driver/i2s.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
|
||||
static void bt_app_task_handler(void *arg);
|
||||
static bool bt_app_send_msg(bt_app_msg_t *msg);
|
||||
@@ -24,7 +29,10 @@ static void bt_app_work_dispatched(bt_app_msg_t *msg);
|
||||
static QueueHandle_t s_bt_app_task_queue = NULL;
|
||||
static TaskHandle_t s_bt_app_task_handle = NULL;
|
||||
static TaskHandle_t s_bt_i2s_task_handle = NULL;
|
||||
static RingbufHandle_t s_ringbuf_i2s = NULL;;
|
||||
static RingbufHandle_t s_ringbuf_i2s = NULL;
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
extern i2s_chan_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
|
||||
{
|
||||
@@ -123,7 +131,11 @@ static void bt_i2s_task_handler(void *arg)
|
||||
for (;;) {
|
||||
data = (uint8_t *)xRingbufferReceive(s_ringbuf_i2s, &item_size, (TickType_t)portMAX_DELAY);
|
||||
if (item_size != 0){
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
|
||||
#else
|
||||
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
|
||||
#endif
|
||||
vRingbufferReturnItem(s_ringbuf_i2s,(void *)data);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -35,7 +35,12 @@
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
|
||||
#include "driver/i2s.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
@@ -70,6 +75,10 @@ typedef struct {
|
||||
static prepare_type_env_t a_prepare_write_env;
|
||||
static prepare_type_env_t b_prepare_write_env;
|
||||
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_chan_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
//Declare the static function
|
||||
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
@@ -681,36 +690,47 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
/* I2S configuration parameters */
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
|
||||
#else
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
|
||||
#endif
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 60,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.tx_desc_auto_clear = true //Auto clear tx descriptor on underflow
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 60,
|
||||
.intr_alloc_flags = 0, /* default interrupt priority */
|
||||
.tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */
|
||||
};
|
||||
|
||||
|
||||
i2s_driver_install(0, &i2s_config, 0, NULL);
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
i2s_set_pin(0, NULL);
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
|
||||
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
|
||||
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
|
||||
#else
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.data_in_num = -1 //Not used
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
i2s_set_pin(0, &pin_config);
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
#endif
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
|
@@ -29,3 +29,4 @@ CONFIG_BT_ACL_CONNECTIONS=4
|
||||
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=n
|
||||
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=n
|
||||
CONFIG_BT_SMP_ENABLE=y
|
||||
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
|
||||
|
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* ADC/DAC are not supported in the new I2S driver, but still available in the legacy I2S driver for backward compatibility
|
||||
* Please turn to the dedicated ADC/DAC driver instead */
|
||||
#pragma message("ADC/DAC on ESP32 will no longer supported via I2S driver")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -13,7 +23,6 @@
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
static const char* TAG = "ad/da";
|
||||
#define V_REF 1100
|
||||
#define ADC1_TEST_CHANNEL (ADC1_CHANNEL_7)
|
||||
@@ -277,9 +286,9 @@ void adc_read_task(void* arg)
|
||||
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, V_REF, &characteristics);
|
||||
while(1) {
|
||||
uint32_t voltage;
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
esp_adc_cal_get_voltage(ADC1_TEST_CHANNEL, &characteristics, &voltage);
|
||||
ESP_LOGI(TAG, "%d mV", voltage);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user