Przeglądaj źródła

Merge branch 'example/i2s_es7210_tdm_record' into 'master'

example: add es7210 4-ch tdm i2s record example

Closes IDF-5774 and IDF-5852

See merge request espressif/esp-idf!19593
Wang Yuan Ze 3 lat temu
rodzic
commit
dac24d5ea4

+ 4 - 0
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

+ 73 - 0
examples/peripherals/i2s/common/format_wav.h

@@ -0,0 +1,73 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Header structure for WAV file with only one data chunk
+ *
+ * @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
+ *
+ * @note Assignment to variables in this struct directely is only possible for little endian architectures
+ *       (including Xtensa & RISC-V)
+ */
+typedef struct {
+    struct {
+        char chunk_id[4]; /*!< Contains the letters "RIFF" in ASCII form */
+        uint32_t chunk_size; /*!< This is the size of the rest of the chunk following this number */
+        char chunk_format[4]; /*!< Contains the letters "WAVE" */
+    } descriptor_chunk; /*!< Canonical WAVE format starts with the RIFF header */
+    struct {
+        char subchunk_id[4]; /*!< Contains the letters "fmt " */
+        uint32_t subchunk_size; /*!< This is the size of the rest of the Subchunk which follows this number */
+        uint16_t audio_format; /*!< PCM = 1, values other than 1 indicate some form of compression */
+        uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */
+        uint32_t sample_rate; /*!< 8000, 44100, etc. */
+        uint32_t byte_rate; /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */
+        uint16_t block_align; /*!< ==NumChannels * BitsPerSample / 8 */
+        uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */
+    } fmt_chunk; /*!< The "fmt " subchunk describes the sound data's format */
+    struct {
+        char subchunk_id[4]; /*!< Contains the letters "data" */
+        uint32_t subchunk_size; /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */
+        int16_t data[0]; /*!< Holds raw audio data */
+    } data_chunk; /*!< The "data" subchunk contains the size of the data and the actual sound */
+} wav_header_t;
+
+/**
+ * @brief Default header for PCM format WAV files
+ *
+ */
+#define WAV_HEADER_PCM_DEFAULT(wav_sample_size, wav_sample_bits, wav_sample_rate, wav_channel_num) { \
+    .descriptor_chunk = { \
+        .chunk_id = {'R', 'I', 'F', 'F'}, \
+        .chunk_size = (wav_sample_size) + sizeof(wav_header_t) - 8, \
+        .chunk_format = {'W', 'A', 'V', 'E'} \
+    }, \
+    .fmt_chunk = { \
+        .subchunk_id = {'f', 'm', 't', ' '}, \
+        .subchunk_size = 16, /* 16 for PCM */ \
+        .audio_format = 1, /* 1 for PCM */ \
+        .num_of_channels = (wav_channel_num), \
+        .sample_rate = (wav_sample_rate), \
+        .byte_rate = (wav_sample_bits) * (wav_sample_rate) * (wav_channel_num) / 8, \
+        .block_align = (wav_sample_bits) * (wav_channel_num) / 8, \
+        .bits_per_sample = (wav_sample_bits)\
+    }, \
+    .data_chunk = { \
+        .subchunk_id = {'d', 'a', 't', 'a'}, \
+        .subchunk_size = (wav_sample_size) \
+    } \
+}
+
+#ifdef __cplusplus
+}
+#endif

+ 6 - 0
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)

+ 85 - 0
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.

+ 4 - 0
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
+)

+ 341 - 0
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;
+}

+ 203 - 0
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 <stdint.h>
+#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

+ 3 - 0
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"
+)

+ 259 - 0
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 <string.h>
+#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.");
+    }
+}

+ 14 - 0
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')

+ 1 - 1
examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt

@@ -1,2 +1,2 @@
 idf_component_register(SRCS "i2s_recorder_main.c"
-                    INCLUDE_DIRS ".")
+                    INCLUDE_DIRS "../../common")

