From f48b647ad56c5928d014d7f813eea88088df93c4 Mon Sep 17 00:00:00 2001 From: wangyuanze Date: Tue, 23 Aug 2022 15:10:13 +0800 Subject: [PATCH] example: add es7210 4-ch tdm i2s record example --- examples/peripherals/.build-test-rules.yml | 4 + .../i2s_codec/i2s_es7210_tdm/CMakeLists.txt | 6 + .../i2s/i2s_codec/i2s_es7210_tdm/README.md | 85 +++++ .../components/es7210/CMakeLists.txt | 4 + .../i2s_es7210_tdm/components/es7210/es7210.c | 341 ++++++++++++++++++ .../i2s_es7210_tdm/components/es7210/es7210.h | 203 +++++++++++ .../i2s_es7210_tdm/main/CMakeLists.txt | 3 + .../main/i2s_es7210_record_example.c | 259 +++++++++++++ .../i2s_es7210_tdm/pytest_i2s_es7210_tdm.py | 14 + 9 files changed, 919 insertions(+) create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/CMakeLists.txt create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/README.md create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/CMakeLists.txt create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.c create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.h create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/CMakeLists.txt create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/i2s_es7210_record_example.c create mode 100644 examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/pytest_i2s_es7210_tdm.py diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 5412877979..741b6e777a 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -50,6 +50,10 @@ examples/peripherals/i2s/i2s_basic/i2s_tdm: disable: - if: SOC_I2S_SUPPORTS_TDM != 1 +examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm: + disable: + - if: SOC_I2S_SUPPORTS_TDM != 1 + examples/peripherals/i2s/i2s_codec/i2s_es8311: disable: - if: SOC_I2S_SUPPORTED != 1 diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/CMakeLists.txt b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/CMakeLists.txt new file mode 100644 index 0000000000..28f0ad3ad3 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(es7210_tdm_record_example) diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/README.md b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/README.md new file mode 100644 index 0000000000..b4a4e857b5 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/README.md @@ -0,0 +1,85 @@ +| Supported Targets | ESP32-C3 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# I2S TDM Example -- ES7210 4-Ch ADC Codec + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +I2S on `ESP32S3` and `ESP32C3` supports [TDM mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#tdm-mode), in which multiple slots can be transmitted by standard I2S connection. + +This example demonstrates how to use I2S TDM mode to record 4 MICs connected to [ES7210](http://www.everest-semi.com/pdf/ES7210%20PB.pdf) codec. ES7210 has 4 TDM modes, which are `ES7210_I2S_FMT_I2S` `ES7210_I2S_FMT_LJ` `ES7210_I2S_FMT_DSP_A` and `ES7210_I2S_FMT_DSP_B`, and they are all supported by I2S TDM driver. Relation between ES7210 TDM modes and I2S Driver TDM modes is shown in the following table. + +| Mode of ES7210 TDM | Mode of I2S Driver TDM | +| :------------------: | :--------------------: | +| ES7210_I2S_FMT_I2S | Philip format | +| ES7210_I2S_FMT_LJ | MSB format | +| ES7210_I2S_FMT_DSP_A | PCM short format | +| ES7210_I2S_FMT_DSP_B | PCM long format | + +Recorded voice will be saved to SD card in `wav` format, and can be played or processed on PC. + +## How to Use Example + +### Hardware Required + +* A development board with any supported Espressif SOC chip (see `Supported Targets` table above) +* An ES7210 module with 4 MICs connected +* A SPI SD card module +* A SD card reader and a PC (if you want to play recorded voice) + +All the GPIO used in this example can be changed according to your board, by macros `EXAMPLE_xxx_IO` defined at the beginning of [i2s_es7210_record_example.c](main/i2s_es7210_record_example.c). + +### Configure the project + +* Set the target of the build by following command, where TARGET can be `esp32s3` or `esp32c3`. +``` +idf.py set-target TARGET +``` +* Change value of `EXAMPLE_I2S_FORMAT` to check I2S driver's functionality on different I2S formats. +* Change `EXAMPLE_ES7210_MIC_GAIN` and `EXAMPLE_ES7210_MIC_BIAS` accoirding your MIC's specs if needed. +* Change `EXAMPLE_ES7210_ADC_VOLUME` if recorded voice is too loud or too quite. + +Note: it's better to adjust `EXAMPLE_ES7210_MIC_GAIN` first. If adjusting MIC gain doesn't meet your demand, you can then adjust `EXAMPLE_ES7210_ADC_VOLUME`. That is to say, it's better to adjust analog gain than digital gain. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Console Output + +``` +I (277) cpu_start: Starting scheduler. +I (281) example: Create I2S receive channel +I (281) example: Configure I2S receive channel to TDM mode +I (291) example: Init I2C used to configure ES7210 +I (291) example: Configure ES7210 codec parameters +I (301) ES7210: format: standard i2s, bit width: 16, tdm mode enabled +I (311) ES7210: sample rate: 48000Hz, mclk frequency: 12288000Hz +I (311) example: Initializing SPI bus for SD card +I (321) example: Mounting SD card +I (321) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (371) sdspi_transaction: cmd=5, R1 response: command not supported +I (381) example: Card size: 14772MB, speed: 20MHz +I (381) example: Opening file /RECORD.WAV +I (391) example: Recording: 1/10s +I (1401) example: Recording: 2/10s +I (2411) example: Recording: 3/10s +I (3401) example: Recording: 4/10s +I (4411) example: Recording: 5/10s +I (5411) example: Recording: 6/10s +I (6421) example: Recording: 7/10s +I (7411) example: Recording: 8/10s +I (8411) example: Recording: 9/10s +I (9401) example: Recording: 10/10s +I (10401) example: Recording done! Flushing file buffer +I (10431) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (10431) example: You can now safely remove the card, recorded file is /RECORD.WAV +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/CMakeLists.txt b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/CMakeLists.txt new file mode 100644 index 0000000000..026081fca6 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "es7210.c" + INCLUDE_DIRS "." + PRIV_REQUIRES driver +) diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.c b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.c new file mode 100644 index 0000000000..479e67a4a5 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.c @@ -0,0 +1,341 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "es7210.h" +#include "esp_log.h" +#include "esp_check.h" + +const static char *TAG = "ES7210"; + +#define IS_ES7210_I2S_FMT(val) (((val)==ES7210_I2S_FMT_I2S) || ((val)==ES7210_I2S_FMT_LJ) || \ + ((val)==ES7210_I2S_FMT_DSP_A) || ((val)==ES7210_I2S_FMT_DSP_B)) + +#define IS_ES7210_I2S_BITS(val) (((val)==ES7210_I2S_BITS_24B) || ((val)==ES7210_I2S_BITS_20B) || \ + ((val)==ES7210_I2S_BITS_18B) || ((val)==ES7210_I2S_BITS_16B) || ((val)==ES7210_I2S_BITS_32B)) + +#define IS_ES7210_MIC_GAIN(val) (((val) >= ES7210_MIC_GAIN_0DB) && ((val) <= ES7210_MIC_GAIN_37_5DB)) + +#define IS_ES7210_MIC_BIAS(val) (((val)==ES7210_MIC_BIAS_2V18) || ((val)==ES7210_MIC_BIAS_2V26) || \ + ((val)==ES7210_MIC_BIAS_2V36) || ((val)==ES7210_MIC_BIAS_2V45) || ((val)==ES7210_MIC_BIAS_2V55) || \ + ((val)==ES7210_MIC_BIAS_2V66) || ((val)==ES7210_MIC_BIAS_2V78) || ((val)==ES7210_MIC_BIAS_2V87)) + +#define ES7210_WRITE_REG(reg_addr, reg_value) do { \ + ESP_RETURN_ON_ERROR(es7210_write_reg(handle, (reg_addr), (reg_value)), \ + TAG, "i2c communication error while writing "#reg_addr); \ +} while(0) + +struct es7210_dev_t { + i2c_port_t i2c_port; // TODO: update to i2c handle in future driver-NG + uint8_t i2c_addr; +}; + +/** + * @brief Clock coefficient structure + * + */ +typedef struct { + uint32_t mclk; /*!< mclk frequency */ + uint32_t lrck; /*!< lrck */ + uint8_t ss_ds; /*!< not used */ + uint8_t adc_div; /*!< adcclk divider */ + uint8_t dll; /*!< dll_bypass */ + uint8_t doubler; /*!< doubler enable */ + uint8_t osr; /*!< adc osr */ + uint8_t mclk_src; /*!< select mclk source */ + uint32_t lrck_h; /*!< The high 4 bits of lrck */ + uint32_t lrck_l; /*!< The low 8 bits of lrck */ +} coeff_div_t; + +/** + * @brief ES7210 clock coefficient lookup table + * + */ +static const coeff_div_t es7210_coeff_div[] = { +// mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl + /* 8k */ + {12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, + {16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00}, + {19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60}, + {4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 11.025k */ + {11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + + /* 12k */ + {12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40}, + + /* 16k */ + {4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80}, + {16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00}, + + /* 22.05k */ + {11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 24k */ + {12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20}, + + /* 32k */ + {12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80}, + {16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58}, + + /* 44.1k */ + {11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + + /* 48k */ + {12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90}, + + /* 64k */ + {16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + {19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + + /* 96k */ + {12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, +}; + +/** + * @brief Get coefficient from coefficient table + * + * @param mclk Desired MCLK value + * @param lrck Desired LRCK vaule + * @return Coefficient struct, NULL if desired value cannot be achieved + */ +static const coeff_div_t *es7210_get_coeff(uint32_t mclk, uint32_t lrck) +{ + for (int i = 0; i < sizeof(es7210_coeff_div) / sizeof(coeff_div_t); i++) { + if (es7210_coeff_div[i].lrck == lrck && es7210_coeff_div[i].mclk == mclk) + return &es7210_coeff_div[i]; + } + return NULL; +} + +static esp_err_t es7210_write_reg(es7210_dev_handle_t handle, uint8_t reg_addr, uint8_t reg_val) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle"); + esp_err_t ret = ESP_OK; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ESP_GOTO_ON_FALSE(cmd, ESP_ERR_NO_MEM, err, TAG, "memory allocation for i2c cmd handle failed"); + + ESP_GOTO_ON_ERROR(i2c_master_start(cmd), err, TAG, "error while appending i2c command"); + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd, handle->i2c_addr<<1 | I2C_MASTER_WRITE, true), + err, TAG, "error while appending i2c command"); + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd, reg_addr, true), err, + TAG, "error while appending i2c command"); + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd, reg_val, true), err, + TAG, "error while appending i2c command"); + ESP_GOTO_ON_ERROR(i2c_master_stop(cmd), err, TAG, "error while appending i2c command"); + + ESP_GOTO_ON_ERROR(i2c_master_cmd_begin(handle->i2c_port, cmd, pdMS_TO_TICKS(1000)), + err, TAG, "error while writing register"); +err: + if(cmd) { + i2c_cmd_link_delete(cmd); + } + return ret; +} + +static esp_err_t es7210_set_i2s_format(es7210_dev_handle_t handle, es7210_i2s_fmt_t i2s_format, + es7210_i2s_bits_t bit_width, bool tdm_enable) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + ESP_RETURN_ON_FALSE(IS_ES7210_I2S_FMT(i2s_format), ESP_ERR_INVALID_ARG, TAG, "invalid i2s format argument"); + ESP_RETURN_ON_FALSE(IS_ES7210_I2S_BITS(bit_width), ESP_ERR_INVALID_ARG, TAG, "invalid i2s bit width argument"); + + uint8_t reg_val = 0; + + switch (bit_width) { + case ES7210_I2S_BITS_16B: + reg_val = 0x60; + break; + case ES7210_I2S_BITS_18B: + reg_val = 0x40; + break; + case ES7210_I2S_BITS_20B: + reg_val = 0x20; + break; + case ES7210_I2S_BITS_24B: + reg_val = 0x00; + break; + case ES7210_I2S_BITS_32B: + reg_val = 0x80; + break; + default: + abort(); + } + ES7210_WRITE_REG(ES7210_SDP_INTERFACE1_REG11, i2s_format | reg_val); + + const char *mode_str = NULL; + switch (i2s_format) { + case ES7210_I2S_FMT_I2S: + reg_val = 0x02; + mode_str = "standard i2s"; + break; + case ES7210_I2S_FMT_LJ: + reg_val = 0x02; + mode_str = "left justify"; + break; + case ES7210_I2S_FMT_DSP_A: + reg_val = 0x01; + mode_str = "DSP-A"; + break; + case ES7210_I2S_FMT_DSP_B: + reg_val = 0x01; + mode_str = "DSP-B"; + break; + default: + abort(); + } + + if (tdm_enable) { // enable 1xFS TDM + ES7210_WRITE_REG(ES7210_SDP_INTERFACE2_REG12, reg_val); + } else { + ES7210_WRITE_REG(ES7210_SDP_INTERFACE2_REG12, 0x00); + } + + ESP_LOGI(TAG, "format: %s, bit width: %d, tdm mode %s", mode_str, bit_width, tdm_enable ? "enabled" : "disabled"); + return ESP_OK; +} + +static esp_err_t es7210_set_i2s_sample_rate(es7210_dev_handle_t handle, uint32_t sample_rate_hz, uint32_t mclk_ratio) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + + uint32_t mclk_freq_hz = sample_rate_hz * mclk_ratio; + const coeff_div_t *coeff_div = es7210_get_coeff(mclk_freq_hz, sample_rate_hz); + ESP_RETURN_ON_FALSE(coeff_div, ESP_ERR_NOT_SUPPORTED, TAG, + "unable to set %"PRIu32"Hz sample rate with %"PRIu32"Hz MCLK", sample_rate_hz, mclk_freq_hz); + /* Set osr */ + ES7210_WRITE_REG(ES7210_OSR_REG07, coeff_div->osr); + /* Set adc_div & doubler & dll */ + ES7210_WRITE_REG(ES7210_MAINCLK_REG02, (coeff_div->adc_div) | (coeff_div->doubler << 6) | (coeff_div->dll << 7)); + /* Set lrck */ + ES7210_WRITE_REG(ES7210_LRCK_DIVH_REG04, coeff_div->lrck_h); + ES7210_WRITE_REG(ES7210_LRCK_DIVL_REG05, coeff_div->lrck_l); + + ESP_LOGI(TAG, "sample rate: %"PRIu32"Hz, mclk frequency: %"PRIu32"Hz", sample_rate_hz, mclk_freq_hz); + return ESP_OK; +} + +static esp_err_t es7210_set_mic_gain(es7210_dev_handle_t handle, es7210_mic_gain_t mic_gain) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + ESP_RETURN_ON_FALSE(IS_ES7210_MIC_GAIN(mic_gain), ESP_ERR_INVALID_ARG, TAG, "invalid mic gain value"); + + ES7210_WRITE_REG(ES7210_MIC1_GAIN_REG43, mic_gain | 0x10); + ES7210_WRITE_REG(ES7210_MIC2_GAIN_REG44, mic_gain | 0x10); + ES7210_WRITE_REG(ES7210_MIC3_GAIN_REG45, mic_gain | 0x10); + ES7210_WRITE_REG(ES7210_MIC4_GAIN_REG46, mic_gain | 0x10); + + return ESP_OK; +} + +static esp_err_t es7210_set_mic_bias(es7210_dev_handle_t handle, es7210_mic_bias_t mic_bias) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + ESP_RETURN_ON_FALSE(IS_ES7210_MIC_BIAS(mic_bias), ESP_ERR_INVALID_ARG, TAG, "invalid mic bias value"); + + ES7210_WRITE_REG(ES7210_MIC12_BIAS_REG41, mic_bias); + ES7210_WRITE_REG(ES7210_MIC34_BIAS_REG42, mic_bias); + + return ESP_OK; +} + +esp_err_t es7210_new_codec(const es7210_i2c_config_t *i2c_conf, es7210_dev_handle_t *handle_out) +{ + ESP_RETURN_ON_FALSE(i2c_conf, ESP_ERR_INVALID_ARG, TAG, "invalid device config pointer"); + ESP_RETURN_ON_FALSE(handle_out, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + + struct es7210_dev_t *handle = calloc(1, sizeof(struct es7210_dev_t)); + ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "memory allocation for device handler failed"); + + handle->i2c_port = i2c_conf->i2c_port; // TODO: check i2c handle in future driver-NG + handle->i2c_addr = i2c_conf->i2c_addr; + + *handle_out = handle; + return ESP_OK; +} + +esp_err_t es7210_del_codec(es7210_dev_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + + free(handle); + + return ESP_OK; +} + +esp_err_t es7210_config_codec(es7210_dev_handle_t handle, const es7210_codec_config_t *codec_conf) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + ESP_RETURN_ON_FALSE(codec_conf, ESP_ERR_INVALID_ARG, TAG, "invalid codec config pointer"); + + /* Perform software reset */ + ES7210_WRITE_REG(ES7210_RESET_REG00, 0xFF); + ES7210_WRITE_REG(ES7210_RESET_REG00, 0x32); + /* Set the initialization time when device powers up */ + ES7210_WRITE_REG(ES7210_TIME_CONTROL0_REG09, 0x30); + ES7210_WRITE_REG(ES7210_TIME_CONTROL1_REG0A, 0x30); + /* Configure HPF for ADC1-4 */ + ES7210_WRITE_REG(ES7210_ADC12_HPF1_REG23, 0x2A); + ES7210_WRITE_REG(ES7210_ADC12_HPF2_REG22, 0x0A); + ES7210_WRITE_REG(ES7210_ADC34_HPF1_REG21, 0x2A); + ES7210_WRITE_REG(ES7210_ADC34_HPF2_REG20, 0x0A); + /* Set bits per sample to 16, data protocal to I2S, enable 1xFS TDM */ + ESP_RETURN_ON_ERROR(es7210_set_i2s_format(handle, codec_conf->i2s_format, codec_conf->bit_width, + codec_conf->flags.tdm_enable), TAG, "error while setting i2s format"); + /* Configure analog power and VMID voltage */ + ES7210_WRITE_REG(ES7210_ANALOG_REG40, 0xC3); + /* Set MIC14 bias to 2.87V */ + ESP_RETURN_ON_ERROR(es7210_set_mic_bias(handle, codec_conf->mic_bias), TAG, "error while setting mic bias"); + /* Set MIC1-4 gain to 30dB */ + ESP_RETURN_ON_ERROR(es7210_set_mic_gain(handle, codec_conf->mic_gain), TAG, "error while setting mic gain"); + /* Power on MIC1-4 */ + ES7210_WRITE_REG(ES7210_MIC1_POWER_REG47, 0x08); + ES7210_WRITE_REG(ES7210_MIC2_POWER_REG48, 0x08); + ES7210_WRITE_REG(ES7210_MIC3_POWER_REG49, 0x08); + ES7210_WRITE_REG(ES7210_MIC4_POWER_REG4A, 0x08); + /* Set ADC sample rate to 48kHz */ + ESP_RETURN_ON_ERROR(es7210_set_i2s_sample_rate(handle, codec_conf->sample_rate_hz, codec_conf->mclk_ratio), + TAG, "error while setting sample rate"); + /* Power down DLL */ + ES7210_WRITE_REG(ES7210_POWER_DOWN_REG06, 0x04); + /* Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power */ + ES7210_WRITE_REG(ES7210_MIC12_POWER_REG4B, 0x0F); + ES7210_WRITE_REG(ES7210_MIC34_POWER_REG4C, 0x0F); + /* Enable device */ + ES7210_WRITE_REG(ES7210_RESET_REG00, 0x71); + ES7210_WRITE_REG(ES7210_RESET_REG00, 0x41); + + return ESP_OK; +} + +esp_err_t es7210_config_volume(es7210_dev_handle_t handle, int8_t volume_db) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + ESP_RETURN_ON_FALSE(volume_db >= -95 && volume_db <= 32, ESP_ERR_INVALID_ARG, TAG, "invalid volume range"); + + /* + * reg_val: 0x00 represents -95.5dB, 0xBF represents 0dB (default after reset), + * and 0xFF represents +32dB, with a 0.5dB step + */ + uint8_t reg_val = 191 + volume_db * 2; + + ES7210_WRITE_REG(ES7210_ADC1_DIRECT_DB_REG1B, reg_val); + ES7210_WRITE_REG(ES7210_ADC2_DIRECT_DB_REG1C, reg_val); + ES7210_WRITE_REG(ES7210_ADC3_DIRECT_DB_REG1D, reg_val); + ES7210_WRITE_REG(ES7210_ADC4_DIRECT_DB_REG1E, reg_val); + + return ESP_OK; +} diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.h b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.h new file mode 100644 index 0000000000..3051e96f3d --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/components/es7210/es7210.h @@ -0,0 +1,203 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "driver/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ES7210 register addresses */ +#define ES7210_RESET_REG00 0x00 /* Reset control */ +#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */ +#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */ +#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */ +#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */ +#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */ +#define ES7210_POWER_DOWN_REG06 0x06 /* power down */ +#define ES7210_OSR_REG07 0x07 +#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */ +#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/ +#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */ +#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */ +#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */ +#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */ +#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */ +#define ES7210_ALC_SEL_REG16 0x16 /* Set ALC mode */ +#define ES7210_ADC1_DIRECT_DB_REG1B 0x1B +#define ES7210_ADC2_DIRECT_DB_REG1C 0x1C +#define ES7210_ADC3_DIRECT_DB_REG1D 0x1D +#define ES7210_ADC4_DIRECT_DB_REG1E 0x1E /* ADC direct dB when ALC close, ALC max gain when ALC open */ +#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */ +#define ES7210_ADC34_HPF1_REG21 0x21 +#define ES7210_ADC12_HPF2_REG22 0x22 +#define ES7210_ADC12_HPF1_REG23 0x23 +#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */ +#define ES7210_MIC12_BIAS_REG41 0x41 +#define ES7210_MIC34_BIAS_REG42 0x42 +#define ES7210_MIC1_GAIN_REG43 0x43 +#define ES7210_MIC2_GAIN_REG44 0x44 +#define ES7210_MIC3_GAIN_REG45 0x45 +#define ES7210_MIC4_GAIN_REG46 0x46 +#define ES7210_MIC1_POWER_REG47 0x47 +#define ES7210_MIC2_POWER_REG48 0x48 +#define ES7210_MIC3_POWER_REG49 0x49 +#define ES7210_MIC4_POWER_REG4A 0x4A +#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */ +#define ES7210_MIC34_POWER_REG4C 0x4C + +/** + * @brief Select I2S interface format for ES7210 + */ +typedef enum { + ES7210_I2S_FMT_I2S = 0x00, /*!< normal I2S format */ + ES7210_I2S_FMT_LJ = 0x01, /*!< left justify format */ + ES7210_I2S_FMT_DSP_A = 0x03, /*!< DSP-A format, MSB is available on 2nd SCLK rising edge after LRCK rising edge */ + ES7210_I2S_FMT_DSP_B = 0x13 /*!< DSP-B format, MSB is available on 1st SCLK rising edge after LRCK rising edge */ +} es7210_i2s_fmt_t; + +/** + * @brief Select I2S bit width for ES7210 + * + */ +typedef enum { + ES7210_I2S_BITS_16B = 16, /*!< 16-bit I2S mode */ + ES7210_I2S_BITS_18B = 18, /*!< 18-bit I2S mode */ + ES7210_I2S_BITS_20B = 20, /*!< 20-bit I2S mode */ + ES7210_I2S_BITS_24B = 24, /*!< 24-bit I2S mode */ + ES7210_I2S_BITS_32B = 32 /*!< 32-bit I2S mode */ +} es7210_i2s_bits_t; + +/** + * @brief Select MIC gain for ES7210 + * + */ +typedef enum { + ES7210_MIC_GAIN_0DB = 0, /*!< 0dB MIC gain */ + ES7210_MIC_GAIN_3DB = 1, /*!< 3dB MIC gain */ + ES7210_MIC_GAIN_6DB = 2, /*!< 6dB MIC gain */ + ES7210_MIC_GAIN_9DB = 3, /*!< 9dB MIC gain */ + ES7210_MIC_GAIN_12DB = 4, /*!< 12dB MIC gain */ + ES7210_MIC_GAIN_15DB = 5, /*!< 15dB MIC gain */ + ES7210_MIC_GAIN_18DB = 6, /*!< 18dB MIC gain */ + ES7210_MIC_GAIN_21DB = 7, /*!< 21dB MIC gain */ + ES7210_MIC_GAIN_24DB = 8, /*!< 24dB MIC gain */ + ES7210_MIC_GAIN_27DB = 9, /*!< 27dB MIC gain */ + ES7210_MIC_GAIN_30DB = 10, /*!< 30dB MIC gain */ + ES7210_MIC_GAIN_33DB = 11, /*!< 33dB MIC gain */ + ES7210_MIC_GAIN_34_5DB = 12, /*!< 34.5dB MIC gain */ + ES7210_MIC_GAIN_36DB = 13, /*!< 36dB MIC gain */ + ES7210_MIC_GAIN_37_5DB = 14 /*!< 37.5dB MIC gain */ +} es7210_mic_gain_t; + +/** + * @brief Select MIC bias for ES7210 + * + */ +typedef enum { + ES7210_MIC_BIAS_2V18 = 0x00, /*!< 2.18V MIC bias */ + ES7210_MIC_BIAS_2V26 = 0x10, /*!< 2.26V MIC bias */ + ES7210_MIC_BIAS_2V36 = 0x20, /*!< 2.36V MIC bias */ + ES7210_MIC_BIAS_2V45 = 0x30, /*!< 2.45V MIC bias */ + ES7210_MIC_BIAS_2V55 = 0x40, /*!< 2.55V MIC bias */ + ES7210_MIC_BIAS_2V66 = 0x50, /*!< 2.66V MIC bias */ + ES7210_MIC_BIAS_2V78 = 0x60, /*!< 2.78V MIC bias */ + ES7210_MIC_BIAS_2V87 = 0x70 /*!< 2.87V MIC bias */ +} es7210_mic_bias_t; + +/** + * @brief Type of es7210 device handle + * + */ +typedef struct es7210_dev_t* es7210_dev_handle_t; + +/** + * @brief ES7210 I2C config struct + * + */ +typedef struct { + i2c_port_t i2c_port; /*!< I2C port used to connecte ES7210 device */ + uint8_t i2c_addr; /*!< I2C address of ES7210 device, can be 0x40 0x41 0x42 or 0x43 according to A0 and A1 pin */ +} es7210_i2c_config_t; + +/** + * @brief ES7210 codec config struct + * + */ +typedef struct { + uint32_t sample_rate_hz; /*!< Sample rate in Hz, common values are supported */ + uint32_t mclk_ratio; /*!< MCLK-to-Sample-rate clock ratio, typically 256 */ + es7210_i2s_fmt_t i2s_format; /*!< I2S format of ES7210's output, can be any value in es7210_i2s_fmt_t */ + es7210_i2s_bits_t bit_width; /*!< I2S bit width of ES7210's output, can be any value in es7210_i2s_bits_t */ + es7210_mic_bias_t mic_bias; /*!< Bias volatge of analog MIC, please refer to your MIC's datasheet */ + es7210_mic_gain_t mic_gain; /*!< Gain of analog MIC, please adjust according to your MIC's sensitivity */ + struct { + uint32_t tdm_enable:1; /*!< Choose whether to enable TDM mode */ + } flags; +} es7210_codec_config_t; + +/** + * @brief Create new ES7210 device handle. + * + * @param[in] i2c_conf Config for I2C used by ES7210 + * @param[out] handle_out New ES7210 device handle + * @return + * - ESP_OK Device handle creation success. + * - ESP_ERR_INVALID_ARG Invalid device handle or argument. + * - ESP_ERR_NO_MEM Memory allocation failed. + * + */ +esp_err_t es7210_new_codec(const es7210_i2c_config_t *i2c_conf, es7210_dev_handle_t *handle_out); + +/** + * @brief Delete ES7210 device handle. + * + * @param[in] handle ES7210 device handle + * @return + * - ESP_OK Device handle deletion success. + * - ESP_ERR_INVALID_ARG Invalid device handle or argument. + * + */ +esp_err_t es7210_del_codec(es7210_dev_handle_t handle); + +/** + * @brief Configure codec-related parameters of ES7210. + * + * @param[in] handle ES7210 device handle + * @param codec_conf codec-related parameters of ES7210 + * @return + * - ESP_OK Codec config success. + * - ESP_ERR_INVALID_ARG Invalid device handle or argument. + * - ESP_ERR_NO_MEM Memory allocation failed. + * - ESP_FAIL Sending command error, slave hasn't ACK the transfer. + * - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode. + * - ESP_ERR_TIMEOUT Operation timeout because the bus is busy. + * + */ +esp_err_t es7210_config_codec(es7210_dev_handle_t handle, const es7210_codec_config_t *codec_conf); + +/** + * @brief Configure volume of ES7210. + * + * @param[in] handle ES7210 device handle + * @param volume_db Volume to be set, in dB, with a range from -95dB to +32dB. + * @return + * - ESP_OK Volume config success. + * - ESP_ERR_INVALID_ARG Invalid device handle or argument. + * - ESP_ERR_NO_MEM Memory allocation failed. + * - ESP_FAIL Sending command error, slave hasn't ACK the transfer. + * - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode. + * - ESP_ERR_TIMEOUT Operation timeout because the bus is busy. + * + */ +esp_err_t es7210_config_volume(es7210_dev_handle_t handle, int8_t volume_db); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/CMakeLists.txt new file mode 100644 index 0000000000..223b8f8931 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "i2s_es7210_record_example.c" + INCLUDE_DIRS "../../../common" +) diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/i2s_es7210_record_example.c b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/i2s_es7210_record_example.c new file mode 100644 index 0000000000..87eb8c5cb8 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/main/i2s_es7210_record_example.c @@ -0,0 +1,259 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "sdkconfig.h" +#include "esp_check.h" +#include "esp_vfs_fat.h" +#include "driver/i2s_tdm.h" +#include "driver/i2c.h" +#include "es7210.h" +#include "format_wav.h" + +#if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3-Korvo-1 pinout +/* I2C port and GPIOs */ +#define EXAMPLE_I2C_NUM (0) +#define EXAMPLE_I2C_SDA_IO (1) +#define EXAMPLE_I2C_SCL_IO (2) + +/* I2S port and GPIOs */ +#define EXAMPLE_I2S_NUM (0) +#define EXAMPLE_I2S_MCK_IO (20) +#define EXAMPLE_I2S_BCK_IO (10) +#define EXAMPLE_I2S_WS_IO (9) +#define EXAMPLE_I2S_DI_IO (11) + +/* SD card SPI GPIOs */ +#define EXAMPLE_SD_SPI_CLK_IO (18) +#define EXAMPLE_SD_SPI_MOSI_IO (17) +#define EXAMPLE_SD_SPI_MISO_IO (16) +#define EXAMPLE_SD_SPI_CS_IO (15) +#elif CONFIG_IDF_TARGET_ESP32C3 +#define EXAMPLE_I2C_NUM (0) +#define EXAMPLE_I2C_SDA_IO (3) +#define EXAMPLE_I2C_SCL_IO (2) + +/* I2S port and GPIOs */ +#define EXAMPLE_I2S_NUM (0) +#define EXAMPLE_I2S_MCK_IO (0) +#define EXAMPLE_I2S_BCK_IO (1) +#define EXAMPLE_I2S_WS_IO (10) +#define EXAMPLE_I2S_DI_IO (8) + +/* SD card SPI GPIOs */ +#define EXAMPLE_SD_SPI_CLK_IO (5) +#define EXAMPLE_SD_SPI_MOSI_IO (7) +#define EXAMPLE_SD_SPI_MISO_IO (6) +#define EXAMPLE_SD_SPI_CS_IO (4) +#endif + +/* I2S configurations */ +#define EXAMPLE_I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S) +#define EXAMPLE_I2S_CHAN_NUM (4) +#define EXAMPLE_I2S_SAMPLE_RATE (48000) +#define EXAMPLE_I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256) +#define EXAMPLE_I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT) +#define EXAMPLE_I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3) + +/* ES7210 configurations */ +#define EXAMPLE_ES7210_I2C_ADDR (0x40) +#define EXAMPLE_ES7210_I2C_CLK (100000) +#define EXAMPLE_ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB) +#define EXAMPLE_ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87) +#define EXAMPLE_ES7210_ADC_VOLUME (0) + +/* SD card & recording configurations */ +#define EXAMPLE_RECORD_TIME_SEC (10) +#define EXAMPLE_SD_MOUNT_POINT "/sdcard" +#define EXAMPLE_RECORD_FILE_PATH "/RECORD.WAV" + +static const char *TAG = "example"; + + +static i2s_chan_handle_t es7210_i2s_init(void) +{ + i2s_chan_handle_t i2s_rx_chan = NULL; + ESP_LOGI(TAG, "Create I2S receive channel"); + i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); + ESP_ERROR_CHECK(i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); + + ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode"); + i2s_tdm_config_t i2s_tdm_rx_conf = { +#if EXAMPLE_I2S_FORMAT == ES7210_I2S_FMT_I2S + .slot_cfg = I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG(EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, EXAMPLE_I2S_TDM_SLOT_MASK), +#elif EXAMPLE_I2S_FORMAT == ES7210_I2S_FMT_LJ + .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, EXAMPLE_I2S_TDM_SLOT_MASK), +#elif EXAMPLE_I2S_FORMAT == ES7210_I2S_FMT_DSP_A + .slot_cfg = I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG(EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, EXAMPLE_I2S_TDM_SLOT_MASK), +#elif EXAMPLE_I2S_FORMAT == ES7210_I2S_FMT_DSP_B + .slot_cfg = I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG(EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, EXAMPLE_I2S_TDM_SLOT_MASK), +#endif + .clk_cfg = { + .clk_src = I2S_CLK_SRC_DEFAULT, + .sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE, + .mclk_multiple = EXAMPLE_I2S_MCLK_MULTIPLE + }, + .gpio_cfg = { + .mclk = EXAMPLE_I2S_MCK_IO, + .bclk = EXAMPLE_I2S_BCK_IO, + .ws = EXAMPLE_I2S_WS_IO, + .dout = -1, // ES7210 only has ADC capability + .din = EXAMPLE_I2S_DI_IO + }, + }; + + ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(i2s_rx_chan, &i2s_tdm_rx_conf)); + + return i2s_rx_chan; +} + +sdmmc_card_t * mount_sdcard(void) +{ + sdmmc_host_t sdmmc_host = SDSPI_HOST_DEFAULT(); + sdmmc_card_t *sdmmc_card = NULL; + + ESP_LOGI(TAG, "Initializing SPI bus for SD card"); + spi_bus_config_t bus_cfg = { + .mosi_io_num = EXAMPLE_SD_SPI_MOSI_IO, + .miso_io_num = EXAMPLE_SD_SPI_MISO_IO, + .sclk_io_num = EXAMPLE_SD_SPI_CLK_IO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 4000, + }; + ESP_ERROR_CHECK(spi_bus_initialize(sdmmc_host.slot, &bus_cfg, SPI_DMA_CH_AUTO)); + + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = EXAMPLE_SD_SPI_CS_IO; + slot_config.host_id = sdmmc_host.slot; + + ESP_LOGI(TAG, "Mounting SD card"); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 2, + .allocation_unit_size = 8 * 1024 + }; + + esp_err_t ret; + while (1) { + ret = esp_vfs_fat_sdspi_mount(EXAMPLE_SD_MOUNT_POINT, &sdmmc_host, &slot_config, &mount_config, &sdmmc_card); + if (ret == ESP_OK) { + break; + } else if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount filesystem."); + } 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)); + } + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Card size: %lluMB, speed: %dMHz", + (((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >> 20, + sdmmc_card->max_freq_khz / 1000); + + return sdmmc_card; +} + +static void es7210_codec_init(void) +{ + ESP_LOGI(TAG, "Init I2C used to configure ES7210"); + i2c_config_t i2c_conf = { + .sda_io_num = EXAMPLE_I2C_SDA_IO, + .scl_io_num = EXAMPLE_I2C_SCL_IO, + .mode = I2C_MODE_MASTER, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = EXAMPLE_ES7210_I2C_CLK, + }; + ESP_ERROR_CHECK(i2c_param_config(EXAMPLE_I2C_NUM, &i2c_conf)); + ESP_ERROR_CHECK(i2c_driver_install(EXAMPLE_I2C_NUM, i2c_conf.mode, 0, 0, 0)); + + /* Create ES7210 device handle */ + es7210_dev_handle_t es7210_handle = NULL; + es7210_i2c_config_t es7210_i2c_conf = { + .i2c_port = EXAMPLE_I2C_NUM, + .i2c_addr = EXAMPLE_ES7210_I2C_ADDR + }; + ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle)); + + ESP_LOGI(TAG, "Configure ES7210 codec parameters"); + es7210_codec_config_t codec_conf = { + .i2s_format = EXAMPLE_I2S_TDM_FORMAT, + .mclk_ratio = EXAMPLE_I2S_MCLK_MULTIPLE, + .sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE, + .bit_width = (es7210_i2s_bits_t)EXAMPLE_I2S_SAMPLE_BITS, + .mic_bias = EXAMPLE_ES7210_MIC_BIAS, + .mic_gain = EXAMPLE_ES7210_MIC_GAIN, + .flags.tdm_enable = true + }; + ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf)); + ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, EXAMPLE_ES7210_ADC_VOLUME)); +} + +static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan) +{ + ESP_RETURN_ON_FALSE(i2s_rx_chan, ESP_FAIL, TAG, "invalid i2s channel handle pointer"); + esp_err_t ret = ESP_OK; + + uint32_t byte_rate = EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_CHAN_NUM * EXAMPLE_I2S_SAMPLE_BITS / 8; + uint32_t wav_size = byte_rate * EXAMPLE_RECORD_TIME_SEC; + + const wav_header_t wav_header = + WAV_HEADER_PCM_DEFAULT(wav_size, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_CHAN_NUM); + + ESP_LOGI(TAG, "Opening file %s", EXAMPLE_RECORD_FILE_PATH); + FILE *f = fopen(EXAMPLE_SD_MOUNT_POINT EXAMPLE_RECORD_FILE_PATH, "w"); + ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file"); + + /* Write wav header */ + ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL, err, + TAG, "error while writting wav header"); + + /* Start recording */ + size_t wav_written = 0; + static int16_t i2s_readraw_buff[4096]; + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "error while starting i2s rx channel"); + while (wav_written < wav_size) { + if(wav_written % byte_rate < sizeof(i2s_readraw_buff)) { + ESP_LOGI(TAG, "Recording: %"PRIu32"/%ds", wav_written/byte_rate + 1, EXAMPLE_RECORD_TIME_SEC); + } + size_t bytes_read = 0; + /* Read RAW samples from ES7210 */ + ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_chan, i2s_readraw_buff, sizeof(i2s_readraw_buff), &bytes_read, + pdMS_TO_TICKS(1000)), err, TAG, "error while reading samples from i2s"); + /* Write the samples to the WAV file */ + ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err, + TAG, "error while writing samples to wav file"); + wav_written += bytes_read; + } + +err: + i2s_channel_disable(i2s_rx_chan); + ESP_LOGI(TAG, "Recording done! Flushing file buffer"); + fclose(f); + + return ret; +} + +void app_main(void) +{ + /* Init I2C bus to configure ES7210 and I2S bus to receive audio data from ES7210 */ + i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init(); + /* Create ES7210 device handle and configure codec parameters */ + es7210_codec_init(); + /* Mount SD card, the recorded audio file will be saved into it */ + sdmmc_card_t *sdmmc_card = mount_sdcard(); + /* Start to record wav audio */ + esp_err_t err = record_wav(i2s_rx_chan); + /* Unmount SD card */ + esp_vfs_fat_sdcard_unmount(EXAMPLE_SD_MOUNT_POINT, sdmmc_card); + if(err == ESP_OK) { + ESP_LOGI(TAG, "Audio was successfully recorded into "EXAMPLE_RECORD_FILE_PATH + ". You can now remove the SD card safely"); + } else { + ESP_LOGE(TAG, "Record failed, "EXAMPLE_RECORD_FILE_PATH" on SD card may not be playable."); + } +} diff --git a/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/pytest_i2s_es7210_tdm.py b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/pytest_i2s_es7210_tdm.py new file mode 100644 index 0000000000..c6ca369f64 --- /dev/null +++ b/examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm/pytest_i2s_es7210_tdm.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32s3 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_i2s_es7210_tdm_example(dut: Dut) -> None: + dut.expect_exact('example: Create I2S receive channel') + dut.expect_exact('example: Configure I2S receive channel to TDM mode') + dut.expect_exact('example: Init I2C used to configure ES7210') + dut.expect_exact('example: Configure ES7210 codec parameters')