From 76066747e7095439f79c393aae26bc4c064b1190 Mon Sep 17 00:00:00 2001 From: Pavel Borcin Date: Tue, 23 Jan 2024 16:33:19 +0100 Subject: [PATCH] feat(s3_korvo_1): Add audio and sd card support --- bsp/esp32_s3_korvo_1/CMakeLists.txt | 4 +- bsp/esp32_s3_korvo_1/Kconfig | 40 ++++ bsp/esp32_s3_korvo_1/README.md | 1 + bsp/esp32_s3_korvo_1/esp32_s3_korvo_1.c | 163 ++++++++++++++- bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf4.c | 109 +++++++++- bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf5.c | 130 +++++++++++- bsp/esp32_s3_korvo_1/idf_component.yml | 6 +- .../include/bsp/esp32_s3_korvo_1.h | 196 +++++++++++++++++- examples/audio/sdkconfig.bsp.esp32_s3_korvo_1 | 11 + 9 files changed, 649 insertions(+), 11 deletions(-) create mode 100644 examples/audio/sdkconfig.bsp.esp32_s3_korvo_1 diff --git a/bsp/esp32_s3_korvo_1/CMakeLists.txt b/bsp/esp32_s3_korvo_1/CMakeLists.txt index ca4cd82e..a9da924f 100644 --- a/bsp/esp32_s3_korvo_1/CMakeLists.txt +++ b/bsp/esp32_s3_korvo_1/CMakeLists.txt @@ -1,10 +1,10 @@ #IDF version is less than IDF5.0 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0") set(SRC_VER "esp32_s3_korvo_1_idf4.c") - set(REQ spiffs) + set(REQ driver spiffs fatfs) else() set(SRC_VER "esp32_s3_korvo_1_idf5.c") - set(REQ spiffs esp_adc) + set(REQ driver spiffs fatfs esp_adc) endif() idf_component_register( diff --git a/bsp/esp32_s3_korvo_1/Kconfig b/bsp/esp32_s3_korvo_1/Kconfig index 3d56c356..9017e724 100644 --- a/bsp/esp32_s3_korvo_1/Kconfig +++ b/bsp/esp32_s3_korvo_1/Kconfig @@ -6,6 +6,46 @@ menu "Board Support Package" help Error check assert the application before returning the error code. + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 0 + range 0 1 + help + ESP32S3 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "uSD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format uSD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "uSD card mount point" + default "/sdcard" + help + Mount point of the uSD card in the Virtual File System + + config BSP_SD_MAX_FILES + int "Max files supported for SD VFS" + default 2 + help + Supported max files for SD in the Virtual File System. + endmenu + menu "SPIFFS - Virtual File System" config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL bool "Format SPIFFS if mounting fails" diff --git a/bsp/esp32_s3_korvo_1/README.md b/bsp/esp32_s3_korvo_1/README.md index c7301495..22559d20 100644 --- a/bsp/esp32_s3_korvo_1/README.md +++ b/bsp/esp32_s3_korvo_1/README.md @@ -15,6 +15,7 @@ The ESP32-S3-Korvo-1 board consists of two parts: the main board (ESP32-S3-Korvo | component | version | |----------------------------------------------------------------------------------------------|-----------| | [espressif/button](https://components.espressif.com/components/espressif/button) | >=2.5,<4.0| +|[espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev)| ~1.1.0 | |[espressif/led_indicator](https://components.espressif.com/components/espressif/led_indicator)|>=0.7,<=0.8| | idf | >=4.4 | diff --git a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1.c b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1.c index df18bde0..254cafbc 100644 --- a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1.c +++ b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1.c @@ -1,9 +1,12 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "driver/i2c.h" #include "esp_err.h" #include "esp_log.h" @@ -11,10 +14,130 @@ #include "bsp/esp-bsp.h" #include "bsp_err_check.h" #include "esp_spiffs.h" +#include "esp_codec_dev.h" +#include "esp_codec_dev_defaults.h" #include "led_indicator.h" +#include "esp_vfs_fat.h" static const char *TAG = "S3-Korvo-1"; +static bool i2c_initialized = false; +sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = BSP_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = BSP_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = CONFIG_BSP_I2C_CLK_SPEED_HZ + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf)); + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM)); + i2c_initialized = false; + return ESP_OK; +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void) +{ + const audio_codec_data_if_t *i2s_data_if = bsp_audio_get_codec_itf_spk(); + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_audio_init(NULL)); + i2s_data_if = bsp_audio_get_codec_itf_spk(); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = false, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + BSP_NULL_CHECK(es8311_dev, NULL); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_OUT, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + const audio_codec_data_if_t *i2s_data_if = bsp_audio_get_codec_itf_mic(); + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_audio_init(NULL)); + i2s_data_if = bsp_audio_get_codec_itf_mic(); + } + assert(i2s_data_if); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES7210_CODEC_DEFAULT_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + es7210_codec_cfg_t es7210_cfg = { + .ctrl_if = i2c_ctrl_if, + .mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC2, + }; + const audio_codec_if_t *es7210_dev = es7210_codec_new(&es7210_cfg); + BSP_NULL_CHECK(es7210_dev, NULL); + + esp_codec_dev_cfg_t codec_es7210_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es7210_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es7210_dev_cfg); +} + /** * @brief led configuration structure * @@ -196,3 +319,41 @@ esp_err_t bsp_spiffs_unmount(void) { return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); } + +esp_err_t bsp_sdcard_mount(void) +{ + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = CONFIG_BSP_SD_MAX_FILES, + .allocation_unit_size = 16 * 1024 + }; + + const sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + const sdmmc_slot_config_t slot_config = { + .clk = BSP_SD_CLK, + .cmd = BSP_SD_CMD, + .d0 = BSP_SD_D0, + .d1 = GPIO_NUM_NC, + .d2 = GPIO_NUM_NC, + .d3 = GPIO_NUM_NC, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = SDMMC_SLOT_NO_CD, + .wp = SDMMC_SLOT_NO_WP, + .width = 1, + .flags = 0, + }; + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); +} diff --git a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf4.c b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf4.c index 7102eda8..460e22bd 100644 --- a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf4.c +++ b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf4.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,10 +7,117 @@ #include "esp_err.h" #include "bsp/esp32_s3_korvo_1.h" #include "bsp_err_check.h" +#include "driver/adc.h" +#include "hal/i2s_types.h" #include "esp_adc_cal.h" +#include "esp_codec_dev_defaults.h" +static const char *TAG = "S3-Korvo-1"; + +#define BSP_I2S0_SIMPLEX_MONO_CFG(_sample_rate) \ + { \ + .mode = I2S_MODE_MASTER | I2S_MODE_TX, \ + .sample_rate = _sample_rate, \ + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, \ + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, \ + .communication_format = I2S_COMM_FORMAT_STAND_I2S, \ + .dma_buf_count = 3, \ + .dma_buf_len = 1024, \ + .tx_desc_auto_clear = true, \ + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM \ + } + +/* This board has 3 microphones, therefore using I2S TDM mode, it allows using more than 2 channels */ +#define BSP_I2S1_CFG(_sample_rate) \ + { \ + .mode = I2S_MODE_MASTER | I2S_MODE_RX, \ + .sample_rate = _sample_rate, \ + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, \ + .channel_format = I2S_CHANNEL_FMT_MULTIPLE, \ + .communication_format = I2S_COMM_FORMAT_STAND_I2S, \ + .dma_buf_count = 6, \ + .dma_buf_len = 1024, \ + .chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1, \ + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM \ + } + +static const audio_codec_data_if_t *i2s_data_if_spk = NULL; /* Codec data interface */ +static const audio_codec_data_if_t *i2s_data_if_mic = NULL; /* Codec data interface */ static esp_adc_cal_characteristics_t bsp_adc_chars; +esp_err_t bsp_audio_init(const i2s_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + + if (i2s_data_if_spk != NULL) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + const i2s_pin_config_t i2s_pin_config_spk = { + .mck_io_num = BSP_I2S0_MCLK, + .bck_io_num = BSP_I2S0_SCLK, + .ws_io_num = BSP_I2S0_LCLK, + .data_out_num = BSP_I2S0_DOUT, + .data_in_num = BSP_I2S0_DSIN + }; + + const i2s_pin_config_t i2s_pin_config_mic = { + .mck_io_num = BSP_I2S1_MCLK, + .bck_io_num = BSP_I2S1_SCLK, + .ws_io_num = BSP_I2S1_LCLK, + .data_out_num = BSP_I2S1_DOUT, + .data_in_num = BSP_I2S1_DSIN + }; + + /* Setup I2S channels */ + + /* I2S_NUM_0 */ + const i2s_config_t std_cfg_default = BSP_I2S0_SIMPLEX_MONO_CFG(16000); + const i2s_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, p_i2s_cfg, 0, NULL)); + ESP_GOTO_ON_ERROR(i2s_set_pin(I2S_NUM_0, &i2s_pin_config_spk), err, TAG, "I2S set pin failed"); + + audio_codec_i2s_cfg_t i2s_cfg_spk = { + .port = I2S_NUM_0, + }; + i2s_data_if_spk = audio_codec_new_i2s_data(&i2s_cfg_spk); + BSP_NULL_CHECK_GOTO(i2s_data_if_spk, err); + + /* I2S_NUM_1 */ + const i2s_config_t tdm_cfg_default = BSP_I2S1_CFG(16000); + ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_1, &tdm_cfg_default, 0, NULL)); + ESP_GOTO_ON_ERROR(i2s_set_pin(I2S_NUM_1, &i2s_pin_config_mic), err, TAG, "I2S set pin failed"); + + audio_codec_i2s_cfg_t i2s_cfg_mic = { + .port = I2S_NUM_1, + }; + i2s_data_if_mic = audio_codec_new_i2s_data(&i2s_cfg_mic); + BSP_NULL_CHECK_GOTO(i2s_data_if_mic, err); + + return ESP_OK; + +err: + i2s_driver_uninstall(I2S_NUM_0); + i2s_driver_uninstall(I2S_NUM_1); + return ret; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf_spk(void) +{ + return i2s_data_if_spk; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf_mic(void) +{ + return i2s_data_if_mic; +} + esp_err_t bsp_adc_initialize(void) { esp_err_t ret = ESP_OK; diff --git a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf5.c b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf5.c index 83d23f4b..cda789c4 100644 --- a/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf5.c +++ b/bsp/esp32_s3_korvo_1/esp32_s3_korvo_1_idf5.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,11 +8,137 @@ #include "esp_adc/adc_oneshot.h" #include "bsp/esp32_s3_korvo_1.h" #include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" -static const char *TAG = "S3-KORVO-1"; +static const char *TAG = "S3-Korvo-1"; +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if_spk = NULL; /* Codec data interface */ +static const audio_codec_data_if_t *i2s_data_if_mic = NULL; /* Codec data interface */ static adc_oneshot_unit_handle_t bsp_adc_handle = NULL; +/* Can be used for i2s_std_gpio_config_t and/or i2s_std_config_t initialization */ +#define BSP_I2S0_GPIO_CFG \ + { \ + .mclk = BSP_I2S0_MCLK, \ + .bclk = BSP_I2S0_SCLK, \ + .ws = BSP_I2S0_LCLK, \ + .dout = BSP_I2S0_DOUT, \ + .din = BSP_I2S0_DSIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +#define BSP_I2S1_GPIO_CFG \ + { \ + .mclk = BSP_I2S1_MCLK, \ + .bclk = BSP_I2S1_SCLK, \ + .ws = BSP_I2S1_LCLK, \ + .dout = BSP_I2S1_DOUT, \ + .din = BSP_I2S1_DSIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +/* This configuration is used by default in bsp_audio_init() */ +#define BSP_I2S0_SIMPLEX_MONO_CFG(_sample_rate) \ + { \ + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \ + .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \ + .gpio_cfg = BSP_I2S0_GPIO_CFG, \ + } + +/* This board has 3 microphones, therefore using I2S TDM mode, it allows using more than 2 channels */ +#define BSP_I2S1_CFG(_sample_rate) \ + { \ + .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(_sample_rate), \ + .slot_cfg = I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT0 | I2S_TDM_SLOT1),\ + .gpio_cfg = BSP_I2S1_GPIO_CFG, \ + } + +static esp_err_t bsp_i2s_chan_init(int i2s_num, i2s_chan_handle_t *tx_chan, i2s_chan_handle_t *rx_chan) +{ + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(i2s_num, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + return i2s_new_channel(&chan_cfg, tx_chan, rx_chan); +} + +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2s_chan_init(I2S_NUM_0, &i2s_tx_chan, NULL)); + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2s_chan_init(I2S_NUM_1, NULL, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S0_SIMPLEX_MONO_CFG(22050); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + const i2s_tdm_config_t tdm_cfg_default = BSP_I2S1_CFG(16000); + + if (i2s_tx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_tx_chan), err, TAG, "I2S enabling failed"); + } + if (i2s_rx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_tdm_mode(i2s_rx_chan, &tdm_cfg_default), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "I2S enabling failed"); + } + + audio_codec_i2s_cfg_t i2s_cfg_spk = { + .port = I2S_NUM_0, + .rx_handle = NULL, + .tx_handle = i2s_tx_chan, + }; + i2s_data_if_spk = audio_codec_new_i2s_data(&i2s_cfg_spk); + BSP_NULL_CHECK_GOTO(i2s_data_if_spk, err); + + audio_codec_i2s_cfg_t i2s_cfg_mic = { + .port = I2S_NUM_1, + .rx_handle = i2s_rx_chan, + .tx_handle = NULL, + }; + i2s_data_if_mic = audio_codec_new_i2s_data(&i2s_cfg_mic); + BSP_NULL_CHECK_GOTO(i2s_data_if_mic, err); + + return ESP_OK; + +err: + if (i2s_tx_chan) { + i2s_del_channel(i2s_tx_chan); + } + if (i2s_rx_chan) { + i2s_del_channel(i2s_rx_chan); + } + + return ret; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf_spk(void) +{ + return i2s_data_if_spk; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf_mic(void) +{ + return i2s_data_if_mic; +} + esp_err_t bsp_adc_initialize(void) { /* ADC was initialized before */ diff --git a/bsp/esp32_s3_korvo_1/idf_component.yml b/bsp/esp32_s3_korvo_1/idf_component.yml index 7ce44496..77ec555e 100644 --- a/bsp/esp32_s3_korvo_1/idf_component.yml +++ b/bsp/esp32_s3_korvo_1/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.0~1" +version: "1.1.0" description: Board Support Package for ESP32-S3-KORVO-1 url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_s3_korvo_1 @@ -15,3 +15,7 @@ dependencies: button: version: ">=2.5,<4.0" public: true + + esp_codec_dev: + version: "~1.1.0" + public: true diff --git a/bsp/esp32_s3_korvo_1/include/bsp/esp32_s3_korvo_1.h b/bsp/esp32_s3_korvo_1/include/bsp/esp32_s3_korvo_1.h index ac9a3975..c2e6fa61 100644 --- a/bsp/esp32_s3_korvo_1/include/bsp/esp32_s3_korvo_1.h +++ b/bsp/esp32_s3_korvo_1/include/bsp/esp32_s3_korvo_1.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,18 +13,62 @@ #pragma once #include "iot_button.h" +#include "esp_codec_dev.h" #include "led_indicator.h" +#include "driver/sdmmc_host.h" + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +#include "driver/i2s.h" +#else +#include "driver/i2s_std.h" +#include "driver/i2s_tdm.h" +#endif +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +#define BSP_CAPS_DISPLAY 0 +#define BSP_CAPS_TOUCH 0 +#define BSP_CAPS_BUTTONS 1 +#define BSP_CAPS_AUDIO 1 +#define BSP_CAPS_AUDIO_SPEAKER 1 +#define BSP_CAPS_AUDIO_MIC 1 +#define BSP_CAPS_SDCARD 1 +#define BSP_CAPS_IMU 0 /************************************************************************************************** * ESP32-S3-Korvo-1 pinout **************************************************************************************************/ +/* I2C */ +#define BSP_I2C_SCL (GPIO_NUM_2) +#define BSP_I2C_SDA (GPIO_NUM_1) + +/* Audio */ +#define BSP_I2S0_SCLK (GPIO_NUM_40) +#define BSP_I2S0_MCLK (GPIO_NUM_42) +#define BSP_I2S0_LCLK (GPIO_NUM_41) +#define BSP_I2S0_DOUT (GPIO_NUM_39) +#define BSP_I2S0_DSIN (GPIO_NUM_NC) + +#define BSP_I2S1_SCLK (GPIO_NUM_10) +#define BSP_I2S1_MCLK (GPIO_NUM_20) +#define BSP_I2S1_LCLK (GPIO_NUM_9) +#define BSP_I2S1_DOUT (GPIO_NUM_NC) +#define BSP_I2S1_DSIN (GPIO_NUM_11) + +#define BSP_POWER_AMP_IO (GPIO_NUM_38) /* Leds */ -#define BSP_LED_RGB_GPIO (GPIO_NUM_19) -#define BSP_LED_NUM (12) +#define BSP_LED_RGB_GPIO (GPIO_NUM_19) +#define BSP_LED_NUM (12) /* Buttons */ -#define BSP_BUTTONS_IO (GPIO_NUM_8) // All 6 buttons mapped to this GPIO +#define BSP_BUTTONS_IO (GPIO_NUM_8) // All 6 buttons mapped to this GPIO + +/* uSD card */ +#define BSP_SD_D0 (GPIO_NUM_16) +#define BSP_SD_CMD (GPIO_NUM_17) +#define BSP_SD_CLK (GPIO_NUM_18) #ifdef __cplusplus extern "C" { @@ -62,6 +106,111 @@ typedef enum { BSP_BUTTON_NUM, } bsp_button_t; +/************************************************************************************************** + * + * I2S audio interface + * + * There are two devices connected to the I2S peripheral: + * - Codec ES8311 for output (playback) path + * - ADC ES7210 for input (recording) path + * + * For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init(). + * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init(). + * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio. + * Example audio play: + * \code{.c} + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, wav_bytes_len); + * esp_codec_dev_close(spk_codec_dev); + * \endcode + **************************************************************************************************/ + +/** + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel() + * @warning The type of i2s_config param is depending on IDF version. + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @param[out] tx_channel I2S TX channel + * @param[out] rx_channel I2S RX channel + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +esp_err_t bsp_audio_init(const i2s_config_t *i2s_config); +#else +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); +#endif + +/** + * @brief Get es8311 codec I2S interface (initialized in bsp_audio_init) + * + * @return + * - Pointer to codec I2S interface handle or NULL when error occured + */ +const audio_codec_data_if_t *bsp_audio_get_codec_itf_spk(void); + +/** + * @brief Get es7210 codec I2S interface (initialized in bsp_audio_init) + * + * @return + * - Pointer to codec I2S interface handle or NULL when error occured + */ +const audio_codec_data_if_t *bsp_audio_get_codec_itf_mic(void); + +/** + * @brief Initialize speaker codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void); + +/** + * @brief Initialize microphone codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); + +/************************************************************************************************** + * + * I2C interface + * + * There are multiple devices connected to I2C peripheral: + * - Codec ES8311 (configuration only) + * - ADC ES7210 (configuration only) + * + * After initialization of I2C, use BSP_I2C_NUM macro when creating I2C device drivers + **************************************************************************************************/ +#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM + +/** + * @brief Init I2C driver + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_i2c_init(void); + +/** + * @brief Deinit I2C driver and free its resources + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * + */ +esp_err_t bsp_i2c_deinit(void); + /************************************************************************************************** * * ADC interface @@ -174,6 +323,45 @@ esp_err_t bsp_spiffs_mount(void); */ esp_err_t bsp_spiffs_unmount(void); +/************************************************************************************************** + * + * uSD card + * + * After mounting the uSD card, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT +extern sdmmc_card_t *bsp_sdcard; + +/** + * @brief Mount microSD card to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_mount(void); + +/** + * @brief Unmount microSD card from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers + */ +esp_err_t bsp_sdcard_unmount(void); + #ifdef __cplusplus } #endif diff --git a/examples/audio/sdkconfig.bsp.esp32_s3_korvo_1 b/examples/audio/sdkconfig.bsp.esp32_s3_korvo_1 new file mode 100644 index 00000000..ed81ae06 --- /dev/null +++ b/examples/audio/sdkconfig.bsp.esp32_s3_korvo_1 @@ -0,0 +1,11 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_USE_MEMMAP=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIFFS_PAGE_SIZE=1024