+ 6 - 30
examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c

@@ -10,6 +10,7 @@
 #include <math.h>
 #include <sys/unistd.h>
 #include <sys/stat.h>
+#include "sdkconfig.h"
 #include "esp_log.h"
 #include "esp_err.h"
 #include "esp_system.h"
@@ -20,7 +21,7 @@
 #include "driver/gpio.h"
 #include "driver/spi_common.h"
 #include "sdmmc_cmd.h"
-#include "sdkconfig.h"
+#include "format_wav.h"
 
 static const char *TAG = "pdm_rec_example";
 
@@ -90,40 +91,15 @@ void mount_sdcard(void)
     sdmmc_card_print_info(stdout, card);
 }
 
-void generate_wav_header(char *wav_header, uint32_t wav_size, uint32_t sample_rate)
-{
-    // See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
-    uint32_t file_size = wav_size + WAVE_HEADER_SIZE - 8;
-    uint32_t byte_rate = BYTE_RATE;
-
-    const char set_wav_header[] = {
-        'R', 'I', 'F', 'F', // ChunkID
-        file_size, file_size >> 8, file_size >> 16, file_size >> 24, // ChunkSize
-        'W', 'A', 'V', 'E', // Format
-        'f', 'm', 't', ' ', // Subchunk1ID
-        0x10, 0x00, 0x00, 0x00, // Subchunk1Size (16 for PCM)
-        0x01, 0x00, // AudioFormat (1 for PCM)
-        0x01, 0x00, // NumChannels (1 channel)
-        sample_rate, sample_rate >> 8, sample_rate >> 16, sample_rate >> 24, // SampleRate
-        byte_rate, byte_rate >> 8, byte_rate >> 16, byte_rate >> 24, // ByteRate
-        0x02, 0x00, // BlockAlign
-        0x10, 0x00, // BitsPerSample (16 bits)
-        'd', 'a', 't', 'a', // Subchunk2ID
-        wav_size, wav_size >> 8, wav_size >> 16, wav_size >> 24, // Subchunk2Size
-    };
-
-    memcpy(wav_header, set_wav_header, sizeof(set_wav_header));
-}
-
 void record_wav(uint32_t rec_time)
 {
     // Use POSIX and C standard library functions to work with files.
     int flash_wr_size = 0;
     ESP_LOGI(TAG, "Opening file");
 
-    char wav_header_fmt[WAVE_HEADER_SIZE];
     uint32_t flash_rec_time = BYTE_RATE * rec_time;
-    generate_wav_header(wav_header_fmt, flash_rec_time, CONFIG_EXAMPLE_SAMPLE_RATE);
+    const wav_header_t wav_header =
+        WAV_HEADER_PCM_DEFAULT(flash_rec_time, 16, CONFIG_EXAMPLE_SAMPLE_RATE, 1);
 
     // First check if file exists before creating a new file.
     struct stat st;
@@ -140,7 +116,7 @@ void record_wav(uint32_t rec_time)
     }
 
     // Write the header to the WAV file
-    fwrite(wav_header_fmt, 1, WAVE_HEADER_SIZE, f);
+    fwrite(&wav_header, sizeof(wav_header), 1, f);
 
     // Start recording
     while (flash_wr_size < flash_rec_time) {
@@ -148,7 +124,7 @@ void record_wav(uint32_t rec_time)
         if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000) == ESP_OK) {
             printf("[0] %d [1] %d [2] %d [3]%d ...\n", i2s_readraw_buff[0], i2s_readraw_buff[1], i2s_readraw_buff[2], i2s_readraw_buff[3]);
             // Write the samples to the WAV file
-            fwrite(i2s_readraw_buff, 1, bytes_read, f);
+            fwrite(i2s_readraw_buff, bytes_read, 1, f);
             flash_wr_size += bytes_read;
         } else {
             printf("Read Failed!\n");