i2s: add basic examples for STD/TDM/PDM mode
This commit is contained in:
@@ -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(i2s_std_example)
|
||||
@@ -0,0 +1,73 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# I2S Basic Standard Mode Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example is going to show how to use the standard mode in simplex mode or full-duplex mode.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with any one of ESP32, ESP32-S2, ESP32-C3 or ESP32-S3 SoC
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
There are simplex mode and duplex mode can be chosen in this example, setting `EXAMPLE_I2S_DUPLEX_MODE` to `0` will adopt the simplex mode, otherwise it will adopt the full-duplex mode.
|
||||
|
||||
Note that ESP32-S2 simplex mode is not available because this example requires both TX & RX channels, however, ESP32-S2 has only one I2S controller and the simplex TX & RX channels can't coexist in a same controller. By the way, the simplex TX & RX channels can't coexist on ESP32 as well, but ESP32 has two I2S controllers so the simplex TX & RX channels can be registered on the different I2S controllers.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT build 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
|
||||
|
||||
While `EXAMPLE_I2S_DUPLEX_MODE` is set to `1`, then you can see the following log:
|
||||
|
||||
```
|
||||
Write Task: i2s write 2048 bytes
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] 12 [1] 34 [2] 56 [3] 78
|
||||
[4] 9a [5] bc [6] de [7] f0
|
||||
|
||||
Write Task: i2s write 2048 bytes
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] 12 [1] 34 [2] 56 [3] 78
|
||||
[4] 9a [5] bc [6] de [7] f0
|
||||
...
|
||||
```
|
||||
|
||||
While `EXAMPLE_I2S_DUPLEX_MODE` is set to `0`, you can see the receiving data is always `0` because no signal inputted:
|
||||
|
||||
```
|
||||
Write Task: i2s write 2048 bytes
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] 0 [1] 0 [2] 0 [3] 0
|
||||
[4] 0 [5] 0 [6] 0 [7] 0
|
||||
|
||||
Write Task: i2s write 2048 bytes
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] 0 [1] 0 [2] 0 [3] 0
|
||||
[4] 0 [5] 0 [6] 0 [7] 0
|
||||
...
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "i2s_std_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
|
||||
* Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated,
|
||||
* Specifically, due to the hardware limitation, the simplex rx & tx channels can't be registered on the same controllers on ESP32 and ESP32-S2,
|
||||
* and ESP32-S2 has only one I2S controller, so it can't allocate two simplex channels */
|
||||
#define EXAMPLE_I2S_DUPLEX_MODE (1 || CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define EXAMPLE_STD_BCLK_IO1 GPIO_NUM_4 // I2S bit clock io number
|
||||
#define EXAMPLE_STD_WS_IO1 GPIO_NUM_5 // I2S word select io number
|
||||
#define EXAMPLE_STD_DOUT_IO1 GPIO_NUM_18 // I2S data out io number
|
||||
#define EXAMPLE_STD_DIN_IO1 GPIO_NUM_19 // I2S data in io number
|
||||
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||
#define EXAMPLE_STD_BCLK_IO2 GPIO_NUM_22 // I2S bit clock io number
|
||||
#define EXAMPLE_STD_WS_IO2 GPIO_NUM_23 // I2S word select io number
|
||||
#define EXAMPLE_STD_DOUT_IO2 GPIO_NUM_25 // I2S data out io number
|
||||
#define EXAMPLE_STD_DIN_IO2 GPIO_NUM_26 // I2S data in io number
|
||||
#endif
|
||||
#else
|
||||
#define EXAMPLE_STD_BCLK_IO1 GPIO_NUM_2 // I2S bit clock io number
|
||||
#define EXAMPLE_STD_WS_IO1 GPIO_NUM_3 // I2S word select io number
|
||||
#define EXAMPLE_STD_DOUT_IO1 GPIO_NUM_4 // I2S data out io number
|
||||
#define EXAMPLE_STD_DIN_IO1 GPIO_NUM_5 // I2S data in io number
|
||||
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||
#define EXAMPLE_STD_BCLK_IO2 GPIO_NUM_6 // I2S bit clock io number
|
||||
#define EXAMPLE_STD_WS_IO2 GPIO_NUM_7 // I2S word select io number
|
||||
#define EXAMPLE_STD_DOUT_IO2 GPIO_NUM_8 // I2S data out io number
|
||||
#define EXAMPLE_STD_DIN_IO2 GPIO_NUM_9 // I2S data in io number
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define EXAMPLE_BUFF_SIZE 2048
|
||||
|
||||
static i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
||||
static i2s_chan_handle_t rx_chan; // I2S rx channel handler
|
||||
|
||||
static void i2s_example_read_task(void *args)
|
||||
{
|
||||
uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||
assert(r_buf); // Check if r_buf allocation success
|
||||
size_t r_bytes = 0;
|
||||
while (1) {
|
||||
/* Read i2s data */
|
||||
if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
|
||||
printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
|
||||
printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
|
||||
r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
|
||||
} else {
|
||||
printf("Read Task: i2s read failed\n");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
free(r_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void i2s_example_write_task(void *args)
|
||||
{
|
||||
uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||
assert(w_buf); // Check if w_buf allocation success
|
||||
|
||||
/* Assign w_buf */
|
||||
for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
|
||||
w_buf[i] = 0x12;
|
||||
w_buf[i + 1] = 0x34;
|
||||
w_buf[i + 2] = 0x56;
|
||||
w_buf[i + 3] = 0x78;
|
||||
w_buf[i + 4] = 0x9A;
|
||||
w_buf[i + 5] = 0xBC;
|
||||
w_buf[i + 6] = 0xDE;
|
||||
w_buf[i + 7] = 0xF0;
|
||||
}
|
||||
|
||||
size_t w_bytes = 0;
|
||||
while (1) {
|
||||
/* Write i2s data */
|
||||
if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
|
||||
printf("Write Task: i2s write %d bytes\n", w_bytes);
|
||||
} else {
|
||||
printf("Write Task: i2s write failed\n");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
free(w_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||
static void i2s_example_init_std_duplex(void)
|
||||
{
|
||||
/* Setp 1: Determine the I2S channel configuration and allocate both channels
|
||||
* The default configuration can be generated by the helper macro,
|
||||
* it only requires the I2S controller id and I2S role */
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
|
||||
|
||||
/* Step 2: Setting the configurations of standard mode, and initialize rx & tx channels
|
||||
* The slot configuration and clock configuration can be generated by the macros
|
||||
* These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_STD_BCLK_IO1,
|
||||
.ws = EXAMPLE_STD_WS_IO1,
|
||||
.dout = EXAMPLE_STD_DOUT_IO1,
|
||||
.din = EXAMPLE_STD_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* Initialize the channels */
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void i2s_example_init_std_simplex(void)
|
||||
{
|
||||
/* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
|
||||
* The default configuration can be generated by the helper macro,
|
||||
* it only requires the I2S controller id and I2S role
|
||||
* The tx and rx channels here are registered on different I2S controller,
|
||||
* only ESP32-C3, ESP32-S3 and ESP32-H2 allow to register two separate tx & rx channels on a same controller */
|
||||
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
|
||||
|
||||
/* Step 2: Setting the configurations of standard mode and initialize each channels one by one
|
||||
* The slot configuration and clock configuration can be generated by the macros
|
||||
* These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_std_config_t tx_std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_STD_BCLK_IO1,
|
||||
.ws = EXAMPLE_STD_WS_IO1,
|
||||
.dout = EXAMPLE_STD_DOUT_IO1,
|
||||
.din = EXAMPLE_STD_DIN_IO1,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
|
||||
|
||||
i2s_std_config_t rx_std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_STD_BCLK_IO2,
|
||||
.ws = EXAMPLE_STD_WS_IO2,
|
||||
.dout = EXAMPLE_STD_DOUT_IO2,
|
||||
.din = EXAMPLE_STD_DIN_IO2,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* Default is only receiving left slot in mono mode,
|
||||
* update to right here to show how to change the default configuration */
|
||||
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_ONLY_RIGHT;
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||
i2s_example_init_std_duplex();
|
||||
#else
|
||||
i2s_example_init_std_simplex();
|
||||
#endif
|
||||
|
||||
/* Step 3: Enable the tx and rx channels before writing or reading data */
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
|
||||
|
||||
/* Step 4: Create writing and reading task */
|
||||
xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
|
||||
xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.generic
|
||||
def test_i2s_basic_example(dut: Dut) -> None:
|
||||
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: tx channel is registered on I2S0 successfully', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: rx channel is registered on I2S0 successfully', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
|
||||
r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz '
|
||||
r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_std: The tx channel on I2S0 has been initialized to STD mode successfully', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), '
|
||||
r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz '
|
||||
r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_std: The rx channel on I2S0 has been initialized to STD mode successfully', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: i2s rx channel enabled', timeout=5)
|
||||
dut.expect(r'Write Task: i2s write ([0-9]+) bytes', timeout=5)
|
||||
dut.expect(r'Read Task: i2s read ([0-9]+) bytes', timeout=5)
|
||||
dut.expect(r'-----------------------------------', timeout=5)
|
||||
dut.expect(r'\[0\] 0 \[1\] 0 \[2\] 0 \[3\] 0', timeout=5)
|
||||
dut.expect(r'\[4\] 0 \[5\] 0 \[6\] 0 \[7\] 0', timeout=5)
|
||||
dut.expect(r'\[0\] 12 \[1\] 34 \[2\] 56 \[3\] 78', timeout=10)
|
||||
dut.expect(r'\[4\] 9a \[5\] bc \[6\] de \[7\] f0', timeout=10)
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
Reference in New Issue
Block a user