i2s: add basic examples for STD/TDM/PDM mode
This commit is contained in:
@@ -1,98 +0,0 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# I2S Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
In this example, we generate a 100Hz triangle and sine wave and send it out from left and right channels at a sample rate of 36kHz through the I2S bus.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3/ESP32-S3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### 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, you will see I2S start to read and write data, and only the first 4 elements in the receive buffer will be displayed. The output log can be seen below:
|
||||
|
||||
```
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
|
||||
[i2s read] 8192 bytes are read successfully
|
||||
----------------------------------------------
|
||||
[0] 0 [1] 0 [2] 0 [3] 0
|
||||
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
|
||||
[i2s read] 8192 bytes are read successfully
|
||||
----------------------------------------------
|
||||
[0] a7d468d9 [1] a88a6a1d [2] a9406b58 [3] a9f66c8b
|
||||
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
|
||||
[i2s read] 8192 bytes are read successfully
|
||||
----------------------------------------------
|
||||
[0] 8b622120 [1] 8c182347 [2] 8cce256c [3] 8d84278d
|
||||
```
|
||||
|
||||
There is a abnormal case that printing `Data dropped`, it is caused by a long polling time of `i2s_channel_read`, please refer to the `Application Notes` section in I2S API reference.
|
||||
|
||||
```
|
||||
[i2s read] 8192 bytes are read successfully
|
||||
----------------------------------------------
|
||||
[0] a7d468d9 [1] a88a6a1d [2] a9406b58 [3] a9f66c8b
|
||||
|
||||
|
||||
[i2s monitor] Data dropped
|
||||
|
||||
|
||||
[i2s monitor] Data dropped
|
||||
|
||||
|
||||
[i2s monitor] Data dropped
|
||||
|
||||
[i2s write] 1440 bytes are written successfully
|
||||
|
||||
[i2s monitor] Data dropped
|
||||
```
|
||||
|
||||
If you have a logic analyzer, you can use a logic analyzer to grab online data. 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 |
|
||||
|:---:|:---:|:---:|
|
||||
| WS |word select| GPIO_NUM_15 |
|
||||
| SCK |continuous serial clock| GPIO_NUM_13 |
|
||||
| SD |serial data| GPIO_NUM_21 |
|
||||
|
||||
## 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.
|
||||
|
||||
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
-1
@@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32_i2s_driver_example)
|
||||
project(i2s_pdm_example)
|
||||
@@ -0,0 +1,165 @@
|
||||
| Supported Targets | ESP32 | 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 PDM TX and RX mode.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
#### General
|
||||
|
||||
* A development board with any one of ESP32, ESP32-C3 or ESP32-S3 SoC
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
#### PDM RX
|
||||
|
||||
* A PDM microphone whose `sel` pin is supposed to be pulled down, and connecting its `clk` pin to `GPIO_NUM_4`, `data` pin to `GPIO_NUM_5`.
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────────┐
|
||||
│ ESP │ │ PDM microphone │
|
||||
│ │ PDM clock │ │
|
||||
│ GPIO 0 ├──────────────►│ CLK │
|
||||
│ │ PDM data │ │
|
||||
│ GPIO 2 │◄──────────────┤ DATA │
|
||||
│ │ │ │
|
||||
│ │ ┌─────┤ SEL │
|
||||
│ │ │ │ │
|
||||
│ GND ├─────────┴─────┤ GND │
|
||||
│ │ │ │
|
||||
│ VCC ├───────────────┤ VCC │
|
||||
└─────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
#### PDM TX
|
||||
|
||||
* An earphone or a speaker
|
||||
* An audio power amplifier that can input PDM signal. If the power amplifier can only receive the analog signal without PDM clock, a band-pass filter is required to restore the PDM data wave into analog signal, before it is transmitted to the power amplifier.
|
||||
|
||||
**MAX98358**
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌───────────────┐
|
||||
│ ESP │ │ MAX 98358 │
|
||||
│ │ PDM clock │ │
|
||||
│ GPIO 4 ├──────────────►│ CLK │ ┌─────────┐
|
||||
│ │ PDM data │ │ │ Speaker │
|
||||
│ GPIO 5 ├──────────────►│ DATA OUTP ├───┤ │
|
||||
│ │ │ │ │ │
|
||||
│ │ ┌─────┤ SD_MODE OUTN ├───┤ │
|
||||
│ │ │ │ │ │ │
|
||||
│ VCC ├─────────┴─────┤ VCC │ └─────────┘
|
||||
│ │ │ │
|
||||
│ GND ├───────────────┤ GND │
|
||||
└─────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
**NS4150**
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌───────────────┐
|
||||
│ ESP │ │ NS 4150 │
|
||||
│ │ │ │
|
||||
│ GPIO 4 │ │ INN │ ┌─────────┐
|
||||
│ │PDM data┌────────────────┐ │ │ │ Speaker │
|
||||
│ GPIO 5 ├────────┤Band-pass Filter├───►│ INP VoP ├───┤ │
|
||||
│ │ └────────────────┘ │ │ │ │
|
||||
│ │ ┌───┤ CTRL VoN ├───┤ │
|
||||
│ │ │ │ │ │ │
|
||||
│ VCC ├──────────────────────────┴───┤ VCC │ └─────────┘
|
||||
│ │ │ │
|
||||
│ GND ├──────────────────────────────┤ GND │
|
||||
└─────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
### Configure the Project
|
||||
|
||||
PDM can only works in simplex mode, setting the macro `EXAMPLE_PDM_DIR` to `EXAMPLE_PDM_TX` or `EXAMPLE_PDM_RX` can choose the PDM direction of this example. But currently ESP32-C3 does not support PDM RX mode.
|
||||
|
||||
### 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
|
||||
|
||||
### PDM TX
|
||||
|
||||
While `EXAMPLE_PDM_DIR` is set to `EXAMPLE_PDM_TX`, then you can see the following log:
|
||||
|
||||
```
|
||||
I2S PDM TX example start
|
||||
---------------------------
|
||||
D (284) i2s_common: tx channel is registered on I2S0 successfully
|
||||
D (294) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
|
||||
D (304) i2s_pdm: Clock division info: [sclk] 160000000 Hz [mdiv] 3 [mclk] 49152000 Hz [bdiv] 8 [bclk] 6144000 Hz
|
||||
D (314) i2s_pdm: The tx channel on I2S0 has been initialized to PDM TX mode successfully
|
||||
D (324) i2s_common: i2s tx channel enabled
|
||||
Playing bass `twinkle twinkle little star`
|
||||
Playing alto `twinkle twinkle little star`
|
||||
Playing treble `twinkle twinkle little star`
|
||||
...
|
||||
```
|
||||
|
||||
You can hear the audio 'twinkle twinkle little star' in three tones if you connected a speaker.on it.
|
||||
|
||||
### PDM RX
|
||||
|
||||
While `EXAMPLE_PDM_DIR` is set to `EXAMPLE_PDM_RX`, but without connecting a PDM microphone, then you can see the following log:
|
||||
|
||||
```
|
||||
I2S PDM RX example start
|
||||
---------------------------
|
||||
D (10) i2s_common: rx channel is registered on I2S0 successfully
|
||||
D (10) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
|
||||
D (20) i2s_common: i2s rx channel enabled
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] -6595 [1] 0 [2] -29199 [3] -32768
|
||||
[4] -30203 [5] -32156 [6] -30704 [7] -31348
|
||||
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] -30935 [1] -30935 [2] -30935 [3] -30935
|
||||
[4] -30935 [5] -30935 [6] -30935 [7] -30935
|
||||
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] -30935 [1] -30935 [2] -30935 [3] -30935
|
||||
[4] -30935 [5] -30935 [6] -30935 [7] -30935
|
||||
```
|
||||
|
||||
And only if you connect a PDM microphone, you can see the data is change:
|
||||
|
||||
```
|
||||
I2S PDM RX example start
|
||||
---------------------------
|
||||
D (10) i2s_common: rx channel is registered on I2S0 successfully
|
||||
D (10) i2s_common: DMA malloc info: dma_desc_num = 6, dma_desc_buf_size = dma_frame_num * slot_num * data_bit_width = 500
|
||||
D (20) i2s_common: i2s rx channel enabled
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] -3181 [1] 0 [2] -7194 [3] -24288
|
||||
[4] -777 [5] 3650 [6] 109 [7] 571
|
||||
|
||||
Read Task: i2s read 2048 bytes
|
||||
-----------------------------------
|
||||
[0] 2391 [1] 2378 [2] 2397 [3] 2385
|
||||
[4] 2399 [5] 2375 [6] 2401 [7] 2389
|
||||
```
|
||||
|
||||
## 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,12 @@
|
||||
set(srcs "i2s_pdm_example_main.c")
|
||||
|
||||
if(CONFIG_SOC_I2S_SUPPORTS_PDM_TX)
|
||||
list(APPEND srcs "i2s_pdm_tx.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_I2S_SUPPORTS_PDM_RX)
|
||||
list(APPEND srcs "i2s_pdm_rx.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define EXAMPLE_BUFF_SIZE 2048
|
||||
|
||||
/**
|
||||
* @brief I2S PDM TX example task
|
||||
*
|
||||
* @param args The user data given from task creating, not used in this example
|
||||
*/
|
||||
void i2s_example_pdm_tx_task(void *args);
|
||||
|
||||
/**
|
||||
* @brief I2S PDM RX example task
|
||||
*
|
||||
* @param args The user data given from task creating, not used in this example
|
||||
*/
|
||||
void i2s_example_pdm_rx_task(void *args);
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 "sdkconfig.h"
|
||||
#include "i2s_pdm_example.h"
|
||||
|
||||
#define EXAMPLE_PDM_TX 0
|
||||
/* ESP32-C3 does not support PDM RX currently */
|
||||
#if !CONFIG_IDF_TARGET_ESP32C3
|
||||
#define EXAMPLE_PDM_RX 1
|
||||
#endif
|
||||
|
||||
#define EXAMPLE_PDM_DIR EXAMPLE_PDM_TX
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if EXAMPLE_PDM_DIR == EXAMPLE_PDM_TX
|
||||
printf("I2S PDM TX example start\n---------------------------\n");
|
||||
xTaskCreate(i2s_example_pdm_tx_task, "i2s_example_pdm_tx_task", 4096, NULL, 5, NULL);
|
||||
#else
|
||||
printf("I2S PDM RX example start\n---------------------------\n");
|
||||
xTaskCreate(i2s_example_pdm_rx_task, "i2s_example_pdm_rx_task", 4096, NULL, 5, NULL);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_pdm.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "i2s_pdm_example.h"
|
||||
|
||||
#include "hal/i2s_ll.h"
|
||||
|
||||
#define EXAMPLE_PDM_RX_CLK_IO GPIO_NUM_0 // I2S PDM RX clock io number
|
||||
#define EXAMPLE_PDM_RX_DIN_IO GPIO_NUM_2 // I2S PDM RX data in io number
|
||||
|
||||
#define EXAMPLE_PDM_RX_FREQ_HZ 16000 // I2S PDM RX frequency
|
||||
|
||||
static i2s_chan_handle_t rx_chan; // I2S rx channel handler
|
||||
|
||||
static void i2s_example_init_pdm_rx(void)
|
||||
{
|
||||
/* Setp 1: Determine the I2S channel configuration and allocate RX channel only
|
||||
* The default configuration can be generated by the helper macro,
|
||||
* but note that PDM channel can only be registered on I2S_NUM_0 */
|
||||
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 PDM RX mode and initialize the RX channel
|
||||
* The slot configuration and clock configuration can be generated by the macros
|
||||
* These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM RX mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_pdm_rx_config_t pdm_rx_cfg = {
|
||||
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_RX_FREQ_HZ),
|
||||
/* The data bit-width of PDM mode is fixed to 16 */
|
||||
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = {
|
||||
.clk = EXAMPLE_PDM_RX_CLK_IO,
|
||||
.din = EXAMPLE_PDM_RX_DIN_IO,
|
||||
.invert_flags = {
|
||||
.clk_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));
|
||||
|
||||
/* Step 3: Enable the rx channels before reading data */
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
|
||||
}
|
||||
|
||||
|
||||
void i2s_example_pdm_rx_task(void *args)
|
||||
{
|
||||
int16_t *r_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||
assert(r_buf);
|
||||
i2s_example_init_pdm_rx();
|
||||
|
||||
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] %d [1] %d [2] %d [3] %d\n[4] %d [5] %d [6] %d [7] %d\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);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "i2s_pdm_example.h"
|
||||
|
||||
#define EXAMPLE_PDM_TX_CLK_IO GPIO_NUM_4 // I2S PDM TX clock io number
|
||||
#define EXAMPLE_PDM_TX_DOUT_IO GPIO_NUM_5 // I2S PDM TX data out io number
|
||||
|
||||
#define EXAMPLE_PDM_TX_FREQ_HZ 44100 // I2S PDM TX frequency
|
||||
#define EXAMPLE_WAVE_AMPTITUDE (1000.0) // 1~32767
|
||||
#define CONST_PI (3.1416f)
|
||||
#define EXAMPLE_SINE_WAVE_LEN(tone) (uint32_t)((EXAMPLE_PDM_TX_FREQ_HZ / (float)tone) + 0.5) // The sample point number per sine wave to generate the tone
|
||||
#define EXAMPLE_TONE_LAST_TIME_MS 500
|
||||
#define EXAMPLE_BYTE_NUM_EVERY_TONE (EXAMPLE_TONE_LAST_TIME_MS * EXAMPLE_PDM_TX_FREQ_HZ / 1000)
|
||||
|
||||
static i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
||||
|
||||
static const uint32_t tone[3][7] = {{262, 294, 330, 349, 392, 440, 494},
|
||||
{523, 587, 659, 698, 784, 880, 988},
|
||||
{1046, 1175, 1318, 1397, 1568, 1760, 1976}}; // The frequency of tones: do, re, mi, fa, so, la, si, in Hz.
|
||||
static const uint8_t song[28] = {1, 1, 5, 5, 6, 6, 5,
|
||||
4, 4, 3, 3, 2, 2, 1,
|
||||
5, 5, 4, 4, 3, 3, 2,
|
||||
5, 5, 4, 4, 3, 3, 2}; // Numbered musical notation of 'twinkle twinkle little star'
|
||||
static const uint8_t rhythm[7] = {1, 1, 1, 1, 1, 1, 2}; // Rhythm of 'twinkle twinkle little star', it's repeated in four sections
|
||||
|
||||
static const char *tone_name[3] = {"bass", "alto", "treble"};
|
||||
|
||||
void i2s_example_init_pdm_tx(void)
|
||||
{
|
||||
/* Setp 1: Determine the I2S channel configuration and allocate TX channel only
|
||||
* The default configuration can be generated by the helper macro,
|
||||
* it only requires the I2S controller id and I2S role,
|
||||
* but note that PDM channel can only be registered on I2S_NUM_0 */
|
||||
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
tx_chan_cfg.auto_clear = true;
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||
|
||||
/* Step 2: Setting the configurations of PDM TX mode and initialize the TX channel
|
||||
* The slot configuration and clock configuration can be generated by the macros
|
||||
* These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM TX mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_pdm_tx_config_t pdm_tx_cfg = {
|
||||
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_TX_FREQ_HZ),
|
||||
/* The data bit-width of PDM mode is fixed to 16 */
|
||||
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = {
|
||||
.clk = EXAMPLE_PDM_TX_CLK_IO,
|
||||
.dout = EXAMPLE_PDM_TX_DOUT_IO,
|
||||
.invert_flags = {
|
||||
.clk_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg));
|
||||
|
||||
/* Step 3: Enable the tx channel before writing data */
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
}
|
||||
|
||||
void i2s_example_pdm_tx_task(void *args)
|
||||
{
|
||||
int16_t *w_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
||||
assert(w_buf);
|
||||
i2s_example_init_pdm_tx();
|
||||
|
||||
size_t w_bytes = 0;
|
||||
|
||||
uint8_t cnt = 0; // The current index of the song
|
||||
uint8_t tone_select = 0; // To selecting the tone level
|
||||
|
||||
printf("Playing %s `twinkle twinkle little star`\n", tone_name[tone_select]);
|
||||
while (1) {
|
||||
int tone_point = EXAMPLE_SINE_WAVE_LEN(tone[tone_select][song[cnt]-1]);
|
||||
/* Generate the tone buffer */
|
||||
for (int i = 0; i < tone_point; i++) {
|
||||
w_buf[i] = (int16_t)((sin(2 * (float)i * CONST_PI / tone_point)) * EXAMPLE_WAVE_AMPTITUDE);
|
||||
}
|
||||
for (int tot_bytes = 0; tot_bytes < EXAMPLE_BYTE_NUM_EVERY_TONE * rhythm[cnt % 7]; tot_bytes += w_bytes) {
|
||||
/* Play the tone */
|
||||
if (i2s_channel_write(tx_chan, w_buf, tone_point * sizeof(int16_t), &w_bytes, 1000) != ESP_OK) {
|
||||
printf("Write Task: i2s write failed\n");
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
/* If finished played, switch the tone level */
|
||||
if (cnt == sizeof(song)) {
|
||||
cnt = 0;
|
||||
tone_select++;
|
||||
tone_select %= 3;
|
||||
printf("Playing %s `twinkle twinkle little star`\n", tone_name[tone_select]);
|
||||
}
|
||||
/* Gap between the tones */
|
||||
vTaskDelay(15);
|
||||
}
|
||||
free(w_buf);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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.esp32s3
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.generic
|
||||
def test_i2s_pdm_example(dut: Dut) -> None:
|
||||
dut.expect(r'I2S PDM TX example start', timeout=5)
|
||||
dut.expect(r'---------------------------', timeout=5)
|
||||
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: 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_pdm: 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_pdm: The tx channel on I2S0 has been initialized to PDM TX mode successfully', timeout=5)
|
||||
dut.expect(r'D \(([0-9]+)\) i2s_common: i2s tx channel enabled', timeout=5)
|
||||
dut.expect(r'Playing bass `twinkle twinkle little star`', timeout=5)
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
@@ -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
|
||||
@@ -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_tdm_example)
|
||||
@@ -0,0 +1,72 @@
|
||||
| Supported Targets | ESP32-C3 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# I2S Basic TDM 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 TDM mode in simplex mode or full-duplex mode.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with any one of 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.
|
||||
|
||||
### 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_tdm_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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_tdm.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 */
|
||||
#define EXAMPLE_I2S_DUPLEX_MODE 1
|
||||
|
||||
#define EXAMPLE_TDM_BCLK_IO1 GPIO_NUM_2 // I2S bit clock io number
|
||||
#define EXAMPLE_TDM_WS_IO1 GPIO_NUM_3 // I2S word select io number
|
||||
#define EXAMPLE_TDM_DOUT_IO1 GPIO_NUM_4 // I2S data out io number
|
||||
#define EXAMPLE_TDM_DIN_IO1 GPIO_NUM_5 // I2S data in io number
|
||||
#if !EXAMPLE_I2S_DUPLEX_MODE
|
||||
#define EXAMPLE_TDM_BCLK_IO2 GPIO_NUM_6 // I2S bit clock io number
|
||||
#define EXAMPLE_TDM_WS_IO2 GPIO_NUM_7 // I2S word select io number
|
||||
#define EXAMPLE_TDM_DOUT_IO2 GPIO_NUM_8 // I2S data out io number
|
||||
#define EXAMPLE_TDM_DIN_IO2 GPIO_NUM_9 // I2S data in io number
|
||||
#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_tdm_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 TDM 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_tdm.h' which can only be used in TDM mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_tdm_config_t tdm_cfg = {
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||
/* Limited by the hardware, the number of bit clock can't exceed 128 in one frame,
|
||||
* which is to say, TDM mode can only support 32 bit-width data upto 4 slots,
|
||||
* 16 bit-width data upto 8 slots and 8 bit-width data upto 16 slots */
|
||||
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
|
||||
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_TDM_BCLK_IO1,
|
||||
.ws = EXAMPLE_TDM_WS_IO1,
|
||||
.dout = EXAMPLE_TDM_DOUT_IO1,
|
||||
.din = EXAMPLE_TDM_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,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
|
||||
* The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
|
||||
* Modify the mclk_multiple to 512 directly here to avoid the warning */
|
||||
tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||
|
||||
/* Initialize the channels */
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void i2s_example_init_tdm_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 */
|
||||
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 TDM 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_tdm.h' which can only be used in TDM mode.
|
||||
* They can help to specify the slot and clock configurations for initialization or re-configuring */
|
||||
i2s_tdm_config_t tx_tdm_cfg = {
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO,
|
||||
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_TDM_BCLK_IO1,
|
||||
.ws = EXAMPLE_TDM_WS_IO1,
|
||||
.dout = EXAMPLE_TDM_DOUT_IO1,
|
||||
.din = EXAMPLE_TDM_DIN_IO1,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
tx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tx_tdm_cfg));
|
||||
|
||||
i2s_tdm_config_t rx_tdm_cfg = {
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO,
|
||||
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 |
|
||||
I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
||||
.bclk = EXAMPLE_TDM_BCLK_IO2,
|
||||
.ws = EXAMPLE_TDM_WS_IO2,
|
||||
.dout = EXAMPLE_TDM_DOUT_IO2,
|
||||
.din = EXAMPLE_TDM_DIN_IO2,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too samll
|
||||
* The driver will increasing the multiple automatically to ensure the bclk_div bigger than 2.
|
||||
* Modify the mclk_multiple to 512 directly here to avoid the warning */
|
||||
rx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512;
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &rx_tdm_cfg));
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if EXAMPLE_I2S_DUPLEX_MODE
|
||||
i2s_example_init_tdm_duplex();
|
||||
#else
|
||||
i2s_example_init_tdm_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,33 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.generic
|
||||
def test_i2s_tdm_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_tdm: 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_tdm: The tx channel on I2S0 has been initialized to TDM 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_tdm: 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_tdm: The rx channel on I2S0 has been initialized to TDM 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
|
||||
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "i2s_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* I2S Example
|
||||
* This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
|
||||
* Every 5 seconds, it will change bits_per_sample [16, 24, 32] for i2s data
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include <math.h>
|
||||
|
||||
#define EXAMPLE_SAMPLE_RATE (36000)
|
||||
#define EXAMPLE_DATA_BIT_WIDTH (I2S_DATA_BIT_WIDTH_16BIT)
|
||||
|
||||
#define I2S_NUM (0)
|
||||
#define WAVE_FREQ_HZ (100)
|
||||
#define PI (3.14159265)
|
||||
#define I2S_BCK_IO (GPIO_NUM_4)
|
||||
#define I2S_WS_IO (GPIO_NUM_5)
|
||||
#define I2S_DO_IO (GPIO_NUM_18)
|
||||
#define I2S_DI_IO (GPIO_NUM_18) /// Loopback internally if data_out and data_in signal are bound to a same GPIO
|
||||
|
||||
#define SAMPLE_PER_CYCLE (EXAMPLE_SAMPLE_RATE/WAVE_FREQ_HZ)
|
||||
|
||||
static i2s_chan_handle_t tx_handle = NULL;
|
||||
static i2s_chan_handle_t rx_handle = NULL;
|
||||
|
||||
static volatile int is_overflow = 0;
|
||||
|
||||
static uint32_t* example_generate_triangle_sine_waves(int bits, uint32_t *buf_len)
|
||||
{
|
||||
uint32_t len = ((bits + 8) / 16)*SAMPLE_PER_CYCLE * 4;
|
||||
uint32_t *samples_data = malloc(len);
|
||||
|
||||
double triangle_float = -(pow(2, bits) / 2 - 1);
|
||||
double triangle_step = (double) pow(2, bits) / SAMPLE_PER_CYCLE;
|
||||
|
||||
for (int i = 0; i < SAMPLE_PER_CYCLE; i++) {
|
||||
double sin_float = sin(i * 2 * PI / SAMPLE_PER_CYCLE);
|
||||
if (sin_float >= 0) {
|
||||
triangle_float += triangle_step;
|
||||
} else {
|
||||
triangle_float -= triangle_step;
|
||||
}
|
||||
sin_float *= (pow(2, bits) / 2 - 1);
|
||||
if (bits == 16) {
|
||||
samples_data[i] = ((short)triangle_float << 16) | (short)sin_float;
|
||||
} else if (bits == 24) { //1-bytes unused
|
||||
samples_data[i * 2] = ((int) triangle_float) << 8;
|
||||
samples_data[i * 2 + 1] = ((int) sin_float) << 8;
|
||||
} else {
|
||||
samples_data[i * 2] = ((int) triangle_float);
|
||||
samples_data[i * 2 + 1] = ((int) sin_float);
|
||||
}
|
||||
|
||||
}
|
||||
*buf_len = len;
|
||||
return samples_data;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
|
||||
{
|
||||
is_overflow++;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void example_i2s_read_task(void * args)
|
||||
{
|
||||
uint32_t *rx_buf = calloc(1, 8192);
|
||||
size_t bytes_read = 0;
|
||||
uint32_t cnt = 0;
|
||||
|
||||
while (1) {
|
||||
if (i2s_channel_read(rx_handle, rx_buf, 8192, &bytes_read, 1000) == ESP_OK) {
|
||||
if (cnt == 0) {
|
||||
printf("\n[i2s read] %d bytes are read successfully\n", bytes_read);
|
||||
printf("----------------------------------------------\n");
|
||||
printf("[0] %4x [1] %4x [2] %4x [3] %4x\n\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
|
||||
}
|
||||
cnt++;
|
||||
cnt %= 10;
|
||||
/* If the polling time is too long, there will be data dropped event */
|
||||
// vTaskDelay(10);
|
||||
} else {
|
||||
printf("[i2s read] %d bytes are read, timeout triggered\n\n", bytes_read);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void example_i2s_write_task(void * args)
|
||||
{
|
||||
uint32_t buf_len = 0;
|
||||
uint32_t *tx_buf = example_generate_triangle_sine_waves(EXAMPLE_DATA_BIT_WIDTH, &buf_len);
|
||||
size_t bytes_written = 0;
|
||||
uint32_t cnt = 0;
|
||||
|
||||
while (1) {
|
||||
if (i2s_channel_write(tx_handle, tx_buf, buf_len, &bytes_written, 1000) == ESP_OK) {
|
||||
if (cnt == 0) {
|
||||
printf("[i2s write] %d bytes are written successfully\n", bytes_written);
|
||||
}
|
||||
cnt++;
|
||||
cnt %= 20;
|
||||
} else {
|
||||
printf("[i2s write] %d bytes are written, timeout triggered\n", bytes_written);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void example_i2s_init_std_duplex(void)
|
||||
{
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
/* Giving both tx and rx handle will make the i2s works in full-duplex mode and can share the bclk and ws signal */
|
||||
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_MSB_SLOT_DEFAULT_CONFIG(EXAMPLE_DATA_BIT_WIDTH, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.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,
|
||||
},
|
||||
},
|
||||
};
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
// APLL clock is more accurate when sample rate is high
|
||||
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
|
||||
#endif
|
||||
/* Initialize the tx channel handle to standard mode */
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
/* Initialize the rx channel handle to standard mode */
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
|
||||
printf("I2S tx and rx channels have been initialized to standard duplex mode\n");
|
||||
|
||||
i2s_event_callbacks_t cbs = {
|
||||
.on_recv = NULL,
|
||||
.on_recv_q_ovf = i2s_rx_queue_overflow_callback,
|
||||
.on_sent = NULL,
|
||||
.on_send_q_ovf = NULL,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_register_event_callback(rx_handle, &cbs, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
|
||||
printf("I2S tx and rx channels enabled\n");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
|
||||
example_i2s_init_std_duplex();
|
||||
xTaskCreate(example_i2s_write_task, "i2s write task", 4096, NULL, 5, NULL);
|
||||
xTaskCreate(example_i2s_read_task, "i2s read task", 8192, NULL, 5, NULL);
|
||||
|
||||
while (1) {
|
||||
if (is_overflow > 0) {
|
||||
printf("[i2s monitor] RX message Queue overflowed\n");
|
||||
is_overflow--;
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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_exact('I2S tx and rx channels have been initialized to standard duplex mode', timeout=30)
|
||||
dut.expect_exact('I2S tx and rx channels enabled', timeout=30)
|
||||
dut.expect_exact('[i2s write] 1440 bytes are written successfully', timeout=30)
|
||||
dut.expect_exact('', timeout=30)
|
||||
dut.expect_exact('[i2s read] 8192 bytes are read successfully', timeout=30)
|
||||
Reference in New Issue
Block a user