mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
refactor(i2s): replace es8311 example with pcm_player
Drop echo/recording now that mic_recorder covers capture. The new example is TX-only and plays embedded PCM on ES8311 or ES8389.
This commit is contained in:
@@ -449,7 +449,7 @@ The examples of the I2S driver can be found in the directory :example:`periphera
|
|||||||
Standard TX/RX Usage
|
Standard TX/RX Usage
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- :example:`peripherals/i2s/i2s_codec/i2s_es8311` demonstrates how to use the I2S ES8311 audio codec with {IDF_TARGET_NAME} to play music or echo sounds, featuring high performance and low power multi-bit delta-sigma audio ADC and DAC, with options to customize music and adjust mic gain and volume.
|
- :example:`peripherals/i2s/wav_player` demonstrates how to play an embedded WAV file through I2S STD TX to an ES8311 or ES8389 codec DAC on {IDF_TARGET_NAME}.
|
||||||
- :example:`peripherals/i2s/i2s_basic/i2s_std` demonstrates how to use the I2S standard mode in either simplex or full-duplex mode on {IDF_TARGET_NAME}.
|
- :example:`peripherals/i2s/i2s_basic/i2s_std` demonstrates how to use the I2S standard mode in either simplex or full-duplex mode on {IDF_TARGET_NAME}.
|
||||||
- :example:`peripherals/i2s/mic_recorder` demonstrates how to record audio from an analog microphone connected to an ES8389 codec through the I2S STD interface.
|
- :example:`peripherals/i2s/mic_recorder` demonstrates how to record audio from an analog microphone connected to an ES8389 codec through the I2S STD interface.
|
||||||
|
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ I2S 驱动例程请参考 :example:`peripherals/i2s` 目录。以下为每种模
|
|||||||
标准 TX/RX 模式的应用
|
标准 TX/RX 模式的应用
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- :example:`peripherals/i2s/i2s_codec/i2s_es8311` 演示了如何在 {IDF_TARGET_NAME} 上使用 I2S ES8311 音频编解码器来播放音乐或回声,具有高性能和低功耗的多位 delta-sigma 音频 ADC 和 DAC,提供自定义音乐、调整麦克风增益和音量的选项。
|
- :example:`peripherals/i2s/wav_player` 演示了如何在 {IDF_TARGET_NAME} 上使用 I2S STD TX,将嵌入的 WAV 音频通过 ES8311 或 ES8389 音频编解码器播放。
|
||||||
- :example:`peripherals/i2s/i2s_basic/i2s_std` 演示了如何在 {IDF_TARGET_NAME} 上以单工或全双工模式使用 I2S 标准模式。
|
- :example:`peripherals/i2s/i2s_basic/i2s_std` 演示了如何在 {IDF_TARGET_NAME} 上以单工或全双工模式使用 I2S 标准模式。
|
||||||
- :example:`peripherals/i2s/mic_recorder` 演示了如何通过 I2S STD 接口,使用 ES8389 音频编解码器录制模拟麦克风的音频。
|
- :example:`peripherals/i2s/mic_recorder` 演示了如何通过 I2S STD 接口,使用 ES8389 音频编解码器录制模拟麦克风的音频。
|
||||||
|
|
||||||
|
|||||||
@@ -241,17 +241,6 @@ examples/peripherals/i2s/i2s_codec/i2s_es7210_tdm:
|
|||||||
- esp_hal_i2s
|
- esp_hal_i2s
|
||||||
- soc
|
- soc
|
||||||
|
|
||||||
examples/peripherals/i2s/i2s_codec/i2s_es8311:
|
|
||||||
disable:
|
|
||||||
- if: (SOC_I2S_SUPPORTED != 1 or SOC_I2C_SUPPORTED != 1)
|
|
||||||
reason: rely on I2S STD mode and I2C to config es7210
|
|
||||||
depends_components:
|
|
||||||
- esp_driver_dma
|
|
||||||
- esp_driver_i2s
|
|
||||||
- esp_driver_i2c
|
|
||||||
- esp_hal_i2s
|
|
||||||
- soc
|
|
||||||
|
|
||||||
examples/peripherals/i2s/mic_recorder:
|
examples/peripherals/i2s/mic_recorder:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_I2S_SUPPORTED != 1
|
- if: SOC_I2S_SUPPORTED != 1
|
||||||
@@ -260,6 +249,17 @@ examples/peripherals/i2s/mic_recorder:
|
|||||||
- esp_hal_i2s
|
- esp_hal_i2s
|
||||||
- soc
|
- soc
|
||||||
|
|
||||||
|
examples/peripherals/i2s/wav_player:
|
||||||
|
disable:
|
||||||
|
- if: (SOC_I2S_SUPPORTED != 1 or SOC_I2C_SUPPORTED != 1)
|
||||||
|
reason: rely on I2S STD TX and I2C to configure the DAC codec
|
||||||
|
depends_components:
|
||||||
|
- esp_driver_dma
|
||||||
|
- esp_driver_i2s
|
||||||
|
- esp_driver_i2c
|
||||||
|
- esp_hal_i2s
|
||||||
|
- soc
|
||||||
|
|
||||||
examples/peripherals/i3c/i3c_i2c_basic:
|
examples/peripherals/i3c/i3c_i2c_basic:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_I3C_MASTER_SUPPORTED != 1
|
- if: SOC_I3C_MASTER_SUPPORTED != 1
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|
|
||||||
| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
|
|
||||||
|
|
||||||
# I2S ES8311 Example
|
|
||||||
|
|
||||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
|
||||||
|
|
||||||
In this example, you can choose music mode or echo mode in menuconfig. In music mode, the example will play a piece of music in 'canon.pcm', and in echo mode, you can hear what you said in earphone.
|
|
||||||
|
|
||||||
## ES8311 brief
|
|
||||||
|
|
||||||
ES8311 low power mono audio codec features:
|
|
||||||
|
|
||||||
- High performance and low power multi-bit delta-sigma audio ADC and DAC
|
|
||||||
- I2S/PCM master or slave serial data port
|
|
||||||
- I2C interface for configuration
|
|
||||||
- ADC: 24-bit, 8 to 96 kHz sampling frequency
|
|
||||||
- ADC: 100 dB signal to noise ratio, -93 dB THD+N
|
|
||||||
- DAC: 24-bit, 8 to 96 kHz sampling frequency
|
|
||||||
- DAC: 110 dB signal to noise ratio, -80 dB THD+N
|
|
||||||
|
|
||||||
For more details, see [ES8311 datasheet](http://www.everest-semi.com/pdf/ES8311%20PB.pdf)
|
|
||||||
|
|
||||||
## How to Use Example
|
|
||||||
|
|
||||||
### Hardware Required
|
|
||||||
|
|
||||||
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
|
|
||||||
* The example can be preconfigured for [ESP-BOX](https://components.espressif.com/components/espressif/esp-box), [ESP32-S2-Kaluga-kit](https://components.espressif.com/components/espressif/esp32_s2_kaluga_kit) and [ESP32-S3-LCD-EV-board](https://components.espressif.com/components/espressif/esp32_s3_lcd_ev_board). More information is in 'Configure the Project' section.
|
|
||||||
* A USB cable for power supply and programming.
|
|
||||||
* A board with ES8311 codec, mic and earphone interface(e.g. ESP-LyraT-8311A extension board).
|
|
||||||
|
|
||||||
### Connection
|
|
||||||
```
|
|
||||||
┌─────────────────┐ ┌──────────────────────────┐
|
|
||||||
│ ESP │ │ ES8311 │
|
|
||||||
│ │ │ │
|
|
||||||
│ I2S_MCK_IO├──────────►│PIN2-MCLK │
|
|
||||||
│ │ │ │ ┌─────────┐
|
|
||||||
│ I2S_BCK_IO├──────────►│PIN6-BCLK PIN12-OUTP├───────────┤ │
|
|
||||||
│ │ │ │ │ EARPHONE│
|
|
||||||
│ I2S_WS_IO├──────────►│PIN8-LRCK PIN13-OUTN├───────────┤ │
|
|
||||||
│ │ │ │ └─────────┘
|
|
||||||
│ I2S_DO_IO├──────────►│PIN9-SDIN │
|
|
||||||
│ │ │ │
|
|
||||||
│ I2S_DI_IO│◄──────────┤PIN7-SDOUT │
|
|
||||||
│ │ │ │ ┌─────────┐
|
|
||||||
│ │ │ PIN18-MIC1P├───────────┤ │
|
|
||||||
│ I2C_SCL_IO├──────────►│PIN1 -CCLK │ │ MIC │
|
|
||||||
│ │ │ PIN17-MIC1N├───────────┤ │
|
|
||||||
│ I2C_SDA_IO│◄─────────►│PIN19-CDATA │ └─────────┘
|
|
||||||
│ │ │ │
|
|
||||||
│ VCC 3.3├───────────┤VCC │
|
|
||||||
│ │ │ │
|
|
||||||
│ GND├───────────┤GND │
|
|
||||||
└─────────────────┘ └──────────────────────────┘
|
|
||||||
```
|
|
||||||
Note: Since ESP32-C3 & ESP32-H2 board does not have GPIO 16/17, you can use other available GPIOs instead. In this example, we set GPIO 6/7 as I2C pins for ESP32-C3 and GPIO 8/9 ESP32-H2 and GPIO 16/17 for other chips, same as GPIO 18/19, we use GPIO 2/3 instead.
|
|
||||||
|
|
||||||
### Dependency
|
|
||||||
|
|
||||||
This example is based on [esp_codec_dev component](https://components.espressif.com/components/espressif/esp_codec_dev)
|
|
||||||
|
|
||||||
The component can be installed by [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). This example already includes it. If you want to install [esp_codec_dev component](https://components.espressif.com/components/espressif/esp_codec_dev) separately in your project, you can input the following command:
|
|
||||||
```
|
|
||||||
idf.py add-dependency "espressif/esp_codec_dev^1.3.4"
|
|
||||||
```
|
|
||||||
|
|
||||||
If the dependency is added, you can check `idf_component.yml` for more detail. When building this example or other projects with managed components, the component manager will search for the required components online and download them into the `managed_components` folder.
|
|
||||||
|
|
||||||
### Configure the Project
|
|
||||||
|
|
||||||
```
|
|
||||||
idf.py menuconfig
|
|
||||||
```
|
|
||||||
You can find configurations for this example in 'Example Configuration' tag.
|
|
||||||
|
|
||||||
* In 'Example mode' subtag, you can set the example mode to 'music' or 'echo'. You can hear a piece of music in 'music' mode and echo the sound sampled by mic in 'echo' mode. You can also customize you own music to play as shown below.
|
|
||||||
|
|
||||||
* In 'Set MIC gain' subtag, you can set the mic gain for echo mode.
|
|
||||||
|
|
||||||
* In 'Voice volume', you can set the volume between 0 to 100.
|
|
||||||
|
|
||||||
* In 'Enable Board Support Package (BSP) support' you can enable support for BSP. You can pick specific BSP in [idf_component.yml](main/idf_component.yml).
|
|
||||||
|
|
||||||
### Build and Flash
|
|
||||||
|
|
||||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
|
||||||
|
|
||||||
```
|
|
||||||
idf.py -p PORT flash monitor
|
|
||||||
```
|
|
||||||
|
|
||||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
|
||||||
|
|
||||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
|
||||||
|
|
||||||
## Example Output
|
|
||||||
|
|
||||||
Running this example in music mode, you can hear a piece of music (canon), the log is shown as follow:
|
|
||||||
|
|
||||||
```
|
|
||||||
I (348) I2S: DMA Malloc info, datalen=blocksize=1200, dma_desc_num=6
|
|
||||||
I (348) I2S: DMA Malloc info, datalen=blocksize=1200, dma_desc_num=6
|
|
||||||
I (358) I2S: I2S0, MCLK output by GPIO0
|
|
||||||
I (368) DRV8311: ES8311 in Slave mode
|
|
||||||
I (378) gpio: GPIO[10]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
|
||||||
I (3718) i2s_es8311: I2S music played, 213996 bytes are written.
|
|
||||||
I (7948) i2s_es8311: I2S music played, 213996 bytes are written.
|
|
||||||
......
|
|
||||||
```
|
|
||||||
|
|
||||||
Running this example in echo mode, you can hear the sound in earphone that collected by mic.
|
|
||||||
```
|
|
||||||
I (312) I2S: DMA Malloc info, datalen=blocksize=1200, dma_desc_num=6
|
|
||||||
I (312) I2S: DMA Malloc info, datalen=blocksize=1200, dma_desc_num=6
|
|
||||||
I (322) I2S: I2S0, MCLK output by GPIO0
|
|
||||||
I (332) DRV8311: ES8311 in Slave mode
|
|
||||||
I (342) gpio: GPIO[10]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have a logic analyzer, you can use a logic analyzer to grab GPIO signal directly. The following table describes the pins we use by default (Note that you can also use other pins for the same purpose).
|
|
||||||
|
|
||||||
| pin name| function | gpio_num |
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
| MCLK |module clock | GPIO_NUM_0|
|
|
||||||
| BCLK |bit clock | GPIO_NUM_4 |
|
|
||||||
| WS |word select | GPIO_NUM_5 |
|
|
||||||
| SDOUT |serial data out| GPIO_NUM_18/2 |
|
|
||||||
| SDIN |serial data in | GPIO_NUM_19/3 |
|
|
||||||
|
|
||||||
Other pins like I2C please refer to `example_config.h`.
|
|
||||||
|
|
||||||
Please note that the power amplifier on some development boards (like P4 EV board) are disabled by default, you might need to set the PA_CTRL pin to high to play the music via a speaker.
|
|
||||||
The PA_CTRL pin can be configured by `idf.py menuconfig`, please check if the PA_CTRL pin is correct on your board if the audio can only be played from the earphones but not the speaker.
|
|
||||||
|
|
||||||
### Customize your own music
|
|
||||||
|
|
||||||
The example have contained a piece of music in canon.pcm, if you want to play your own music, you can follow these steps:
|
|
||||||
|
|
||||||
1. Choose the music in any format you want to play (e.g. a.mp3)
|
|
||||||
2. Install 'ffmpeg' tool
|
|
||||||
3. Check your music format using ```ffprobe a.mp3```, you can get the stream format (e.g. Stream #0.0: Audio: mp3, 44100Hz, stereo, s16p, 64kb/s)
|
|
||||||
4. Cut your music since there is no enough space for the whole piece of music. ```ffmpeg -i a.mp3 -ss 00:00:00 -t 00:00:20 a_cut.mp3```
|
|
||||||
5. Transfer the music format into .pcm. ```ffmpeg -i a_cut.mp3 -f s16le -ar 16000 -ac 2 -acodec pcm_s16le a.pcm```
|
|
||||||
6. Move 'a.pcm' under 'main' directory
|
|
||||||
7. Replace 'canon.pcm' with 'a.pcm' in 'CMakeLists.txt' under 'main' directory
|
|
||||||
8. Replace '_binary_canon_pcm_start' and '_binary_canon_pcm_end' with '_binary_a_pcm_start' and '_binary_a_pcm_end' in `i2s_es8311_example.c`
|
|
||||||
9. Download the example and enjoy your own music
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
* Program upload failure
|
|
||||||
|
|
||||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
|
||||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
|
||||||
|
|
||||||
* Failed to get audio from specker
|
|
||||||
|
|
||||||
* The PA (Power Amplifier) on some dev-kits might be disabled by default, please check the schematic to see if PA_CTRL is connected to any GPIO or something.
|
|
||||||
* Pull-up the PA_CTRL pin either by setting that GPIO to high or by connecting it to 3.3V with a jump wire should help.
|
|
||||||
|
|
||||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
idf_component_register(SRCS "i2s_es8311_example.c"
|
|
||||||
PRIV_REQUIRES esp_driver_i2s esp_driver_i2c esp_driver_gpio
|
|
||||||
EMBED_FILES "canon.pcm")
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
menu "Example Configuration"
|
|
||||||
|
|
||||||
menu "Example IO Configuration"
|
|
||||||
config EXAMPLE_PA_CTRL_IO
|
|
||||||
int "Power Amplifier control IO"
|
|
||||||
default 53 if IDF_TARGET_ESP32P4
|
|
||||||
default -1
|
|
||||||
help
|
|
||||||
Set GPIO number for PA control. Set -1 to disable PA control.
|
|
||||||
|
|
||||||
config EXAMPLE_I2C_SDA_IO
|
|
||||||
int "I2C SDA IO"
|
|
||||||
default 17 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32H4
|
|
||||||
default 9 if IDF_TARGET_ESP32H2
|
|
||||||
default 7
|
|
||||||
|
|
||||||
config EXAMPLE_I2C_SCL_IO
|
|
||||||
int "I2C SCL IO"
|
|
||||||
default 16 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32H4
|
|
||||||
default 8 if IDF_TARGET_ESP32H2 || IDF_TARGET_ESP32P4
|
|
||||||
default 6
|
|
||||||
|
|
||||||
config EXAMPLE_I2S_MCLK_IO
|
|
||||||
int "I2S MCLK IO"
|
|
||||||
default 13 if IDF_TARGET_ESP32P4
|
|
||||||
default 0
|
|
||||||
|
|
||||||
config EXAMPLE_I2S_BCLK_IO
|
|
||||||
int "I2S BCLK IO"
|
|
||||||
default 12 if IDF_TARGET_ESP32P4
|
|
||||||
default 4
|
|
||||||
|
|
||||||
config EXAMPLE_I2S_WS_IO
|
|
||||||
int "I2S WS IO"
|
|
||||||
default 10 if IDF_TARGET_ESP32P4
|
|
||||||
default 5
|
|
||||||
|
|
||||||
config EXAMPLE_I2S_DIN_IO
|
|
||||||
int "I2S DIN IO"
|
|
||||||
default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
|
||||||
default 11 if IDF_TARGET_ESP32P4
|
|
||||||
default 3
|
|
||||||
|
|
||||||
config EXAMPLE_I2S_DOUT_IO
|
|
||||||
int "I2S DOUT IO"
|
|
||||||
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
|
||||||
default 9 if IDF_TARGET_ESP32P4
|
|
||||||
default 2
|
|
||||||
|
|
||||||
endmenu
|
|
||||||
|
|
||||||
choice EXAMPLE_MODE
|
|
||||||
prompt "Example mode"
|
|
||||||
default EXAMPLE_MODE_MUSIC
|
|
||||||
help
|
|
||||||
Select example work mode
|
|
||||||
|
|
||||||
config EXAMPLE_MODE_MUSIC
|
|
||||||
bool "music"
|
|
||||||
config EXAMPLE_MODE_ECHO
|
|
||||||
depends on !EXAMPLE_BSP
|
|
||||||
bool "echo"
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
config EXAMPLE_MIC_GAIN
|
|
||||||
int "Set MIC gain (unit: dB)"
|
|
||||||
depends on EXAMPLE_MODE_ECHO
|
|
||||||
default 18
|
|
||||||
help
|
|
||||||
Select mic gain for echo mode
|
|
||||||
|
|
||||||
config EXAMPLE_VOICE_VOLUME
|
|
||||||
int "Voice volume"
|
|
||||||
range 0 100
|
|
||||||
default 60
|
|
||||||
help
|
|
||||||
Set voice volume
|
|
||||||
endmenu
|
|
||||||
Binary file not shown.
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: CC0-1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
|
|
||||||
/* Example configurations */
|
|
||||||
#define EXAMPLE_RECV_BUF_SIZE (2400)
|
|
||||||
#define EXAMPLE_SAMPLE_RATE (16000)
|
|
||||||
#define EXAMPLE_MCLK_MULTIPLE (384) // If not using 24-bit data width, 256 should be enough
|
|
||||||
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
|
|
||||||
#define EXAMPLE_VOICE_VOLUME CONFIG_EXAMPLE_VOICE_VOLUME
|
|
||||||
#define EXAMPLE_PA_CTRL_IO CONFIG_EXAMPLE_PA_CTRL_IO
|
|
||||||
#if CONFIG_EXAMPLE_MODE_ECHO
|
|
||||||
#define EXAMPLE_MIC_GAIN CONFIG_EXAMPLE_MIC_GAIN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* I2C port and GPIOs */
|
|
||||||
#define I2C_NUM (0)
|
|
||||||
#define I2C_SCL_IO CONFIG_EXAMPLE_I2C_SCL_IO
|
|
||||||
#define I2C_SDA_IO CONFIG_EXAMPLE_I2C_SDA_IO
|
|
||||||
|
|
||||||
/* I2S port and GPIOs */
|
|
||||||
#define I2S_NUM (0)
|
|
||||||
#define I2S_MCK_IO CONFIG_EXAMPLE_I2S_MCLK_IO
|
|
||||||
#define I2S_BCK_IO CONFIG_EXAMPLE_I2S_BCLK_IO
|
|
||||||
#define I2S_WS_IO CONFIG_EXAMPLE_I2S_WS_IO
|
|
||||||
#define I2S_DO_IO CONFIG_EXAMPLE_I2S_DOUT_IO
|
|
||||||
#define I2S_DI_IO CONFIG_EXAMPLE_I2S_DIN_IO
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: CC0-1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/i2s_std.h"
|
|
||||||
#include "driver/i2c_master.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include "esp_codec_dev_defaults.h"
|
|
||||||
#include "esp_codec_dev.h"
|
|
||||||
#include "esp_codec_dev_vol.h"
|
|
||||||
#include "esp_check.h"
|
|
||||||
#include "example_config.h"
|
|
||||||
|
|
||||||
static const char *TAG = "i2s_es8311";
|
|
||||||
static const char err_reason[][30] = {"input param is invalid",
|
|
||||||
"operation timeout"
|
|
||||||
};
|
|
||||||
static i2s_chan_handle_t tx_handle = NULL;
|
|
||||||
static i2s_chan_handle_t rx_handle = NULL;
|
|
||||||
|
|
||||||
/* 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_master_bus_handle_t i2c_bus_handle = NULL;
|
|
||||||
i2c_master_bus_config_t i2c_mst_cfg = {
|
|
||||||
.i2c_port = I2C_NUM,
|
|
||||||
.sda_io_num = I2C_SDA_IO,
|
|
||||||
.scl_io_num = I2C_SCL_IO,
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
/* Pull-up internally for no external pull-up case.
|
|
||||||
Suggest to use external pull-up to ensure a strong enough pull-up. */
|
|
||||||
.flags.enable_internal_pullup = true,
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_cfg, &i2c_bus_handle));
|
|
||||||
|
|
||||||
/* Create control interface with I2C bus handle */
|
|
||||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
|
||||||
.port = I2C_NUM,
|
|
||||||
.addr = ES8311_CODEC_DEFAULT_ADDR,
|
|
||||||
.bus_handle = i2c_bus_handle,
|
|
||||||
};
|
|
||||||
const audio_codec_ctrl_if_t *ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
|
||||||
assert(ctrl_if);
|
|
||||||
|
|
||||||
/* Create data interface with I2S bus handle */
|
|
||||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
|
||||||
.port = I2S_NUM,
|
|
||||||
.rx_handle = rx_handle,
|
|
||||||
.tx_handle = tx_handle,
|
|
||||||
};
|
|
||||||
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
|
||||||
assert(data_if);
|
|
||||||
|
|
||||||
/* Create ES8311 interface handle */
|
|
||||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
|
||||||
assert(gpio_if);
|
|
||||||
es8311_codec_cfg_t es8311_cfg = {
|
|
||||||
.ctrl_if = ctrl_if,
|
|
||||||
.gpio_if = gpio_if,
|
|
||||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
|
|
||||||
.master_mode = false,
|
|
||||||
.use_mclk = I2S_MCK_IO >= 0,
|
|
||||||
.pa_pin = EXAMPLE_PA_CTRL_IO,
|
|
||||||
.pa_reverted = false,
|
|
||||||
.hw_gain = {
|
|
||||||
.pa_voltage = 5.0,
|
|
||||||
.codec_dac_voltage = 3.3,
|
|
||||||
},
|
|
||||||
.mclk_div = EXAMPLE_MCLK_MULTIPLE,
|
|
||||||
};
|
|
||||||
const audio_codec_if_t *es8311_if = es8311_codec_new(&es8311_cfg);
|
|
||||||
assert(es8311_if);
|
|
||||||
|
|
||||||
/* Create the top codec handle with ES8311 interface handle and data interface */
|
|
||||||
esp_codec_dev_cfg_t dev_cfg = {
|
|
||||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
|
|
||||||
.codec_if = es8311_if,
|
|
||||||
.data_if = data_if,
|
|
||||||
};
|
|
||||||
esp_codec_dev_handle_t codec_handle = esp_codec_dev_new(&dev_cfg);
|
|
||||||
assert(codec_handle);
|
|
||||||
|
|
||||||
/* Specify the sample configurations and open the device */
|
|
||||||
esp_codec_dev_sample_info_t sample_cfg = {
|
|
||||||
.bits_per_sample = I2S_DATA_BIT_WIDTH_16BIT,
|
|
||||||
.channel = 2,
|
|
||||||
.channel_mask = 0x03,
|
|
||||||
.sample_rate = EXAMPLE_SAMPLE_RATE,
|
|
||||||
.mclk_multiple = EXAMPLE_MCLK_MULTIPLE,
|
|
||||||
};
|
|
||||||
if (esp_codec_dev_open(codec_handle, &sample_cfg) != ESP_CODEC_DEV_OK) {
|
|
||||||
ESP_LOGE(TAG, "Open codec device failed");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the initial volume and gain */
|
|
||||||
if (esp_codec_dev_set_out_vol(codec_handle, EXAMPLE_VOICE_VOLUME) != ESP_CODEC_DEV_OK) {
|
|
||||||
ESP_LOGE(TAG, "set output volume failed");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
#if CONFIG_EXAMPLE_MODE_ECHO
|
|
||||||
if (esp_codec_dev_set_in_gain(codec_handle, EXAMPLE_MIC_GAIN) != ESP_CODEC_DEV_OK) {
|
|
||||||
ESP_LOGE(TAG, "set input gain failed");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t i2s_driver_init(void)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
|
||||||
i2s_std_config_t std_cfg = {
|
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
|
|
||||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
|
||||||
.gpio_cfg = {
|
|
||||||
.mclk = I2S_MCK_IO,
|
|
||||||
.bclk = I2S_BCK_IO,
|
|
||||||
.ws = I2S_WS_IO,
|
|
||||||
.dout = I2S_DO_IO,
|
|
||||||
.din = I2S_DI_IO,
|
|
||||||
.invert_flags = {
|
|
||||||
.mclk_inv = false,
|
|
||||||
.bclk_inv = false,
|
|
||||||
.ws_inv = false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
|
||||||
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;
|
|
||||||
uint8_t *data_ptr = (uint8_t *)music_pcm_start;
|
|
||||||
|
|
||||||
/* (Optional) Disable TX channel and preload the data before enabling the TX channel,
|
|
||||||
* so that the valid data can be transmitted immediately */
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write));
|
|
||||||
data_ptr += bytes_write; // Move forward the data pointer
|
|
||||||
|
|
||||||
/* Enable the TX channel */
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
|
||||||
while (1) {
|
|
||||||
/* Write music to earphone */
|
|
||||||
ret = i2s_channel_write(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write, portMAX_DELAY);
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
/* Since we set timeout to 'portMAX_DELAY' in 'i2s_channel_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 write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
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 failed.");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
data_ptr = (uint8_t *)music_pcm_start;
|
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_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_channel_read(rx_handle, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_read, 1000);
|
|
||||||
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_channel_write(tx_handle, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_write, 1000);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
printf("i2s es8311 codec example start\n-----------------------------\n");
|
|
||||||
/* Initialize i2s peripheral */
|
|
||||||
if (i2s_driver_init() != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "i2s driver init failed");
|
|
||||||
abort();
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "i2s driver init success");
|
|
||||||
}
|
|
||||||
/* Initialize i2c peripheral and config es8311 codec by i2c */
|
|
||||||
if (es8311_codec_init() != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "es8311 codec init failed");
|
|
||||||
abort();
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "es8311 codec init success");
|
|
||||||
}
|
|
||||||
|
|
||||||
#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
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
## IDF Component Manager Manifest File
|
|
||||||
dependencies:
|
|
||||||
espressif/esp_codec_dev: ^1.6.0
|
|
||||||
i2s_examples_common:
|
|
||||||
path: ${IDF_PATH}/examples/peripherals/i2s/i2s_examples_common
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
|
||||||
import pytest
|
|
||||||
from pytest_embedded import Dut
|
|
||||||
from pytest_embedded_idf.utils import idf_parametrize
|
|
||||||
from pytest_embedded_idf.utils import soc_filtered_targets
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.generic
|
|
||||||
@idf_parametrize(
|
|
||||||
'target',
|
|
||||||
soc_filtered_targets('SOC_I2S_SUPPORTED == 1'),
|
|
||||||
indirect=['target'],
|
|
||||||
)
|
|
||||||
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
|
|
||||||
def test_i2s_es8311_example_generic(dut: Dut) -> None:
|
|
||||||
dut.expect('i2s es8311 codec example start')
|
|
||||||
dut.expect('-----------------------------')
|
|
||||||
dut.expect('i2s_es8311: i2s driver init success')
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.es8311
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
'config',
|
|
||||||
[
|
|
||||||
's3_korvo2_v3',
|
|
||||||
],
|
|
||||||
indirect=True,
|
|
||||||
)
|
|
||||||
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
|
|
||||||
def test_i2s_es8311_example_korvo2_v3(dut: Dut) -> None:
|
|
||||||
dut.expect(r'i2s es8311 codec example start')
|
|
||||||
dut.expect(r'-----------------------------')
|
|
||||||
dut.expect(r'i2s_es8311: i2s driver init success')
|
|
||||||
dut.expect(r'i2s_es8311: es8311 codec init success')
|
|
||||||
dut.expect(r'Returned from app_main\(\)')
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
CONFIG_EXAMPLE_PA_CTRL_IO=48
|
|
||||||
CONFIG_EXAMPLE_I2C_SDA_IO=17
|
|
||||||
CONFIG_EXAMPLE_I2C_SCL_IO=18
|
|
||||||
CONFIG_EXAMPLE_I2S_MCLK_IO=16
|
|
||||||
CONFIG_EXAMPLE_I2S_BCLK_IO=9
|
|
||||||
CONFIG_EXAMPLE_I2S_WS_IO=45
|
|
||||||
CONFIG_EXAMPLE_I2S_DIN_IO=10
|
|
||||||
CONFIG_EXAMPLE_I2S_DOUT_IO=8
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n
|
|
||||||
@@ -72,7 +72,7 @@ menu "Example Configuration"
|
|||||||
|
|
||||||
config EXAMPLE_I2S_MCLK_IO
|
config EXAMPLE_I2S_MCLK_IO
|
||||||
int "I2S MCLK GPIO (set -1 if the codec does not use an external MCLK)"
|
int "I2S MCLK GPIO (set -1 if the codec does not use an external MCLK)"
|
||||||
default 2
|
default -1
|
||||||
help
|
help
|
||||||
Set the GPIO number used for the I2S MCLK signal. When set to -1, no MCLK
|
Set the GPIO number used for the I2S MCLK signal. When set to -1, no MCLK
|
||||||
is generated and the ES8389 derives its clock from BCLK.
|
is generated and the ES8389 derives its clock from BCLK.
|
||||||
|
|||||||
@@ -132,10 +132,36 @@ static void record_from_pdm_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_EXAMPLE_MIC_TYPE_AMIC
|
#if CONFIG_EXAMPLE_MIC_TYPE_AMIC
|
||||||
|
static i2s_chan_handle_t i2s_rx_init(void)
|
||||||
|
{
|
||||||
|
/* Simplex RX. I2S_ROLE_MASTER: the SoC drives BCLK/WS (and optional MCLK)
|
||||||
|
* so the ES8389 can run as I2S slave. */
|
||||||
|
i2s_chan_handle_t rx_handle = NULL;
|
||||||
|
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
||||||
|
|
||||||
|
i2s_std_config_t std_cfg = {
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
|
||||||
|
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(EXAMPLE_BITS_PER_SAMPLE, EXAMPLE_CHANNEL_COUNT),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = CONFIG_EXAMPLE_I2S_MCLK_IO,
|
||||||
|
.bclk = CONFIG_EXAMPLE_I2S_BCLK_IO,
|
||||||
|
.ws = CONFIG_EXAMPLE_I2S_WS_IO,
|
||||||
|
.din = CONFIG_EXAMPLE_I2S_DIN_IO,
|
||||||
|
.dout = GPIO_NUM_NC,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||||
|
/* Clocks must be running before the codec PLL is started over I2C. */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||||
|
return rx_handle;
|
||||||
|
}
|
||||||
|
|
||||||
static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
||||||
{
|
{
|
||||||
/* The analog signal is sampled by the ES8389 ADC. I2S RX receives its digital samples
|
i2s_chan_handle_t rx_handle = i2s_rx_init();
|
||||||
* and, as bus master, generates the BCLK/WS (and optional MCLK) required by the codec. */
|
|
||||||
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
@@ -149,7 +175,6 @@ static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_handle));
|
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_handle));
|
||||||
|
|
||||||
/* I2C configures codec registers; PCM samples use I2S. */
|
|
||||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||||
.bus_handle = i2c_bus_handle,
|
.bus_handle = i2c_bus_handle,
|
||||||
.addr = ES8389_CODEC_DEFAULT_ADDR,
|
.addr = ES8389_CODEC_DEFAULT_ADDR,
|
||||||
@@ -162,33 +187,12 @@ static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
.ctrl_if = ctrl_if,
|
.ctrl_if = ctrl_if,
|
||||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_ADC,
|
.codec_mode = ESP_CODEC_DEV_WORK_MODE_ADC,
|
||||||
.pa_pin = GPIO_NUM_NC,
|
.pa_pin = GPIO_NUM_NC,
|
||||||
/* When MCLK is unused, the ES8389 derives its clock from BCLK. */
|
|
||||||
.use_mclk = CONFIG_EXAMPLE_I2S_MCLK_IO >= 0,
|
.use_mclk = CONFIG_EXAMPLE_I2S_MCLK_IO >= 0,
|
||||||
.mclk_div = EXAMPLE_MCLK_MULTIPLE,
|
.mclk_div = EXAMPLE_MCLK_MULTIPLE,
|
||||||
};
|
};
|
||||||
const audio_codec_if_t *codec_if = es8389_codec_new(&es8389_cfg);
|
const audio_codec_if_t *codec_if = es8389_codec_new(&es8389_cfg);
|
||||||
ESP_ERROR_CHECK(codec_if ? ESP_OK : ESP_FAIL);
|
ESP_ERROR_CHECK(codec_if ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
/* Recording needs only an RX channel. In master mode, RX also drives BCLK/WS. */
|
|
||||||
i2s_chan_handle_t rx_handle = NULL;
|
|
||||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
|
||||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
|
|
||||||
|
|
||||||
i2s_std_config_t std_cfg = {
|
|
||||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
|
|
||||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(EXAMPLE_BITS_PER_SAMPLE, EXAMPLE_CHANNEL_COUNT),
|
|
||||||
.gpio_cfg = {
|
|
||||||
.mclk = CONFIG_EXAMPLE_I2S_MCLK_IO,
|
|
||||||
.bclk = CONFIG_EXAMPLE_I2S_BCLK_IO,
|
|
||||||
.ws = CONFIG_EXAMPLE_I2S_WS_IO,
|
|
||||||
.dout = GPIO_NUM_NC,
|
|
||||||
.din = CONFIG_EXAMPLE_I2S_DIN_IO,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
|
||||||
|
|
||||||
/* Pass the already-configured RX channel to the codec data interface. */
|
|
||||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||||
.rx_handle = rx_handle,
|
.rx_handle = rx_handle,
|
||||||
};
|
};
|
||||||
@@ -200,8 +204,8 @@ static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
.codec_if = codec_if,
|
.codec_if = codec_if,
|
||||||
.data_if = data_if,
|
.data_if = data_if,
|
||||||
};
|
};
|
||||||
esp_codec_dev_handle_t codec_handle = esp_codec_dev_new(&dev_cfg);
|
esp_codec_dev_handle_t codec_dev = esp_codec_dev_new(&dev_cfg);
|
||||||
ESP_ERROR_CHECK(codec_handle ? ESP_OK : ESP_FAIL);
|
ESP_ERROR_CHECK(codec_dev ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
esp_codec_dev_sample_info_t sample_cfg = {
|
esp_codec_dev_sample_info_t sample_cfg = {
|
||||||
.bits_per_sample = EXAMPLE_BITS_PER_SAMPLE,
|
.bits_per_sample = EXAMPLE_BITS_PER_SAMPLE,
|
||||||
@@ -210,8 +214,8 @@ static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
.sample_rate = EXAMPLE_SAMPLE_RATE,
|
.sample_rate = EXAMPLE_SAMPLE_RATE,
|
||||||
.mclk_multiple = EXAMPLE_MCLK_MULTIPLE,
|
.mclk_multiple = EXAMPLE_MCLK_MULTIPLE,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_open(codec_handle, &sample_cfg));
|
ESP_ERROR_CHECK(esp_codec_dev_open(codec_dev, &sample_cfg));
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(codec_handle, CONFIG_EXAMPLE_MIC_GAIN));
|
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(codec_dev, CONFIG_EXAMPLE_MIC_GAIN));
|
||||||
ESP_LOGI(TAG, "ES8389 codec initialized");
|
ESP_LOGI(TAG, "ES8389 codec initialized");
|
||||||
|
|
||||||
/* Discard the startup PCM to skip the microphone or codec startup transient. */
|
/* Discard the startup PCM to skip the microphone or codec startup transient. */
|
||||||
@@ -219,9 +223,8 @@ static void record_from_es8389_microphone(uint8_t *pcm_data, size_t pcm_size)
|
|||||||
ESP_LOGI(TAG, "Starting ES8389 recording for %d seconds!", EXAMPLE_RECORD_TIME_SECONDS);
|
ESP_LOGI(TAG, "Starting ES8389 recording for %d seconds!", EXAMPLE_RECORD_TIME_SECONDS);
|
||||||
capture_pcm(rx_handle, pcm_data, pcm_size);
|
capture_pcm(rx_handle, pcm_data, pcm_size);
|
||||||
|
|
||||||
/* Closing the input device disables RX before its channel is deleted. */
|
ESP_ERROR_CHECK(esp_codec_dev_close(codec_dev));
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_close(codec_handle));
|
esp_codec_dev_delete(codec_dev);
|
||||||
esp_codec_dev_delete(codec_handle);
|
|
||||||
ESP_ERROR_CHECK(audio_codec_delete_codec_if(codec_if));
|
ESP_ERROR_CHECK(audio_codec_delete_codec_if(codec_if));
|
||||||
ESP_ERROR_CHECK(audio_codec_delete_data_if(data_if));
|
ESP_ERROR_CHECK(audio_codec_delete_data_if(data_if));
|
||||||
ESP_ERROR_CHECK(audio_codec_delete_ctrl_if(ctrl_if));
|
ESP_ERROR_CHECK(audio_codec_delete_ctrl_if(ctrl_if));
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
|||||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
idf_build_set_property(MINIMAL_BUILD ON)
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
|
||||||
project(i2s-es8311-example)
|
project(wav_player_example)
|
||||||
122
examples/peripherals/i2s/wav_player/README.md
Normal file
122
examples/peripherals/i2s/wav_player/README.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
|
||||||
|
|
||||||
|
# I2S WAV Player Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This example plays an embedded WAV file through a codec DAC over I2S STD TX. It supports two codecs, selectable in menuconfig:
|
||||||
|
|
||||||
|
* **ES8311** — low-power mono ADC/DAC codec (for example ESP-BOX and ESP32-S3-Korvo-2).
|
||||||
|
* **ES8389** — stereo ADC/DAC codec (for example ESP32-S31-Korvo-1).
|
||||||
|
|
||||||
|
The firmware embeds `assets/music.wav` in flash and loops it on the TX channel. A standard PCM WAV is a short RIFF header plus little-endian signed PCM; the example finds the `data` chunk and hands that payload to I2S. The clip stays mapped in flash and is copied in DMA-sized pieces, not into a RAM buffer. There is no microphone path. For recording, see [mic_recorder](../mic_recorder).
|
||||||
|
|
||||||
|
| Audio Setting | Value |
|
||||||
|
|:---:|:---:|
|
||||||
|
| Sample Rate |16000 Hz|
|
||||||
|
| Bits per Sample |16 bits|
|
||||||
|
| Channels |Stereo|
|
||||||
|
| Source |Embedded `assets/music.wav`|
|
||||||
|
|
||||||
|
These settings are defined as macros at the top of `main/wav_player_example_main.c` (see `EXAMPLE_SAMPLE_RATE`, `EXAMPLE_BITS_PER_SAMPLE`, etc.). **A replacement WAV must be uncompressed signed 16-bit PCM with the same rate, width, and channel count.**
|
||||||
|
|
||||||
|
`music.wav` is an excerpt of *Canon in D Major (piano only)* by [Ava](https://freemusicarchive.org/music/ava-drumm/single/canon-in-d-major-piano-only), released on the Free Music Archive under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). The clip was resampled for this example. See [assets/README.md](assets/README.md) for the full provenance.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with a SoC that supports I2S STD and I2C (see `Supported Targets` table above).
|
||||||
|
* A USB cable for power supply and programming.
|
||||||
|
* A board or module with an ES8311 or ES8389 codec and a headphone or speaker output.
|
||||||
|
|
||||||
|
### Connection
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌──────────────────────────┐
|
||||||
|
│ ESP │ │ ES8311 / ES8389 │
|
||||||
|
│ │ │ │
|
||||||
|
│ I2S_MCK_IO├──────────►│MCLK │
|
||||||
|
│ │ │ │ ┌─────────┐
|
||||||
|
│ I2S_BCK_IO├──────────►│BCLK analog out├───────────┤ │
|
||||||
|
│ │ │ │ │ EARPHONE│
|
||||||
|
│ I2S_WS_IO├──────────►│LRCK / WS │ │ / SPK │
|
||||||
|
│ │ │ │ └─────────┘
|
||||||
|
│ I2S_DOUT_IO├──────────►│SDIN │
|
||||||
|
│ │ │ │
|
||||||
|
│ I2C_SCL_IO├──────────►│SCL / CCLK │
|
||||||
|
│ │ │ │
|
||||||
|
│ I2C_SDA_IO│◄─────────►│SDA / CDATA │
|
||||||
|
│ │ │ │
|
||||||
|
│ PA_CTRL ├──────────►│amp EN / CTRL (optional) │
|
||||||
|
│ │ │ │
|
||||||
|
│ VCC 3.3├───────────┤VCC │
|
||||||
|
│ GND├───────────┤GND │
|
||||||
|
└─────────────────┘ └──────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
The codec chip has no amplifier pin. `PA_CTRL` is the MCU GPIO that enables the board's speaker amplifier (HT8691, NS4150, and similar). Check the schematic: the enable is often labeled `EN`, `CTRL`, `PA`, or even `INT`. Set `Power amplifier enable IO` in menuconfig to that GPIO so the codec driver raises it when the DAC starts. Set it to `-1` if the enable is already strapped high or the board has no amplifier. A disabled amplifier is a common reason for silent speakers while headphones still work.
|
||||||
|
|
||||||
|
Assign every signal in the diagram to your board's GPIOs under `Example Configuration` → `Example IO Configuration` in menuconfig. Some targets also ship extra defaults in `sdkconfig.defaults.<target>`; `idf.py set-target` applies those automatically.
|
||||||
|
|
||||||
|
### Dependency
|
||||||
|
|
||||||
|
This example is based on [esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev). The component is already listed in `main/idf_component.yml` and is fetched by the [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html) on the first build.
|
||||||
|
|
||||||
|
To add the same dependency to another project:
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py add-dependency "espressif/esp_codec_dev^1.6.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the Project
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
In the `Example Configuration` menu:
|
||||||
|
|
||||||
|
* Use `DAC / codec type` to select ES8311 or ES8389.
|
||||||
|
* Use `Example IO Configuration` to assign the shared I2C, I2S, and amplifier-enable GPIOs.
|
||||||
|
* Use `Voice volume` to set the output volume (0 to 100).
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py -p PORT flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
wav player example start
|
||||||
|
--------------------------
|
||||||
|
I (...) wav_player: i2s driver init success
|
||||||
|
I (...) wav_player: ES8311 codec init success
|
||||||
|
I (...) wav_player: i2s music played, ... bytes are written.
|
||||||
|
I (...) wav_player: i2s music played, ... bytes are written.
|
||||||
|
......
|
||||||
|
```
|
||||||
|
|
||||||
|
An ES8389 run looks the same, except the codec init line names ES8389.
|
||||||
|
|
||||||
|
If you have a logic analyzer, probe the I2S signals on the GPIOs you set in `Example IO Configuration`.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
* No sound from the speaker or headphone
|
||||||
|
|
||||||
|
* Confirm `DAC / codec type` and the I2C/I2S GPIOs match the board. Playback data goes to the codec **SDI** (SDIN) pin, not SDO.
|
||||||
|
* If the board has no MCLK pin (ESP32-S31-Korvo-1 does not), set `I2S MCLK IO` to -1 so the codec derives its clock from BCLK. Driving an unconnected MCLK GPIO while the codec waits for external MCLK produces silence.
|
||||||
|
* Check the schematic and set `Power amplifier enable IO` to the speaker-amplifier enable (`EN` / `CTRL` / `PA` / `INT`). Set the option to -1 if that pin is already strapped high.
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||||
16
examples/peripherals/i2s/wav_player/assets/README.md
Normal file
16
examples/peripherals/i2s/wav_player/assets/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Audio clip provenance
|
||||||
|
|
||||||
|
`music.wav` is a short excerpt of the following recording, converted for this example to 16 kHz, signed 16-bit, stereo PCM:
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Title | Canon in D Major (piano only) |
|
||||||
|
| Artist | Ava |
|
||||||
|
| Source | https://freemusicarchive.org/music/ava-drumm/single/canon-in-d-major-piano-only |
|
||||||
|
| Released | 12 August 2021 |
|
||||||
|
| License | [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) |
|
||||||
|
|
||||||
|
The recording is a piano performance of Johann Pachelbel's *Canon in D*.
|
||||||
|
Pachelbel's composition is in the public domain. Ava released this performance under CC0 1.0, dedicating it to the public domain.
|
||||||
|
|
||||||
|
The example firmware only embeds this file; it does not change the license of the recording.
|
||||||
BIN
examples/peripherals/i2s/wav_player/assets/music.wav
Normal file
BIN
examples/peripherals/i2s/wav_player/assets/music.wav
Normal file
Binary file not shown.
8
examples/peripherals/i2s/wav_player/main/CMakeLists.txt
Normal file
8
examples/peripherals/i2s/wav_player/main/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
idf_component_register(SRCS "wav_player_example_main.c"
|
||||||
|
PRIV_REQUIRES esp_driver_i2s esp_driver_i2c esp_driver_gpio
|
||||||
|
INCLUDE_DIRS .)
|
||||||
|
|
||||||
|
# Embed the WAV as-is. The firmware skips the RIFF header and plays the signed
|
||||||
|
# 16-bit PCM payload from flash; no build-time conversion and no RAM copy of the clip.
|
||||||
|
target_add_binary_data(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/../assets/music.wav"
|
||||||
|
BINARY RENAME_TO "music_wav")
|
||||||
79
examples/peripherals/i2s/wav_player/main/Kconfig.projbuild
Normal file
79
examples/peripherals/i2s/wav_player/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
choice EXAMPLE_DAC_TYPE
|
||||||
|
prompt "DAC / codec type"
|
||||||
|
default EXAMPLE_DAC_TYPE_ES8311
|
||||||
|
help
|
||||||
|
Select the DAC codec connected to the board. Both options use I2S STD TX
|
||||||
|
to send PCM and I2C to configure the codec:
|
||||||
|
|
||||||
|
- ES8311: low-power mono ADC/DAC codec used on boards such as ESP-BOX
|
||||||
|
and ESP32-S3-Korvo-2.
|
||||||
|
- ES8389: stereo ADC/DAC codec used on boards such as ESP32-S31-Korvo-1.
|
||||||
|
|
||||||
|
config EXAMPLE_DAC_TYPE_ES8311
|
||||||
|
bool "ES8311 codec"
|
||||||
|
select CODEC_ES8311_SUPPORT
|
||||||
|
|
||||||
|
config EXAMPLE_DAC_TYPE_ES8389
|
||||||
|
bool "ES8389 codec"
|
||||||
|
select CODEC_ES8389_SUPPORT
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
menu "Example IO Configuration"
|
||||||
|
config EXAMPLE_PA_CTRL_IO
|
||||||
|
int "Power amplifier enable IO"
|
||||||
|
default -1
|
||||||
|
help
|
||||||
|
GPIO connected to the board's speaker-amplifier enable
|
||||||
|
(often labeled EN, CTRL, PA, or INT). This is not a pin
|
||||||
|
on the ES8311 or ES8389. The codec driver raises it when
|
||||||
|
the DAC starts. Set -1 if the board has no amplifier
|
||||||
|
enable, or if that pin is already strapped high.
|
||||||
|
|
||||||
|
config EXAMPLE_I2C_SDA_IO
|
||||||
|
int "I2C SDA IO"
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Set the GPIO number used for the I2C SDA signal towards the codec.
|
||||||
|
|
||||||
|
config EXAMPLE_I2C_SCL_IO
|
||||||
|
int "I2C SCL IO"
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set the GPIO number used for the I2C SCL signal towards the codec.
|
||||||
|
|
||||||
|
config EXAMPLE_I2S_MCLK_IO
|
||||||
|
int "I2S MCLK IO (set -1 if the codec does not use an external MCLK)"
|
||||||
|
default -1
|
||||||
|
help
|
||||||
|
Set the GPIO number used for the I2S MCLK signal. When set to -1, no MCLK
|
||||||
|
is generated and the codec derives its clock from BCLK.
|
||||||
|
|
||||||
|
config EXAMPLE_I2S_BCLK_IO
|
||||||
|
int "I2S BCLK IO"
|
||||||
|
default 3
|
||||||
|
help
|
||||||
|
Set the GPIO number used for the I2S BCLK signal towards the codec.
|
||||||
|
|
||||||
|
config EXAMPLE_I2S_WS_IO
|
||||||
|
int "I2S WS/LRCK IO"
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
Set the GPIO number used for the I2S WS/LRCK signal towards the codec.
|
||||||
|
|
||||||
|
config EXAMPLE_I2S_DOUT_IO
|
||||||
|
int "I2S DOUT IO"
|
||||||
|
default 7
|
||||||
|
help
|
||||||
|
Set the GPIO number used for sending PCM data to the codec.
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
config EXAMPLE_VOICE_VOLUME
|
||||||
|
int "Voice volume"
|
||||||
|
range 0 100
|
||||||
|
default 60
|
||||||
|
help
|
||||||
|
Set the codec output volume.
|
||||||
|
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
espressif/esp_codec_dev: "^1.6.0"
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/i2s_std.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
#include "esp_codec_dev.h"
|
||||||
|
#include "esp_codec_dev_defaults.h"
|
||||||
|
|
||||||
|
static const char *TAG = "wav_player";
|
||||||
|
|
||||||
|
/* These four values are the I2S slot format AND the WAV / codec format. They must match. */
|
||||||
|
#define EXAMPLE_SAMPLE_RATE 16000
|
||||||
|
#define EXAMPLE_BITS_PER_SAMPLE 16
|
||||||
|
#define EXAMPLE_CHANNEL_COUNT 2
|
||||||
|
#define EXAMPLE_STEREO_CHANNEL_MASK 0x03 /* Bit0 = left, bit1 = right. */
|
||||||
|
|
||||||
|
/* Most codecs expect MCLK = 256 * LRCK (sample rate) when an external MCLK pin is wired. */
|
||||||
|
#define EXAMPLE_MCLK_MULTIPLE 256
|
||||||
|
#define EXAMPLE_I2C_CLOCK_HZ 100000
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_DAC_TYPE_ES8311
|
||||||
|
#define EXAMPLE_CODEC_I2C_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||||
|
#define EXAMPLE_CODEC_NAME "ES8311"
|
||||||
|
#else
|
||||||
|
#define EXAMPLE_CODEC_I2C_ADDR ES8389_CODEC_DEFAULT_ADDR
|
||||||
|
#define EXAMPLE_CODEC_NAME "ES8389"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* CMake embeds the WAV in flash as a binary blob (target_add_binary_data). */
|
||||||
|
extern const uint8_t music_wav_start[] asm("_binary_music_wav_start");
|
||||||
|
extern const uint8_t music_wav_end[] asm("_binary_music_wav_end");
|
||||||
|
|
||||||
|
static bool fourcc_eq(const uint8_t *p, const char *id)
|
||||||
|
{
|
||||||
|
return memcmp(p, id, 4) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t read_le16(const uint8_t *p)
|
||||||
|
{
|
||||||
|
return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t read_le32(const uint8_t *p)
|
||||||
|
{
|
||||||
|
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2S transmits raw PCM only. A WAV file is a RIFF container: skip "fmt " / "data"
|
||||||
|
* chunks until the little-endian PCM payload, and reject compressed encodings. */
|
||||||
|
static esp_err_t wav_get_pcm(const uint8_t *wav, size_t wav_len, const uint8_t **pcm, size_t *pcm_len)
|
||||||
|
{
|
||||||
|
if (wav_len < 12 || !fourcc_eq(wav, "RIFF") || !fourcc_eq(wav + 8, "WAVE")) {
|
||||||
|
ESP_LOGE(TAG, "not a RIFF/WAVE file");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *fmt = NULL;
|
||||||
|
uint32_t fmt_size = 0;
|
||||||
|
const uint8_t *data = NULL;
|
||||||
|
uint32_t data_size = 0;
|
||||||
|
|
||||||
|
for (size_t off = 12; off + 8 <= wav_len;) {
|
||||||
|
uint32_t size = read_le32(wav + off + 4);
|
||||||
|
size_t payload = off + 8;
|
||||||
|
if (size > wav_len - payload) {
|
||||||
|
ESP_LOGE(TAG, "WAV chunk overruns file");
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
if (fourcc_eq(wav + off, "fmt ")) {
|
||||||
|
fmt = wav + payload;
|
||||||
|
fmt_size = size;
|
||||||
|
} else if (fourcc_eq(wav + off, "data")) {
|
||||||
|
data = wav + payload;
|
||||||
|
data_size = size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
off = payload + size + (size & 1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt == NULL || fmt_size < 16 || data == NULL || data_size == 0) {
|
||||||
|
ESP_LOGE(TAG, "WAV missing fmt or data chunk");
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t audio_format = read_le16(fmt);
|
||||||
|
uint16_t channels = read_le16(fmt + 2);
|
||||||
|
uint32_t sample_rate = read_le32(fmt + 4);
|
||||||
|
uint16_t bits = read_le16(fmt + 14);
|
||||||
|
if (audio_format != 1) {
|
||||||
|
ESP_LOGE(TAG, "WAV is not uncompressed PCM (format %u)", audio_format);
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (channels != EXAMPLE_CHANNEL_COUNT ||
|
||||||
|
sample_rate != EXAMPLE_SAMPLE_RATE ||
|
||||||
|
bits != EXAMPLE_BITS_PER_SAMPLE) {
|
||||||
|
ESP_LOGE(TAG, "WAV is %u Hz / %u-bit / %u ch, expected %u / %u / %u",
|
||||||
|
(unsigned)sample_rate, bits, channels,
|
||||||
|
EXAMPLE_SAMPLE_RATE, EXAMPLE_BITS_PER_SAMPLE, EXAMPLE_CHANNEL_COUNT);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pcm = data;
|
||||||
|
*pcm_len = data_size;
|
||||||
|
ESP_LOGI(TAG, "WAV PCM payload %u bytes", (unsigned)data_size);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const audio_codec_if_t *create_dac_codec_if(const audio_codec_ctrl_if_t *ctrl_if,
|
||||||
|
const audio_codec_gpio_if_t *gpio_if)
|
||||||
|
{
|
||||||
|
/* The codec is the I2S slave: it receives BCLK/WS (and optional MCLK) from the SoC.
|
||||||
|
* pa_pin is the board amplifier enable, not a pin on the codec die. */
|
||||||
|
#if CONFIG_EXAMPLE_DAC_TYPE_ES8311
|
||||||
|
es8311_codec_cfg_t dac_cfg = {
|
||||||
|
.ctrl_if = ctrl_if,
|
||||||
|
.gpio_if = gpio_if,
|
||||||
|
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||||
|
.master_mode = false,
|
||||||
|
.use_mclk = CONFIG_EXAMPLE_I2S_MCLK_IO >= 0,
|
||||||
|
.pa_pin = CONFIG_EXAMPLE_PA_CTRL_IO,
|
||||||
|
.hw_gain = {
|
||||||
|
.pa_voltage = 5.0,
|
||||||
|
.codec_dac_voltage = 3.3,
|
||||||
|
},
|
||||||
|
.mclk_div = EXAMPLE_MCLK_MULTIPLE,
|
||||||
|
};
|
||||||
|
return es8311_codec_new(&dac_cfg);
|
||||||
|
#else
|
||||||
|
es8389_codec_cfg_t dac_cfg = {
|
||||||
|
.ctrl_if = ctrl_if,
|
||||||
|
.gpio_if = gpio_if,
|
||||||
|
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||||
|
.master_mode = false,
|
||||||
|
.use_mclk = CONFIG_EXAMPLE_I2S_MCLK_IO >= 0,
|
||||||
|
.pa_pin = CONFIG_EXAMPLE_PA_CTRL_IO,
|
||||||
|
.pa_reverted = false,
|
||||||
|
.hw_gain = {
|
||||||
|
.pa_voltage = 5.0,
|
||||||
|
.codec_dac_voltage = 3.3,
|
||||||
|
},
|
||||||
|
.mclk_div = EXAMPLE_MCLK_MULTIPLE,
|
||||||
|
};
|
||||||
|
return es8389_codec_new(&dac_cfg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static i2s_chan_handle_t i2s_driver_init(void)
|
||||||
|
{
|
||||||
|
/* Simplex TX: only a transmit channel. I2S_ROLE_MASTER means the SoC generates
|
||||||
|
* BCLK and WS. The codec must be configured as I2S slave (see create_dac_codec_if).
|
||||||
|
* auto_clear writes zeros if the DMA ring underruns, which avoids a stuck last sample. */
|
||||||
|
i2s_chan_handle_t tx_handle = NULL;
|
||||||
|
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||||
|
chan_cfg.auto_clear = true;
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||||
|
|
||||||
|
/* Standard (Philips) I2S: WS low = left slot, WS high = right slot.
|
||||||
|
* Each slot is EXAMPLE_BITS_PER_SAMPLE bits. Stereo 16-bit uses
|
||||||
|
* BCLK = sample_rate * 32. MCLK, if used, is sample_rate * EXAMPLE_MCLK_MULTIPLE. */
|
||||||
|
i2s_std_config_t std_cfg = {
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
|
||||||
|
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(EXAMPLE_BITS_PER_SAMPLE, EXAMPLE_CHANNEL_COUNT),
|
||||||
|
.gpio_cfg = {
|
||||||
|
.mclk = CONFIG_EXAMPLE_I2S_MCLK_IO,
|
||||||
|
.bclk = CONFIG_EXAMPLE_I2S_BCLK_IO,
|
||||||
|
.ws = CONFIG_EXAMPLE_I2S_WS_IO,
|
||||||
|
.dout = CONFIG_EXAMPLE_I2S_DOUT_IO,
|
||||||
|
.din = GPIO_NUM_NC,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||||
|
/* Enable now so BCLK/WS (and MCLK) are already running when the codec PLL starts. */
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
return tx_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dac_codec_init(i2s_chan_handle_t tx_handle)
|
||||||
|
{
|
||||||
|
/* I2C programs codec registers (volume, mute, clock source, PA). PCM is still
|
||||||
|
* written with i2s_channel_write(); the handle is only handed to the codec
|
||||||
|
* so esp_codec_dev_open() can match the slot format already set on I2S. */
|
||||||
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
|
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.i2c_port = -1, /* Let the driver select a free I2C port */
|
||||||
|
.sda_io_num = CONFIG_EXAMPLE_I2C_SDA_IO,
|
||||||
|
.scl_io_num = CONFIG_EXAMPLE_I2C_SCL_IO,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
|
/* Enable the internal pull-ups in case there is no external pull-up on the board.
|
||||||
|
* External pull-ups are recommended to make the bus more robust. */
|
||||||
|
.flags.enable_internal_pullup = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_handle));
|
||||||
|
|
||||||
|
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||||
|
.bus_handle = i2c_bus_handle,
|
||||||
|
.addr = EXAMPLE_CODEC_I2C_ADDR,
|
||||||
|
.clock_speed_hz = EXAMPLE_I2C_CLOCK_HZ,
|
||||||
|
};
|
||||||
|
const audio_codec_ctrl_if_t *ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||||
|
ESP_ERROR_CHECK(ctrl_if ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||||
|
ESP_ERROR_CHECK(gpio_if ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
const audio_codec_if_t *codec_if = create_dac_codec_if(ctrl_if, gpio_if);
|
||||||
|
ESP_ERROR_CHECK(codec_if ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||||
|
.tx_handle = tx_handle,
|
||||||
|
};
|
||||||
|
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||||
|
ESP_ERROR_CHECK(data_if ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
esp_codec_dev_cfg_t dev_cfg = {
|
||||||
|
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
|
||||||
|
.codec_if = codec_if,
|
||||||
|
.data_if = data_if,
|
||||||
|
};
|
||||||
|
esp_codec_dev_handle_t codec_dev = esp_codec_dev_new(&dev_cfg);
|
||||||
|
ESP_ERROR_CHECK(codec_dev ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
/* open() applies the same sample format to the codec (and keeps the I2S
|
||||||
|
* channel in sync), then enables the DAC and the board PA. */
|
||||||
|
esp_codec_dev_sample_info_t sample_cfg = {
|
||||||
|
.bits_per_sample = EXAMPLE_BITS_PER_SAMPLE,
|
||||||
|
.channel = EXAMPLE_CHANNEL_COUNT,
|
||||||
|
.channel_mask = EXAMPLE_STEREO_CHANNEL_MASK,
|
||||||
|
.sample_rate = EXAMPLE_SAMPLE_RATE,
|
||||||
|
.mclk_multiple = EXAMPLE_MCLK_MULTIPLE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_codec_dev_open(codec_dev, &sample_cfg));
|
||||||
|
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(codec_dev, CONFIG_EXAMPLE_VOICE_VOLUME));
|
||||||
|
ESP_LOGI(TAG, "%s codec init success", EXAMPLE_CODEC_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("wav player example start\n--------------------------\n");
|
||||||
|
|
||||||
|
const uint8_t *pcm = NULL;
|
||||||
|
size_t pcm_len = 0;
|
||||||
|
ESP_ERROR_CHECK(wav_get_pcm(music_wav_start, music_wav_end - music_wav_start, &pcm, &pcm_len));
|
||||||
|
|
||||||
|
i2s_chan_handle_t tx_handle = i2s_driver_init();
|
||||||
|
ESP_LOGI(TAG, "i2s driver init success");
|
||||||
|
|
||||||
|
dac_codec_init(tx_handle);
|
||||||
|
|
||||||
|
/* Disable TX, preload the DMA descriptors, then enable. Otherwise the first
|
||||||
|
* frames on the wire are zeros or stale SRAM (a click at the start of playback). */
|
||||||
|
size_t bytes_write = 0;
|
||||||
|
const uint8_t *data_ptr = pcm;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, pcm_len, &bytes_write));
|
||||||
|
data_ptr += bytes_write;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
size_t remain = (size_t)(pcm + pcm_len - data_ptr);
|
||||||
|
while (remain > 0) {
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_write(tx_handle, data_ptr, remain, &bytes_write, portMAX_DELAY));
|
||||||
|
if (bytes_write == 0) {
|
||||||
|
ESP_LOGE(TAG, "i2s music play failed.");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
data_ptr += bytes_write;
|
||||||
|
remain -= bytes_write;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "i2s music played, %d bytes are written.", (int)pcm_len);
|
||||||
|
data_ptr = pcm;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
examples/peripherals/i2s/wav_player/pytest_wav_player.py
Normal file
35
examples/peripherals/i2s/wav_player/pytest_wav_player.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
from pytest_embedded_idf.utils import soc_filtered_targets
|
||||||
|
|
||||||
|
I2S_TARGETS = soc_filtered_targets('SOC_I2S_SUPPORTED == 1')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config',
|
||||||
|
[
|
||||||
|
'es8311',
|
||||||
|
'es8389',
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
@idf_parametrize(
|
||||||
|
'target',
|
||||||
|
I2S_TARGETS,
|
||||||
|
indirect=['target'],
|
||||||
|
)
|
||||||
|
def test_wav_player_example(dut: Dut, config: str) -> None:
|
||||||
|
dut.expect_exact('wav player example start')
|
||||||
|
dut.expect_exact('wav_player: i2s driver init success')
|
||||||
|
|
||||||
|
# Generic CI runners have no codec; only check the rest when running locally.
|
||||||
|
if os.getenv('CI_JOB_ID'):
|
||||||
|
return
|
||||||
|
|
||||||
|
dut.expect_exact(f'wav_player: {config.upper()} codec init success', timeout=10)
|
||||||
1
examples/peripherals/i2s/wav_player/sdkconfig.ci.es8311
Normal file
1
examples/peripherals/i2s/wav_player/sdkconfig.ci.es8311
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_EXAMPLE_DAC_TYPE_ES8311=y
|
||||||
1
examples/peripherals/i2s/wav_player/sdkconfig.ci.es8389
Normal file
1
examples/peripherals/i2s/wav_player/sdkconfig.ci.es8389
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_EXAMPLE_DAC_TYPE_ES8389=y
|
||||||
3
examples/peripherals/i2s/wav_player/sdkconfig.defaults
Normal file
3
examples/peripherals/i2s/wav_player/sdkconfig.defaults
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# This file must exist. CMake only loads sdkconfig.defaults.<target>
|
||||||
|
# as a companion of each file listed in SDKCONFIG_DEFAULTS, which
|
||||||
|
# defaults to this path.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_I2C_SDA_IO=21
|
||||||
|
CONFIG_EXAMPLE_I2C_SCL_IO=22
|
||||||
|
CONFIG_EXAMPLE_I2S_BCLK_IO=4
|
||||||
|
CONFIG_EXAMPLE_I2S_WS_IO=5
|
||||||
|
CONFIG_EXAMPLE_I2S_DOUT_IO=18
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
CONFIG_EXAMPLE_I2C_SDA_IO=0
|
||||||
|
CONFIG_EXAMPLE_I2C_SCL_IO=1
|
||||||
|
CONFIG_EXAMPLE_I2S_BCLK_IO=3
|
||||||
|
CONFIG_EXAMPLE_I2S_WS_IO=4
|
||||||
|
CONFIG_EXAMPLE_I2S_DOUT_IO=5
|
||||||
|
CONFIG_EXAMPLE_PA_CTRL_IO=7
|
||||||
@@ -106,7 +106,6 @@ env_markers =
|
|||||||
httpbin: runner for tests that need to access the httpbin service
|
httpbin: runner for tests that need to access the httpbin service
|
||||||
flash_4mb: C2 runners with 4 MB flash
|
flash_4mb: C2 runners with 4 MB flash
|
||||||
jtag_re_enable: Runner to re-enable jtag which is softly disabled by burning bit SOFT_DIS_JTAG on eFuse
|
jtag_re_enable: Runner to re-enable jtag which is softly disabled by burning bit SOFT_DIS_JTAG on eFuse
|
||||||
es8311: Development board that carries es8311 codec
|
|
||||||
camera: Runner with camera
|
camera: Runner with camera
|
||||||
ov5647: Runner with camera ov5647
|
ov5647: Runner with camera ov5647
|
||||||
esp32s3_eye: Runner for esp32s3_eye and camera ov2640
|
esp32s3_eye: Runner for esp32s3_eye and camera ov2640
|
||||||
|
|||||||
Reference in New Issue
Block a user