/* * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/i2s.h" #include "esp_system.h" #include "esp_check.h" #include "es8311.h" /* I2C port and GPIOs */ #define I2C_NUM (0) #ifdef CONFIG_IDF_TARGET_ESP32C3 #define I2C_SDA_IO (GPIO_NUM_17) #define I2C_SCL_IO (GPIO_NUM_16) #else #define I2C_SDA_IO (GPIO_NUM_15) #define I2C_SCL_IO (GPIO_NUM_14) #endif /* I2S port and GPIOs */ #define I2S_NUM (0) #define I2S_MCK_IO (GPIO_NUM_0) #define I2S_BCK_IO (GPIO_NUM_4) #define I2S_WS_IO (GPIO_NUM_5) #define I2S_DO_IO (GPIO_NUM_18) #define I2S_DI_IO (GPIO_NUM_19) /* Example configurations */ #define EXAMPLE_RECV_BUF_SIZE (2048) #define EXAMPLE_SAMPLE_RATE (16000) #define EXAMPLE_MCLK_MULTIPLE I2S_MCLK_MULTIPLE_256 #define EXAMPLE_VOICE_VOLUME CONFIG_EXAMPLE_VOICE_VOLUME #if CONFIG_EXAMPLE_MODE_ECHO #define EXAMPLE_MIC_GAIN CONFIG_EXAMPLE_MIC_GAIN #endif static const char *TAG = "i2s_es8311"; static const char err_reason[][30] = {"input param is invalid", "operation timeout" }; /* Import music file as buffer */ #if CONFIG_EXAMPLE_MODE_MUSIC extern const uint8_t music_pcm_start[] asm("_binary_canon_pcm_start"); extern const uint8_t music_pcm_end[] asm("_binary_canon_pcm_end"); #endif static esp_err_t es8311_codec_init(void) { /* Initialize I2C peripheral */ i2c_config_t es_i2c_cfg = { .sda_io_num = I2C_SDA_IO, .scl_io_num = I2C_SCL_IO, .mode = I2C_MODE_MASTER, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 100000, }; ESP_RETURN_ON_ERROR(i2c_param_config(I2C_NUM, &es_i2c_cfg), TAG, "config i2c failed"); ESP_RETURN_ON_ERROR(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0), TAG, "install i2c driver failed"); /* Initialize es8311 codec */ es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0); ESP_RETURN_ON_FALSE(es_handle, ESP_FAIL, TAG, "es8311 create failed"); es8311_clock_config_t es_clk = { .mclk_from_mclk_pin = true, .sample_frequency = EXAMPLE_SAMPLE_RATE }; es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16); ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE), TAG, "set es8311 sample frequency failed"); ESP_RETURN_ON_ERROR(es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL), TAG, "set es8311 volume failed"); ESP_RETURN_ON_ERROR(es8311_microphone_config(es_handle, false), TAG, "set es8311 microphone failed"); #if CONFIG_EXAMPLE_MODE_ECHO ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(es_handle, EXAMPLE_MIC_GAIN), TAG, "set es8311 microphone gain faield"); #endif return ESP_OK; } static esp_err_t i2s_driver_init(void) { i2s_config_t i2s_cfg = { .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX, .sample_rate = EXAMPLE_SAMPLE_RATE, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .tx_desc_auto_clear = true, #if SOC_I2S_SUPPORTS_TDM .total_chan = 2, .chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1, .left_align = false, .big_edin = false, .bit_order_msb = false, .skip_msk = false, #endif .dma_desc_num = 8, .dma_frame_num = 64, .use_apll = false, .mclk_multiple = EXAMPLE_MCLK_MULTIPLE, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, }; ESP_RETURN_ON_ERROR(i2s_driver_install(I2S_NUM, &i2s_cfg, 0, NULL), TAG, "install i2s failed"); i2s_pin_config_t i2s_pin_cfg = { .mck_io_num = I2S_MCK_IO, .bck_io_num = I2S_BCK_IO, .ws_io_num = I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO }; ESP_RETURN_ON_ERROR(i2s_set_pin(I2S_NUM, &i2s_pin_cfg), TAG, "set i2s pins failed"); return ESP_OK; } #if CONFIG_EXAMPLE_MODE_MUSIC static void i2s_music(void *args) { esp_err_t ret = ESP_OK; size_t bytes_write = 0; while (1) { /* Write music to earphone */ ret = i2s_write(I2S_NUM, music_pcm_start, music_pcm_end - music_pcm_start, &bytes_write, portMAX_DELAY); if (ret != ESP_OK) { /* Since we set timeout to 'portMAX_DELAY' in 'i2s_write' so you won't reach here unless you set other timeout value, if timeout detected, it means write operation failed. */ ESP_LOGE(TAG, "[music] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]); abort(); } /* Clear DMA buffer to avoid noise from legacy data in buffer */ i2s_zero_dma_buffer(I2S_NUM); if (bytes_write > 0) { ESP_LOGI(TAG, "[music] i2s music played, %d bytes are written.", bytes_write); } else { ESP_LOGE(TAG, "[music] i2s music play falied."); abort(); } vTaskDelay(1000 / portTICK_RATE_MS); } vTaskDelete(NULL); } #else static void i2s_echo(void *args) { int *mic_data = malloc(EXAMPLE_RECV_BUF_SIZE); if (!mic_data) { ESP_LOGE(TAG, "[echo] No memory for read data buffer"); abort(); } esp_err_t ret = ESP_OK; size_t bytes_read = 0; size_t bytes_write = 0; ESP_LOGI(TAG, "[echo] Echo start"); while (1) { memset(mic_data, 0, EXAMPLE_RECV_BUF_SIZE); /* Read sample data from mic */ ret = i2s_read(I2S_NUM, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_read, 100); if (ret != ESP_OK) { ESP_LOGE(TAG, "[echo] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]); abort(); } /* Write sample data to earphone */ ret = i2s_write(I2S_NUM, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_write, 100); if (ret != ESP_OK) { ESP_LOGE(TAG, "[echo] i2s write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]); abort(); } if (bytes_read != bytes_write) { ESP_LOGW(TAG, "[echo] %d bytes read but only %d bytes are written", bytes_read, bytes_write); } } vTaskDelete(NULL); } #endif void app_main(void) { /* Initialize i2s peripheral */ if (i2s_driver_init() != ESP_OK) { ESP_LOGE(TAG, "i2s driver init failed"); abort(); } /* Initialize i2c peripheral and config es8311 codec by i2c */ if (es8311_codec_init() != ESP_OK) { ESP_LOGE(TAG, "es8311 codec init failed"); abort(); } #if CONFIG_EXAMPLE_MODE_MUSIC /* Play a piece of music in music mode */ xTaskCreate(i2s_music, "i2s_music", 4096, NULL, 5, NULL); #else /* Echo the sound from MIC in echo mode */ xTaskCreate(i2s_echo, "i2s_echo", 8192, NULL, 5, NULL); #endif }