|
|
@@ -13,6 +13,8 @@
|
|
|
#include "esp_log.h"
|
|
|
#include "esp_rom_sys.h"
|
|
|
#include "esp_check.h"
|
|
|
+#include "esp_vfs_fat.h"
|
|
|
+#include "sdmmc_cmd.h"
|
|
|
#include "driver/i2s_std.h"
|
|
|
#include "driver/i2s_types.h"
|
|
|
#include "driver/i2s_pdm.h"
|
|
|
@@ -20,14 +22,22 @@
|
|
|
|
|
|
#include <sys/unistd.h>
|
|
|
#include <sys/stat.h>
|
|
|
+#include "dirent.h"
|
|
|
+#include "driver/sdmmc_host.h"
|
|
|
+#include <errno.h>
|
|
|
|
|
|
#include "esp_aec.h"
|
|
|
#include "tca9555_driver.h"
|
|
|
|
|
|
+#if ((SOC_SDMMC_HOST_SUPPORTED) && (FUNC_SDMMC_EN))
|
|
|
+#include "driver/sdmmc_host.h"
|
|
|
+#endif /* ((SOC_SDMMC_HOST_SUPPORTED) && (FUNC_SDMMC_EN)) */
|
|
|
+
|
|
|
|
|
|
#define ADC_I2S_CHANNEL 2
|
|
|
#define ADC_BITS_PER_SAMPLE 32
|
|
|
|
|
|
+static sdmmc_card_t *card;
|
|
|
static const char *TAG = "board";
|
|
|
static int s_play_sample_rate = 16000;
|
|
|
static int s_play_channel_format = 1;
|
|
|
@@ -48,6 +58,7 @@ static audio_codec_gpio_if_t *play_gpio_if = NULL;
|
|
|
static audio_codec_if_t *play_codec_if = NULL;
|
|
|
static esp_codec_dev_handle_t play_dev = NULL;
|
|
|
static i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
|
|
+static uint32_t SDCard_Size = 0;
|
|
|
static bool s_volume_buttons_task_started = false;
|
|
|
|
|
|
static aec_handle_t *aec = NULL;
|
|
|
@@ -58,7 +69,9 @@ static int16_t *out = NULL;
|
|
|
|
|
|
static esp_err_t bsp_aec_init(void)
|
|
|
{
|
|
|
- /* aec_create_from_config() is used instead of aec_create() so the NLP
|
|
|
+ /* Espressif's direct AEC flow: create, query the algorithm frame size,
|
|
|
+ * then allocate aligned signed-16-bit audio buffers for each frame.
|
|
|
+ * aec_create_from_config() is used instead of aec_create() so the NLP
|
|
|
* (residual echo suppression) level can be pushed to its strongest
|
|
|
* setting; a longer filter_length also helps cancel longer echo tails. */
|
|
|
aec_config_t aec_cfg = {
|
|
|
@@ -164,7 +177,7 @@ static esp_err_t i2c_master_init(void)
|
|
|
}
|
|
|
|
|
|
ESP_LOGI(TAG, "I2C bus initialized successfully");
|
|
|
- return ESP_OK;
|
|
|
+ return ESP_OK; // 返回 ESP_OK 表示成功
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -200,7 +213,20 @@ esp_err_t bsp_codec_adc_init(int sample_rate)
|
|
|
|
|
|
es7210_codec_cfg_t es7210_cfg = {
|
|
|
.ctrl_if = record_ctrl_if,
|
|
|
-
|
|
|
+ /*
|
|
|
+ * Microphone initialization for the ES7210 ADC codec.
|
|
|
+ * This board supports multiple mic inputs, and the current code enables all
|
|
|
+ * 4 available mic channels:
|
|
|
+ * MIC1 + MIC2 + MIC3 + MIC4
|
|
|
+ *
|
|
|
+ * For a 2-mic board, the intended setup is typically to enable only the two
|
|
|
+ * physical microphones that are wired to the board, for example:
|
|
|
+ * .mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC2;
|
|
|
+ *
|
|
|
+ * In other words, the current configuration is not "one mic only"; it is
|
|
|
+ * configured to allow all entries in the codec, even though the app logic only
|
|
|
+ * reads one channel from the resulting stream.
|
|
|
+ */
|
|
|
.mic_selected =
|
|
|
ES7210_SEL_MIC1 |
|
|
|
ES7210_SEL_MIC2 |
|
|
|
@@ -233,7 +259,15 @@ esp_err_t bsp_codec_adc_init(int sample_rate)
|
|
|
|
|
|
esp_codec_dev_sample_info_t fs = {
|
|
|
.sample_rate = sample_rate,
|
|
|
-
|
|
|
+ /*
|
|
|
+ * Stereo is opened here, which matches a 2-channel mic stream.
|
|
|
+ * If the board is wired as a 2-mic stereo pair, the code can use both
|
|
|
+ * channels by reading left and right samples from the interleaved buffer.
|
|
|
+ * Example:
|
|
|
+ * // .mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC2;
|
|
|
+ * // left = mic_buffer[i * 2 + 0];
|
|
|
+ * // right = mic_buffer[i * 2 + 1];
|
|
|
+ */
|
|
|
.channel = 2,
|
|
|
.bits_per_sample = 16,
|
|
|
};
|
|
|
@@ -269,6 +303,7 @@ esp_err_t bsp_codec_dac_init(int sample_rate, int channel_format, int bits_per_c
|
|
|
{
|
|
|
esp_err_t ret_val = ESP_OK;
|
|
|
|
|
|
+ // Do initialize of related interface: data_if, ctrl_if and gpio_if
|
|
|
audio_codec_i2s_cfg_t i2s_cfg = {
|
|
|
.port = I2S_NUM_1,
|
|
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
|
|
@@ -396,6 +431,7 @@ esp_err_t esp_audio_get_play_vol(int *volume)
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|
|
|
+// static esp_err_t bsp_i2s_init(i2s_port_t i2s_num, uint32_t sample_rate, i2s_channel_fmt_t channel_format, i2s_bits_per_chan_t bits_per_chan)
|
|
|
static esp_err_t bsp_i2s_init(int i2s_num, uint32_t sample_rate, int channel_format, int bits_per_chan)
|
|
|
{
|
|
|
esp_err_t ret_val = ESP_OK;
|
|
|
@@ -486,6 +522,7 @@ static esp_err_t bsp_codec_init(
|
|
|
int dac_channel_format,
|
|
|
int dac_bits_per_chan)
|
|
|
{
|
|
|
+ //ESP_LOGE(TAG, "bsp_codec_adc_init START");
|
|
|
esp_err_t ret;
|
|
|
|
|
|
ret = bsp_codec_adc_init(adc_sample_rate);
|
|
|
@@ -597,12 +634,21 @@ esp_err_t esp_get_feed_data(
|
|
|
int buffer_len)
|
|
|
{
|
|
|
|
|
|
+ // ESP_LOGE(TAG, "NEW esp_get_feed_data CALLED");
|
|
|
+
|
|
|
if(record_dev == NULL)
|
|
|
{
|
|
|
ESP_LOGE(TAG,"record_dev NULL");
|
|
|
return ESP_ERR_INVALID_STATE;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ // ESP_LOGI(TAG,
|
|
|
+ // "read buffer=%p size=%d",
|
|
|
+ // buffer,
|
|
|
+ // buffer_len);
|
|
|
+
|
|
|
+
|
|
|
esp_err_t ret;
|
|
|
|
|
|
ret = esp_codec_dev_read(
|
|
|
@@ -610,6 +656,12 @@ esp_err_t esp_get_feed_data(
|
|
|
buffer,
|
|
|
buffer_len);
|
|
|
|
|
|
+
|
|
|
+ // ESP_LOGI(TAG,
|
|
|
+ // "codec read ret=%s",
|
|
|
+ // esp_err_to_name(ret));
|
|
|
+
|
|
|
+
|
|
|
if(ret != ESP_OK)
|
|
|
{
|
|
|
return ret;
|
|
|
@@ -646,7 +698,8 @@ char* esp_get_input_format(void)
|
|
|
|
|
|
static void bsp_volume_buttons_task(void *arg)
|
|
|
{
|
|
|
-
|
|
|
+ /* K1/K3 are wired through the TCA9555 IO expander, not raw ESP32 GPIOs
|
|
|
+ * (GPIO0 is the BOOT/RESET strapping pin, so it must not be used here). */
|
|
|
int last_key1 = 1;
|
|
|
int last_key3 = 1;
|
|
|
|
|
|
@@ -702,6 +755,8 @@ static esp_err_t bsp_volume_buttons_init(void)
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|
|
|
+ /* K1/K3 input direction is already configured on the TCA9555 expander by
|
|
|
+ * tca9555_driver_init(); no direct GPIO configuration is needed here. */
|
|
|
BaseType_t task_created = xTaskCreate(bsp_volume_buttons_task, "vol_btns", 4096, NULL, 5, NULL);
|
|
|
if (task_created != pdPASS) {
|
|
|
return ESP_FAIL;
|
|
|
@@ -746,7 +801,16 @@ else
|
|
|
{
|
|
|
ESP_LOGI(TAG,"bsp_codec_init OK");
|
|
|
}
|
|
|
-
|
|
|
+ /* Initialize PA */
|
|
|
+ /*gpio_config_t io_conf;
|
|
|
+ memset(&io_conf, 0, sizeof(io_conf));
|
|
|
+ io_conf.intr_type = GPIO_INTR_DISABLE;
|
|
|
+ io_conf.mode = GPIO_MODE_OUTPUT;
|
|
|
+ io_conf.pin_bit_mask = ((1ULL << GPIO_PWR_CTRL));
|
|
|
+ io_conf.pull_down_en = 0;
|
|
|
+ io_conf.pull_up_en = 0;
|
|
|
+ gpio_config(&io_conf);
|
|
|
+ */
|
|
|
gpio_set_level(GPIO_PWR_CTRL, 1);
|
|
|
|
|
|
esp_err_t button_ret = bsp_volume_buttons_init();
|
|
|
@@ -757,3 +821,190 @@ else
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|
|
|
+esp_err_t esp_sdcard_init(char *mount_point, size_t max_files)
|
|
|
+{
|
|
|
+ if (NULL != card) {
|
|
|
+ return ESP_ERR_INVALID_STATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if SD crad is supported */
|
|
|
+ if (!FUNC_SDMMC_EN && !FUNC_SDSPI_EN) {
|
|
|
+ ESP_LOGE(TAG, "SDMMC and SDSPI not supported on this board!");
|
|
|
+ return ESP_ERR_NOT_SUPPORTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ esp_err_t ret_val = ESP_OK;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Options for mounting the filesystem.
|
|
|
+ * If format_if_mount_failed is set to true, SD card will be partitioned and
|
|
|
+ * formatted in case when mounting fails.
|
|
|
+ *
|
|
|
+ */
|
|
|
+ esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
|
|
+ .format_if_mount_failed = false,
|
|
|
+ .max_files = max_files,
|
|
|
+ .allocation_unit_size = 16 * 1024
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Use settings defined above to initialize SD card and mount FAT filesystem.
|
|
|
+ * Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
|
|
|
+ * Please check its source code and implement error recovery when developing
|
|
|
+ * production applications.
|
|
|
+ *
|
|
|
+ */
|
|
|
+ sdmmc_host_t host =
|
|
|
+#if FUNC_SDMMC_EN
|
|
|
+ SDMMC_HOST_DEFAULT();
|
|
|
+#else
|
|
|
+ SDSPI_HOST_DEFAULT();
|
|
|
+ spi_bus_config_t bus_cfg = {
|
|
|
+ .mosi_io_num = GPIO_SDSPI_MOSI,
|
|
|
+ .miso_io_num = GPIO_SDSPI_MISO,
|
|
|
+ .sclk_io_num = GPIO_SDSPI_SCLK,
|
|
|
+ .quadwp_io_num = GPIO_NUM_NC,
|
|
|
+ .quadhd_io_num = GPIO_NUM_NC,
|
|
|
+ .max_transfer_sz = 4000,
|
|
|
+ };
|
|
|
+ ret_val = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CH_AUTO);
|
|
|
+ if (ret_val != ESP_OK) {
|
|
|
+ ESP_LOGE(TAG, "Failed to initialize bus.");
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief This initializes the slot without card detect (CD) and write protect (WP) signals.
|
|
|
+ * Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
|
|
+ *
|
|
|
+ */
|
|
|
+#if FUNC_SDMMC_EN
|
|
|
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
|
|
+#else
|
|
|
+ sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
|
|
|
+#endif
|
|
|
+
|
|
|
+#if FUNC_SDMMC_EN
|
|
|
+ /* Config SD data width. 0, 4 or 8. Currently for SD card, 8 bit is not supported. */
|
|
|
+ slot_config.width = SDMMC_BUS_WIDTH;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief On chips where the GPIOs used for SD card can be configured, set them in
|
|
|
+ * the slot_config structure.
|
|
|
+ *
|
|
|
+ */
|
|
|
+#if SOC_SDMMC_USE_GPIO_MATRIX
|
|
|
+ slot_config.clk = GPIO_SDMMC_CLK;
|
|
|
+ slot_config.cmd = GPIO_SDMMC_CMD;
|
|
|
+ slot_config.d0 = GPIO_SDMMC_D0;
|
|
|
+ slot_config.d1 = GPIO_SDMMC_D1;
|
|
|
+ slot_config.d2 = GPIO_SDMMC_D2;
|
|
|
+ slot_config.d3 = GPIO_SDMMC_D3;
|
|
|
+#endif
|
|
|
+ slot_config.cd = GPIO_SDMMC_DET;
|
|
|
+ slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
|
|
+#else
|
|
|
+ slot_config.gpio_cs = GPIO_SDSPI_CS;
|
|
|
+ slot_config.host_id = host.slot;
|
|
|
+#endif
|
|
|
+ /**
|
|
|
+ * @brief Enable internal pullups on enabled pins. The internal pullups
|
|
|
+ * are insufficient however, please make sure 10k external pullups are
|
|
|
+ * connected on the bus. This is for debug / example purpose only.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* get FAT filesystem on SD card registered in VFS. */
|
|
|
+ ret_val =
|
|
|
+#if FUNC_SDMMC_EN
|
|
|
+ esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
|
|
+#else
|
|
|
+ esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Check for SDMMC mount result. */
|
|
|
+ if (ret_val != ESP_OK) {
|
|
|
+ if (ret_val == ESP_FAIL) {
|
|
|
+ ESP_LOGE(TAG, "Failed to mount filesystem. "
|
|
|
+ "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
|
|
+ } else {
|
|
|
+ ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
|
|
+ "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret_val));
|
|
|
+ }
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Card has been initialized, print its properties. */
|
|
|
+ sdmmc_card_print_info(stdout, card);
|
|
|
+ SDCard_Size = ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024);
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+esp_err_t esp_sdcard_deinit(char *mount_point)
|
|
|
+{
|
|
|
+ if (NULL == mount_point) {
|
|
|
+ return ESP_ERR_INVALID_STATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Unmount an SD card from the FAT filesystem and release resources acquired */
|
|
|
+ esp_err_t ret_val = esp_vfs_fat_sdcard_unmount(mount_point, card);
|
|
|
+
|
|
|
+ /* Make SD/MMC card information structure pointer NULL */
|
|
|
+ card = NULL;
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+#define MOUNT_POINT "/sdcard"
|
|
|
+
|
|
|
+static const char *SD_TAG = "SD";
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][MAX_FILE_NAME_SIZE], uint16_t maxFiles)
|
|
|
+{
|
|
|
+ DIR *dir = opendir(directory);
|
|
|
+ if (dir == NULL) {
|
|
|
+ ESP_LOGE(SD_TAG, "Path: <%s> does not exist", directory);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t fileCount = 0;
|
|
|
+ struct dirent *entry;
|
|
|
+
|
|
|
+ while ((entry = readdir(dir)) != NULL && fileCount < maxFiles) {
|
|
|
+
|
|
|
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char *dot = strrchr(entry->d_name, '.');
|
|
|
+ if (dot != NULL && dot != entry->d_name) {
|
|
|
+
|
|
|
+ if (strcasecmp(dot, fileExtension) == 0) {
|
|
|
+ strncpy(File_Name[fileCount], entry->d_name, MAX_FILE_NAME_SIZE - 1);
|
|
|
+ File_Name[fileCount][MAX_FILE_NAME_SIZE - 1] = '\0';
|
|
|
+
|
|
|
+ char filePath[MAX_PATH_SIZE];
|
|
|
+ snprintf(filePath, MAX_PATH_SIZE, "%s/%s", directory, entry->d_name);
|
|
|
+
|
|
|
+ printf("File found: %s\r\n", filePath);
|
|
|
+ fileCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else{
|
|
|
+
|
|
|
+ // printf("No extension found for file: %s\r\n", entry->d_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ closedir(dir);
|
|
|
+
|
|
|
+ if (fileCount > 0) {
|
|
|
+ ESP_LOGI(SD_TAG, "Retrieved %d files with extension '%s'", fileCount, fileExtension);
|
|
|
+ } else {
|
|
|
+ ESP_LOGW(SD_TAG, "No files with extension '%s' found in directory: %s", fileExtension, directory);
|
|
|
+ }
|
|
|
+
|
|
|
+ return fileCount;
|
|
|
+}
|