refactor(i2s): make i2s driver as component

This commit is contained in:
laokaiyao
2023-11-07 21:06:07 +08:00
parent f0129e17a8
commit 74441d14ab
68 changed files with 297 additions and 177 deletions

View File

@@ -1,21 +0,0 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/driver/,${CMAKE_BINARY_DIR}/esp-idf/hal/
--elf-file ${CMAKE_BINARY_DIR}/i2s_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@@ -1,2 +0,0 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -1,7 +0,0 @@
set(srcs "test_app_main.c"
"test_i2s.c"
"test_i2s_iram.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity esp_driver_pcnt driver spi_flash
WHOLE_ARCHIVE)

View File

@@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in I2S driver, the threadhold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ___ ____ ____ _____ _
// |_ _|___ \/ ___| |_ _|__ ___| |_
// | | __) \___ \ | |/ _ \/ __| __|
// | | / __/ ___) | | | __/\__ \ |_
// |___|_____|____/ |_|\___||___/\__|
printf(" ___ ____ ____ _____ _ \r\n");
printf(" |_ _|___ \\/ ___| |_ _|__ ___| |_ \r\n");
printf(" | | __) \\___ \\ | |/ _ \\/ __| __|\r\n");
printf(" | | / __/ ___) | | | __/\\__ \\ |_ \r\n");
printf(" |___|_____|____/ |_|\\___||___/\\__|\r\n");
unity_run_menu();
}

View File

@@ -1,905 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "unity.h"
#include "math.h"
#include "esp_rom_gpio.h"
#include "soc/i2s_periph.h"
#include "driver/i2s_std.h"
#if SOC_I2S_SUPPORTS_PDM
#include "driver/i2s_pdm.h"
#endif
#if SOC_I2S_SUPPORTS_TDM
#include "driver/i2s_tdm.h"
#endif
#include "hal/i2s_hal.h"
#include "esp_private/i2s_platform.h"
#if SOC_PCNT_SUPPORTED
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#endif
#include "../../test_inc/test_i2s.h"
#define I2S_TEST_MODE_SLAVE_TO_MASTER 0
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
#define I2S_TEST_MASTER_DEFAULT_PIN { \
.mclk = MASTER_MCK_IO, \
.bclk = MASTER_BCK_IO, \
.ws = MASTER_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
.mclk = -1, \
.bclk = SLAVE_BCK_IO, \
.ws = SLAVE_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
static void i2s_test_io_config(int mode)
{
// Connect internal signals using IO matrix.
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_BCK_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[DATA_OUT_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_BCK_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
gpio_set_direction(DATA_OUT_IO, GPIO_MODE_INPUT_OUTPUT);
switch (mode) {
#if SOC_I2S_NUM > 1
case I2S_TEST_MODE_SLAVE_TO_MASTER: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_rx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_tx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_tx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[1].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
case I2S_TEST_MODE_MASTER_TO_SLAVE: {
esp_rom_gpio_connect_out_signal(MASTER_BCK_IO, i2s_periph_signal[0].m_tx_bck_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_BCK_IO, i2s_periph_signal[1].s_rx_bck_sig, 0);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, i2s_periph_signal[1].s_rx_ws_sig, 0);
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[1].data_in_sig, 0);
}
break;
#endif
case I2S_TEST_MODE_LOOPBACK: {
esp_rom_gpio_connect_out_signal(DATA_OUT_IO, i2s_periph_signal[0].data_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(DATA_OUT_IO, i2s_periph_signal[0].data_in_sig, 0);
}
break;
default: {
TEST_FAIL_MESSAGE("error: mode not supported");
}
break;
}
}
static void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan)
{
#define I2S_SEND_BUF_LEN 100
#define I2S_RECV_BUF_LEN 10000
size_t bytes_write = 0;
size_t bytes_read = 0;
bool is_success = false;
uint8_t *send_buf = (uint8_t *)calloc(I2S_SEND_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(send_buf);
uint8_t *recv_buf = (uint8_t *)calloc(I2S_RECV_BUF_LEN, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(recv_buf);
for (int i = 0; i < I2S_SEND_BUF_LEN; i++) {
send_buf[i] = i + 1;
}
// write data to slave
TEST_ESP_OK(i2s_channel_write(tx_chan, send_buf, I2S_SEND_BUF_LEN, &bytes_write, 1000));
TEST_ESP_OK(i2s_channel_read(rx_chan, recv_buf, I2S_RECV_BUF_LEN, &bytes_read, 1000));
TEST_ASSERT_EQUAL_INT32(I2S_SEND_BUF_LEN, bytes_write);
TEST_ASSERT_EQUAL_INT32(I2S_RECV_BUF_LEN, bytes_read);
// test the read data right or not
for (int i = 0, j = 0; i < (I2S_RECV_BUF_LEN - I2S_SEND_BUF_LEN); i++) {
if (recv_buf[i] == 1) {
for (j = 1; (j < I2S_SEND_BUF_LEN) && (recv_buf[i+j] == j + 1); j++) {}
if (j == I2S_SEND_BUF_LEN) {
is_success = true;
goto finish;
}
i += j;
}
}
finish:
free(send_buf);
free(recv_buf);
TEST_ASSERT(is_success);
}
// To check if the software logic of I2S driver is correct
TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_chan_info_t chan_info;
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_NONE);
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.mode == I2S_COMM_MODE_STD);
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ASSERT(i2s_channel_get_info(tx_handle, &chan_info) == ESP_ERR_NOT_FOUND);
/* Duplex channel basic test */
chan_cfg.id = I2S_NUM_0; // Specify port id to I2S port 0
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
TEST_ASSERT(chan_info.pair_chan == rx_handle);
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Repeat to check if a same port can be allocated again */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
/* Hold the occupation */
TEST_ESP_OK(i2s_platform_acquire_occupation(I2S_NUM_0, "test_i2s"));
TEST_ASSERT(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle) == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(i2s_platform_release_occupation(I2S_NUM_0));
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
static volatile bool task_run_flag;
static void i2s_read_task(void *args) {
i2s_chan_handle_t rx_handle = (i2s_chan_handle_t)args;
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(recv_buf);
size_t recv_size = 0;
esp_err_t ret = ESP_OK;
uint32_t cnt = 1;
while (task_run_flag) {
ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300);
if (ret == ESP_ERR_TIMEOUT) {
printf("Read timeout count: %"PRIu32"\n", cnt++);
}
}
free(recv_buf);
vTaskDelete(NULL);
}
static void i2s_write_task(void *args) {
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
uint8_t *send_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(send_buf);
size_t send_size = 0;
esp_err_t ret = ESP_OK;
uint32_t cnt = 1;
while (task_run_flag) {
ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300);
if (ret == ESP_ERR_TIMEOUT) {
printf("Write timeout count: %"PRIu32"\n", cnt++);
}
}
free(send_buf);
vTaskDelete(NULL);
}
static void i2s_reconfig_task(void *args) {
i2s_chan_handle_t tx_handle = (i2s_chan_handle_t)args;
i2s_chan_info_t chan_info;
TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info));
i2s_chan_handle_t rx_handle = chan_info.pair_chan;
int cnt = 1;
while (task_run_flag) {
/* Reconfig the slot while reading / writing */
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the slot...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &slot_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
/* Reconfig the clock while reading / writing */
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE / 2);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the clock...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &clk_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &clk_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
/* Reconfig the gpio while reading / writing */
i2s_std_gpio_config_t gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_WS_IO,
.ws = MASTER_BCK_IO,
.dout = DATA_IN_IO,
.din = DATA_IN_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
};
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
printf("[%d] Reconfiguring the gpio...\n", cnt);
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(tx_handle, &gpio_cfg));
TEST_ESP_OK(i2s_channel_reconfig_std_gpio(rx_handle, &gpio_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
vTaskDelay(pdMS_TO_TICKS(200));
cnt++;
}
vTaskDelete(NULL);
}
TEST_CASE("I2S_thread_concurrent_safety_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
/* Enable the channels before creating reading/writing task*/
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
task_run_flag = true;
/* reading task to keep reading */
xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL);
/* writing task to keep writing */
xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL);
/* reconfig taks to reconfigurate the settings every 200 ms */
xTaskCreate(i2s_reconfig_task, "i2s_reconfig_task", 4096, tx_handle, 6, NULL);
/* Wait 3 seconds to see if any failures occur */
vTaskDelay(pdMS_TO_TICKS(4000));
/* Stop those three tasks */
task_run_flag = false;
/* Wait for the three thread deleted */
vTaskDelay(pdMS_TO_TICKS(1000));
/* Disable the channels, they will keep waiting until the current reading / writing finished */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
/* Delete the channels */
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
static bool whether_contains_exapected_data(uint16_t *src, uint32_t src_len, uint32_t src_step, uint32_t start_val, uint32_t val_step)
{
uint32_t val = start_val;
uint32_t index_step = 1;
for (int i = 0; val < 100 && i < src_len; i += index_step) {
if (src[i] == val) {
if (val == start_val && i < src_len - 8) {
printf("start index: %d ---> \n%d %d %d %d %d %d %d %d\n", i,
src[i], src[i+1], src[i+2], src[i+3],
src[i+4], src[i+5], src[i+6], src[i+7]);
}
index_step = src_step;
val += val_step;
} else {
index_step = 1;
val = start_val;
}
}
return val >= 100;
}
/**
* @brief Test mono and stereo mode of I2S by loopback
* @note Only rx channel distinguish left mono and right mono, tx channel does not
* @note 1. Check switch mono/stereo by 'i2s_set_clk'
* 2. Check rx right mono and left mono (requiring tx works in stereo mode)
* 3. Check tx mono (requiring rx works in stereo mode)
*/
TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]")
{
#define WRITE_BUF_LEN 2000
#define READ_BUF_LEN 4000
#define RETEY_TIMES 3
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
bool is_failed = false;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 8;
chan_cfg.dma_frame_num = 128;
i2s_std_config_t tx_std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
// In stereo mode
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_std_config_t rx_std_cfg = tx_std_cfg;
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
/* TX channel basic test */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &rx_std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &tx_std_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
uint16_t *w_buf = calloc(1, WRITE_BUF_LEN);
uint16_t *r_buf = calloc(1, READ_BUF_LEN);
size_t w_bytes = 0;
size_t r_bytes = 0;
uint32_t retry = 0;
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
w_buf[n] = n%100;
}
/* rx right mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x01[R] 0x03[R] ... */
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
#endif
/* Expected: 1 3 5 7 9 ... 97 99 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 1, 1, 2)) {
break;
}
}
if (retry >= RETEY_TIMES) {
printf("rx right mono test failed\n");
is_failed = true;
goto err;
}
printf("rx right mono test passed\n");
/* rx left mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[R] 0x02[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
}
#endif
/* Expected: 2 4 6 8 10 ... 96 98 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 1, 2, 2)) {
break;
}
}
if (retry >= RETEY_TIMES) {
printf("rx left mono test failed\n");
is_failed = true;
goto err;
}
printf("rx left mono test passed\n");
/* tx/rx stereo test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
* rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
rx_std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;
rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_BOTH;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
/* Expected: 1 2 3 4 ... 98 99 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 1, 1, 1)) {
break;
}
}
if (retry >= RETEY_TIMES) {
printf("tx/rx stereo test failed\n");
is_failed = true;
goto err;
}
printf("tx/rx stereo test passed\n");
#if !CONFIG_IDF_TARGET_ESP32 // the 16 bit channel sequence on ESP32 is incorrect
/* tx mono rx stereo test
* tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ...
* rx receive: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ... */
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
i2s_std_slot_config_t std_slot = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO);
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_slot));
TEST_ESP_OK(i2s_channel_reconfig_std_slot(rx_handle, &rx_std_cfg.slot_cfg));
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
TEST_ESP_OK(i2s_channel_write(tx_handle, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
/* Expected: 1 x 2 x 3 x ... 98 x 99 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 2, 1, 1)) {
break;
}
}
if (retry >= RETEY_TIMES) {
printf("tx mono rx stereo test failed\n");
is_failed = true;
goto err;
}
printf("tx mono rx stereo test passed\n");
#endif
err:
if (is_failed) {
for (int i = 0; i < READ_BUF_LEN / 2; i++) {
printf("%x ", r_buf[i]);
}
printf("\n");
}
free(w_buf);
free(r_buf);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
TEST_ASSERT_FALSE(is_failed);
}
TEST_CASE("I2S_memory_leak_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
/* The first operation will always take some memory */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
int memory_left = esp_get_free_heap_size();
printf("\r\nHeap size before: %d\n", memory_left);
for (int i = 0; i < 30; i++) {
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
std_cfg.slot_cfg.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT;
TEST_ESP_OK(i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg));
std_cfg.clk_cfg.sample_rate_hz = 44100;
TEST_ESP_OK(i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
TEST_ASSERT(memory_left == esp_get_free_heap_size());
}
printf("\r\nHeap size after: %"PRIu32"\n", esp_get_free_heap_size());
}
TEST_CASE("I2S_loopback_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_test_io_config(I2S_TEST_MODE_LOOPBACK);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_NUM > 1
TEST_CASE("I2S_master_write_slave_read_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_mst_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_slv_cfg));
i2s_test_io_config(I2S_TEST_MODE_MASTER_TO_SLAVE);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
TEST_CASE("I2S_master_read_slave_write_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t mst_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_chan_config_t slv_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_SLAVE);
i2s_std_config_t std_mst_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
i2s_std_config_t std_slv_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_SLAVE_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&mst_chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_new_channel(&slv_chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_slv_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_mst_cfg));
i2s_test_io_config(I2S_TEST_MODE_SLAVE_TO_MASTER);
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
i2s_read_write_test(tx_handle, rx_handle);
TEST_ESP_OK(i2s_channel_disable(tx_handle));
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif
/*------------------------------ Clock Test --------------------------------*/
#if SOC_PCNT_SUPPORTED
#define TEST_I2S_PERIOD_MS 100
static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_config_t* clk_cfg)
{
TEST_ASSERT_NOT_NULL(rx_chan);
TEST_ASSERT_NOT_NULL(clk_cfg);
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = MASTER_WS_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
// Reconfig GPIO signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[MASTER_WS_IO], PIN_FUNC_GPIO);
gpio_set_direction(MASTER_WS_IO, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
const uint32_t test_freq[] = {
8000, 10000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000, 88200,
96000, 128000,144000,196000};
int real_pulse = 0;
int case_cnt = sizeof(test_freq) / sizeof(uint32_t);
#if SOC_I2S_SUPPORTS_PLL_F96M
// 196000 Hz sample rate doesn't support on PLL_96M target
case_cnt = 15;
#endif
#if SOC_I2S_SUPPORTS_XTAL
// Can't support a very high sample rate while using XTAL as clock source
if (clk_cfg->clk_src == I2S_CLK_SRC_XTAL) {
case_cnt = 10;
}
#endif
#if CONFIG_IDF_ENV_FPGA
// Limit the test sample rate on FPGA platform due to the low frequency it supports.
case_cnt = 10;
#endif
for (int i = 0; i < case_cnt; i++) {
int expt_pulse = (int)((float)test_freq[i] * (TEST_I2S_PERIOD_MS / 1000.0));
clk_cfg->sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_chan, clk_cfg));
TEST_ESP_OK(i2s_channel_enable(rx_chan));
vTaskDelay(1); // Waiting for hardware totally started
// pcnt will count the pulse number on WS signal in 100ms
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
esp_rom_delay_us(100 * 1000);
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
printf("[%"PRIu32" Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ESP_OK(i2s_channel_disable(rx_chan));
// Check if the error between real pulse number and expected pulse number is within 1%
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
}
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
TEST_CASE("I2S_default_PLL_clock_test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
// ESP32-P4 has no PLL except XTAL
#if !CONFIG_IDF_TARGET_ESP32P4
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
#endif // CONFIG_IDF_TARGET_ESP32P4
#if SOC_I2S_SUPPORTS_XTAL
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_XTAL;
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
#endif
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#if SOC_I2S_SUPPORTS_APLL
TEST_CASE("I2S_APLL_clock_test", "[i2s]")
{
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
TEST_ESP_OK(i2s_del_channel(rx_handle));
}
#endif // SOC_I2S_SUPPORTS_APLL
#endif // SOC_PCNT_SUPPORTED
static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
{
int *cnt = (int *)user_ctx;
(*cnt)++;
return false;
}
TEST_CASE("I2S_package_lost_test", "[i2s]")
{
/* Steps of calculate appropriate parameters of I2S buffer:
* Known by user: sample_rate = 144k, data_bit_width = 32, slot_num = 2, polling_cycle = 10 ms
* 1. dma_buffer_size = dma_frame_num * slot_num * data_bit_width / 8 <= 4092
* dma_frame_num <= 511, dma_frame_num is as big as possible.
* interrupt_interval = dma_frame_num / sample_rate = 3.549 ms
* 2. dma_desc_num > polling_cycle / interrupt_interval = cell(2.818) = 3
* 3. recv_buffer_size > dma_desc_num * dma_buffer_size = 3 * 4092 = 12276 bytes */
#define TEST_RECV_BUF_LEN 12276
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 3;
chan_cfg.dma_frame_num = 511;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
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,
};
int count = 0;
TEST_ESP_OK(i2s_channel_register_event_callback(rx_handle, &cbs, &count));
uint32_t test_freq[] = {16000, 32000, 48000, 64000, 96000, 128000, 144000};
uint32_t test_num = sizeof(test_freq) / sizeof(uint32_t);
uint8_t *data = (uint8_t *)calloc(TEST_RECV_BUF_LEN, sizeof(uint8_t));
size_t bytes_read = 0;
int i;
for (i = 0; i < test_num; i++) {
printf("Testing %"PRIu32" Hz sample rate\n", test_freq[i]);
std_cfg.clk_cfg.sample_rate_hz = test_freq[i];
TEST_ESP_OK(i2s_channel_reconfig_std_clock(rx_handle, &std_cfg.clk_cfg));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
for (int j = 0; j < 10; j++) {
TEST_ESP_OK(i2s_channel_read(rx_handle, (void *)data, TEST_RECV_BUF_LEN, &bytes_read, portMAX_DELAY));
// To simulate 10ms delay caused by other statements like data process
vTaskDelay(1);
}
TEST_ESP_OK(i2s_channel_disable(rx_handle));
if (count > 0) {
printf("package lost detected at %"PRIu32" Hz\n", test_freq[i]);
goto finish;
}
}
finish:
TEST_ESP_OK(i2s_del_channel(rx_handle));
free(data);
// Test failed if package lost within 96000
TEST_ASSERT(i == test_num);
}

View File

@@ -1,121 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include "driver/i2s_std.h"
#include "esp_attr.h"
#include "soc/soc_caps.h"
#if CONFIG_IDF_TARGET_ESP32P4
#include "rom/cache.h"
#endif
#include "esp_private/i2s_platform.h"
#include "esp_private/spi_flash_os.h"
#include "../../test_inc/test_i2s.h"
#if CONFIG_I2S_ISR_IRAM_SAFE
#define GET_DMA_BUFFERS_BY_OFFSET(base_addr, offset) (uint8_t **)(*((uint32_t *)base_addr + offset))
static bool IRAM_ATTR test_i2s_tx_done_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
{
int *is_triggered = (int *)user_ctx;
if (*(uint8_t *)(event->data) != 0) {
*is_triggered = 1;
}
return false;
}
static void IRAM_ATTR test_i2s_iram_write(i2s_chan_handle_t tx_handle)
{
// Get the DMA buf pointer via the offset of 'bufs' field in i2s_channel_t struct
size_t offset = i2s_platform_get_dma_buffer_offset() / sizeof(uint32_t); // Get the offset and transfer to unit 'uint32_t' (i.e. 4 bytes)
uint8_t **dma_bufs = GET_DMA_BUFFERS_BY_OFFSET(tx_handle, offset);
// disable cache and non-iram ISR handlers
spi_flash_guard_get()->start();
// write data into dma buffer directly, the data in dma buffer will be sent automatically
for (int i = 0; i < 400; i++) {
dma_bufs[0][i] = i % 100 + 1;
}
#if CONFIG_IDF_TARGET_ESP32P4
// TODO: need to consider PSRAM if I2S driver supports EDMA
Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)dma_bufs[0], 400);
#endif
// enable cache and non-iram ISR handlers
spi_flash_guard_get()->end();
}
TEST_CASE("i2s_iram_interrupt_safe", "[i2s]")
{
i2s_chan_handle_t tx_chan = NULL;
i2s_chan_handle_t rx_chan = NULL;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = 2;
chan_cfg.dma_frame_num = 100;
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
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_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = MASTER_MCK_IO,
.bclk = MASTER_BCK_IO,
.ws = MASTER_WS_IO,
.dout = DATA_OUT_IO,
.din = DATA_OUT_IO, // same gpio to loopback
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
TEST_ESP_OK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
int is_triggered = 0;
i2s_event_callbacks_t cbs = {
.on_recv = NULL,
.on_recv_q_ovf = NULL,
.on_sent = test_i2s_tx_done_callback,
.on_send_q_ovf = NULL,
};
TEST_ESP_OK(i2s_channel_register_event_callback(tx_chan, &cbs, &is_triggered));
TEST_ESP_OK(i2s_channel_enable(tx_chan));
TEST_ESP_OK(i2s_channel_enable(rx_chan));
uint8_t *recv_buf = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(recv_buf != NULL);
size_t r_bytes;
int i = 0;
test_i2s_iram_write(tx_chan);
for (int retry = 0; retry < 3; retry++) {
i2s_channel_read(rx_chan, recv_buf, 2000, &r_bytes, pdMS_TO_TICKS(1000));
for (i = 0; i < 2000 - 100; i++) {
if (recv_buf[i] == 1 && recv_buf[i + 1] == 2) {
goto finish;
}
}
}
finish:
TEST_ESP_OK(i2s_channel_disable(tx_chan));
TEST_ESP_OK(i2s_channel_disable(rx_chan));
TEST_ESP_OK(i2s_del_channel(tx_chan));
TEST_ESP_OK(i2s_del_channel(rx_chan));
TEST_ASSERT(i < (2000 - 100));
for (int j = 1; j <= 100; j++) {
TEST_ASSERT_EQUAL_UINT8(recv_buf[i++], j);
}
TEST_ASSERT(is_triggered);
free(recv_buf);
}
#endif // CONFIG_PCNT_ISR_IRAM_SAFE

View File

@@ -1,24 +0,0 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32s3
@pytest.mark.esp32h2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'iram_safe',
'release',
],
indirect=True,
)
def test_i2s(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -1,8 +0,0 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_I2S_ISR_IRAM_SAFE=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
# place non-ISR FreeRTOS functions in Flash
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y

View File

@@ -1,5 +0,0 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -1,2 +0,0 @@
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT_EN=n

View File

@@ -1,13 +0,0 @@
# 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)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS
"$ENV{IDF_PATH}/tools/unit-test-app/components"
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_multi_dev_test)

View File

@@ -1,3 +0,0 @@
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -1,5 +0,0 @@
idf_component_register(SRCS "test_app_main.c" "test_i2s_multi_dev.c"
PRIV_REQUIRES unity driver test_utils
INCLUDE_DIRS "."
WHOLE_ARCHIVE
)

View File

@@ -1,41 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
unity_run_menu();
}

View File

@@ -1,394 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "test_utils.h"
#include "driver/gpio.h"
#include "driver/i2s_tdm.h"
#include "driver/i2s_std.h"
#include "soc/i2s_struct.h"
static const char *TAG = "i2s_multi_dev_test";
#define TEST_I2S_FRAME_SIZE (128) // Frame numbers in every writing / reading
#define TEST_I2S_ARRAY_LENGTH (1024) // The loop data length for verification
#define TEST_I2S_MAX_DATA (256) // The maximum data value in the data buffer
#define TEST_I2S_MAX_FAIL_CNT (3) // Max broken packet count
#define TEST_I2S_FRAME_TIMEOUT_SEC (10.0f) // Timeout seconds of waiting for a correct frame
#define TEST_I2S_NUM (I2S_NUM_0) // ESP32-C3 has only I2S0
#define TEST_I2S_MCK_IO (GPIO_NUM_0)
#define TEST_I2S_BCK_IO (GPIO_NUM_4)
#define TEST_I2S_WS_IO (GPIO_NUM_5)
#if CONFIG_IDF_TARGET_ESP32H2
#define TEST_I2S_DO_IO (GPIO_NUM_2)
#define TEST_I2S_DI_IO (GPIO_NUM_3) // DI and DO gpio will be reversed on slave runner
#else
#define TEST_I2S_DO_IO (GPIO_NUM_6)
#define TEST_I2S_DI_IO (GPIO_NUM_7) // DI and DO gpio will be reversed on slave runner
#endif // CONFIG_IDF_TARGET_ESP32H2
typedef struct {
uint32_t *buffer;
uint32_t buffer_size;
} test_i2s_tdm_write_buffer_t;
#define TEST_I2S_DEFAULT_GPIO(mclk_pin, is_master) { \
.mclk = mclk_pin, \
.bclk = TEST_I2S_BCK_IO, \
.ws = TEST_I2S_WS_IO, \
.dout = is_master ? TEST_I2S_DO_IO : TEST_I2S_DI_IO, \
.din = is_master ? TEST_I2S_DI_IO : TEST_I2S_DO_IO, \
}
static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask)
{
i2s_chan_handle_t i2s_tdm_tx_handle = NULL;
i2s_chan_handle_t i2s_tdm_rx_handle = NULL;
/* Create I2S tx and rx channels */
i2s_chan_config_t i2s_channel_config = {
.id = TEST_I2S_NUM,
.role = I2S_ROLE_MASTER,
.dma_desc_num = 4,
.dma_frame_num = TEST_I2S_FRAME_SIZE,
.auto_clear = false
};
TEST_ESP_OK(i2s_new_channel(&i2s_channel_config, &i2s_tdm_tx_handle, &i2s_tdm_rx_handle));
/* Configure channels to TDM mode */
i2s_tdm_config_t i2s_tdm_config = {
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(sample_rate),
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(bit_width, I2S_SLOT_MODE_STEREO, slot_mask),
.gpio_cfg = TEST_I2S_DEFAULT_GPIO(GPIO_NUM_NC, true),
};
i2s_tdm_config.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_tx_handle, &i2s_tdm_config));
TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_rx_handle, &i2s_tdm_config));
uint32_t channel_count = 32 - __builtin_clz(slot_mask);
size_t buf_size = channel_count * TEST_I2S_FRAME_SIZE * (bit_width / 8);
/* Allocate I2S rx buffer */
ESP_LOGI(TAG, "Allocating I2S TDM master rx buffer, size=%u", buf_size);
uint32_t *rx_buffer = malloc(buf_size);
TEST_ASSERT(rx_buffer);
/* Allocate I2S tx buffer */
ESP_LOGI(TAG, "Allocating I2S TDM master tx buffer, size=%u", buf_size);
uint32_t *tx_buffer = malloc(buf_size);
TEST_ASSERT(tx_buffer);
/* Fill in the tx buffer */
for (uint32_t i = 0, data_cnt = 0; i < buf_size / sizeof(uint32_t); i ++) {
tx_buffer[i] = data_cnt;
data_cnt++;
data_cnt %= TEST_I2S_MAX_DATA;
}
size_t w_bytes = buf_size;
while (w_bytes != 0) {
ESP_ERROR_CHECK(i2s_channel_preload_data(i2s_tdm_tx_handle, tx_buffer, buf_size, &w_bytes));
}
unity_wait_for_signal("Slave Ready");
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle));
vTaskDelay(pdMS_TO_TICKS(100)); // Waiting the data be steady on line
unity_send_signal("Master Ready");
/* Slave is ready, start the writing task */
ESP_LOGI(TAG, "I2S TDM master receive & send start");
esp_err_t read_ret = ESP_OK;
uint32_t count = 1;
uint8_t fail_cnt = 0;
size_t bytes_read = 0;
for (fail_cnt = 0; fail_cnt < TEST_I2S_MAX_FAIL_CNT && count < TEST_I2S_MAX_DATA; fail_cnt++) {
if (i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, buf_size, &bytes_read, 1000) != ESP_OK) {
continue;
}
for (int i = 0; i < buf_size && count < TEST_I2S_MAX_DATA; i++) {
if (rx_buffer[i] == count) {
count++;
} else if (count != 1) {
ESP_LOGE(TAG, "Failed at index: %d real: %" PRIu32 " expect: %" PRIu32, i, rx_buffer[i], count);
count = 1;
}
}
}
ESP_LOGI(TAG, "I2S TDM master stop");
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle));
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle));
free(rx_buffer);
free(tx_buffer);
TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle));
ESP_LOGI(TAG, "I2S TDM master resources freed");
TEST_ASSERT_TRUE_MESSAGE(read_ret == ESP_OK, "Master read timeout ");
TEST_ASSERT_TRUE_MESSAGE(fail_cnt < TEST_I2S_MAX_FAIL_CNT, "Exceed retry times ");
TEST_ASSERT_EQUAL_UINT32(TEST_I2S_MAX_DATA, count);
}
static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask)
{
i2s_chan_handle_t i2s_tdm_tx_handle = NULL;
i2s_chan_handle_t i2s_tdm_rx_handle = NULL;
/* Create I2S tx and rx channels */
i2s_chan_config_t i2s_channel_config = {
.id = TEST_I2S_NUM,
.role = I2S_ROLE_SLAVE,
.dma_desc_num = 4,
.dma_frame_num = TEST_I2S_FRAME_SIZE,
.auto_clear = false
};
TEST_ESP_OK(i2s_new_channel(&i2s_channel_config, &i2s_tdm_tx_handle, &i2s_tdm_rx_handle));
/* Configure channels to TDM mode */
i2s_tdm_config_t i2s_tdm_config = {
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(sample_rate),
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(bit_width, I2S_SLOT_MODE_STEREO, slot_mask),
.gpio_cfg = TEST_I2S_DEFAULT_GPIO(GPIO_NUM_NC, false),
};
if (sample_rate >= 96000) {
i2s_tdm_config.clk_cfg.bclk_div = 12;
}
TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_tx_handle, &i2s_tdm_config));
TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_rx_handle, &i2s_tdm_config));
/* Allocate I2S rx buffer */
uint32_t channel_count = 32 - __builtin_clz(slot_mask);
uint32_t buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (bit_width / 8);
ESP_LOGI(TAG, "Allocating I2S TDM slave buffer, size=%"PRIu32, buffer_size);
uint32_t *echo_buffer = malloc(buffer_size);
TEST_ASSERT(echo_buffer);
unity_send_signal("Slave Ready");
unity_wait_for_signal("Master Ready");
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle));
ESP_LOGI(TAG, "I2S TDM slave receive & send start");
size_t bytes_read = 0, bytes_written = 0;
/* Loop until reading or writing failed, which indicates the master has finished and deleted the I2S peripheral */
while (true) {
if (i2s_channel_read(i2s_tdm_rx_handle, echo_buffer, buffer_size, &bytes_read, 500) != ESP_OK) {
break;
}
if (i2s_channel_write(i2s_tdm_tx_handle, echo_buffer, buffer_size, &bytes_written, 500) != ESP_OK) {
break;
}
}
ESP_LOGI(TAG, "I2S TDM slave receive stop");
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle));
free(echo_buffer);
TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle));
ESP_LOGI(TAG, "I2S TDM slave resources freed");
}
static void test_i2s_tdm_master_48k_32bits_4slots(void)
{
test_i2s_tdm_master(48000, I2S_DATA_BIT_WIDTH_32BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
static void test_i2s_tdm_slave_48k_32bits_4slots(void)
{
test_i2s_tdm_slave(48000, I2S_DATA_BIT_WIDTH_32BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_48k_32bits_4slots", "[I2S_TDM]",
test_i2s_tdm_master_48k_32bits_4slots, test_i2s_tdm_slave_48k_32bits_4slots);
static void test_i2s_tdm_master_48k_16bits_4slots(void)
{
test_i2s_tdm_master(48000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
static void test_i2s_tdm_slave_48k_16bits_4slots(void)
{
test_i2s_tdm_slave(48000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_48k_16bits_4slots", "[I2S_TDM]",
test_i2s_tdm_master_48k_16bits_4slots, test_i2s_tdm_slave_48k_16bits_4slots);
static void test_i2s_tdm_master_48k_8bits_4slots(void)
{
test_i2s_tdm_master(48000, I2S_DATA_BIT_WIDTH_8BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
static void test_i2s_tdm_slave_48k_8bits_4slots(void)
{
test_i2s_tdm_slave(48000, I2S_DATA_BIT_WIDTH_8BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_48k_8bits_4slots", "[I2S_TDM]",
test_i2s_tdm_master_48k_8bits_4slots, test_i2s_tdm_slave_48k_8bits_4slots);
/* The I2S source clock can only reach 96Mhz on ESP32H2,
which can't satisfy the following configurations in slave mode */
#if !CONFIG_IDF_TARGET_ESP32H2
static void test_i2s_tdm_master_48k_16bits_8slots(void)
{
test_i2s_tdm_master(48000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 |
I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7);
}
static void test_i2s_tdm_slave_48k_16bits_8slots(void)
{
test_i2s_tdm_slave(48000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 |
I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_48k_16bits_8slots", "[I2S_TDM]",
test_i2s_tdm_master_48k_16bits_8slots, test_i2s_tdm_slave_48k_16bits_8slots);
static void test_i2s_tdm_master_96k_16bits_4slots(void)
{
test_i2s_tdm_master(96000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
static void test_i2s_tdm_slave_96k_16bits_4slots(void)
{
test_i2s_tdm_slave(96000, I2S_DATA_BIT_WIDTH_16BIT, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_96k_16bits_4slots", "[I2S_TDM]",
test_i2s_tdm_master_96k_16bits_4slots, test_i2s_tdm_slave_96k_16bits_4slots);
#endif // !CONFIG_IDF_TARGET_ESP32H2
static void test_i2s_external_clk_src(bool is_master, bool is_external)
{
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE);
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
.gpio_cfg = TEST_I2S_DEFAULT_GPIO(TEST_I2S_MCK_IO, is_master),
};
if (is_external) {
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_EXTERNAL;
std_cfg.clk_cfg.ext_clk_freq_hz = 11289600;
}
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
if (is_master && !is_external) {
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO);
memcpy(&std_cfg.slot_cfg, &slot_cfg, sizeof(i2s_std_slot_config_t));
}
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
if (is_master) {
if (!is_external) {
// Delay bclk to get compensate the data delay
I2S0.rx_timing.rx_bck_out_dm = 1;
}
uint8_t mst_tx_data[4] = {0x12, 0x34, 0x56, 0x78};
size_t w_bytes = 4;
while (w_bytes == 4) {
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, mst_tx_data, 4, &w_bytes));
}
}
uint8_t *recv_buff = (uint8_t *)calloc(1, TEST_I2S_ARRAY_LENGTH);
TEST_ASSERT(recv_buff);
unity_send_signal(is_master ? "Master Ready" : "Slave Ready");
unity_wait_for_signal(is_master ? "Slave Ready" : "Master Ready");
if (is_external) {
unity_wait_for_signal("External Clock Ready");
}
TEST_ESP_OK(i2s_channel_enable(tx_handle));
TEST_ESP_OK(i2s_channel_enable(rx_handle));
if (!is_external) {
unity_send_signal("External Clock Ready");
}
bool is_success = false;
size_t bytes_read = 0;
if (is_master) {
if (is_external) {
unity_send_signal("Master Data Ready");
}
// Wait the Slave data ready on line
unity_wait_for_signal("Slave Data Ready");
vTaskDelay(pdMS_TO_TICKS(100)); // Wait another 100 ms for the data to be steady in DMA buffer
TEST_ESP_OK(i2s_channel_read(rx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000));
} else {
if (!is_external) {
unity_wait_for_signal("Master Data Ready");
}
TEST_ESP_OK(i2s_channel_read(rx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000));
// Fill the DMA buffer
for (int i = 0; i < 6; i++) {
TEST_ESP_OK(i2s_channel_write(tx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000));
}
// Send the signal indicates the data have been ready on line
unity_send_signal("Slave Data Ready");
}
// Check the data
for (int i = 0; i < TEST_I2S_ARRAY_LENGTH; i++) {
if (recv_buff[i] == 0x12 && recv_buff[i + 1] == 0x34 &&
recv_buff[i + 2] == 0x56 && recv_buff[i + 3] == 0x78) {
is_success = true;
break;
}
printf("%x ", recv_buff[i]);
}
printf("\n");
if (is_master) {
unity_send_signal("Master Finished");
} else {
unity_wait_for_signal("Master Finished");
}
// Disable and free the resources
TEST_ESP_OK(i2s_channel_disable(rx_handle));
TEST_ESP_OK(i2s_channel_disable(tx_handle));
free(recv_buff);
TEST_ESP_OK(i2s_del_channel(rx_handle));
TEST_ESP_OK(i2s_del_channel(tx_handle));
// Assert whether the test success
TEST_ASSERT(is_success);
}
static void test_i2s_master_clock_out(void)
{
test_i2s_external_clk_src(true, false);
}
static void test_i2s_slave_clock_in(void)
{
test_i2s_external_clk_src(false, true);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_external_clock_master_output_slave_input", "[I2S]",
test_i2s_master_clock_out, test_i2s_slave_clock_in);
static void test_i2s_master_clock_in(void)
{
test_i2s_external_clk_src(true, true);
}
static void test_i2s_slave_clock_out(void)
{
test_i2s_external_clk_src(false, false);
}
TEST_CASE_MULTIPLE_DEVICES("I2S_external_clock_master_input_slave_output", "[I2S]",
test_i2s_master_clock_in, test_i2s_slave_clock_out);

View File

@@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32h2
@pytest.mark.generic_multi_device
@pytest.mark.parametrize('count', [
2,
], indirect=True)
def test_i2s_multi_dev(case_tester) -> None: # type: ignore
case_tester.run_all_multi_dev_cases(reset=True, timeout=30)

View File

@@ -1,3 +0,0 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_I2S_ENABLE_DEBUG_LOG=y

View File

@@ -155,14 +155,14 @@ static void example_i2s_init(void)
};
//install and start i2s driver
TEST_ESP_OK( i2s_driver_install(i2s_num, &i2s_config, 0, NULL) );
TEST_ESP_OK(i2s_driver_install(i2s_num, &i2s_config, 0, NULL));
//init ADC pad
TEST_ESP_OK( i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL) );
TEST_ESP_OK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL));
}
static void example_i2s_deinit(void)
{
TEST_ESP_OK( i2s_driver_uninstall(EXAMPLE_I2S_NUM) );
TEST_ESP_OK(i2s_driver_uninstall(EXAMPLE_I2S_NUM));
}
/**
@@ -172,9 +172,11 @@ static void example_disp_buf(uint8_t *buf, int length)
{
printf("\n======");
for (int i = 0; i < length; i += 2) {
uint16_t data = ((uint16_t)buf[i+1] << 8) | (uint16_t)buf[i];
uint16_t data = ((uint16_t)buf[i + 1] << 8) | (uint16_t)buf[i];
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&data;
if ((i) % 16 == 0) printf("\n");
if ((i) % 16 == 0) {
printf("\n");
}
printf("[%d_%d] ", p->type1.channel, p->type1.data);
}
printf("\n======\n");
@@ -183,17 +185,17 @@ static void example_disp_buf(uint8_t *buf, int length)
static esp_err_t adc_dma_data_check(uint8_t *buf, int length, int ideal_level)
{
for (int i = 0; i < length; i += 2) {
uint16_t data = ((uint16_t)buf[i+1] << 8) | (uint16_t)buf[i];
uint16_t data = ((uint16_t)buf[i + 1] << 8) | (uint16_t)buf[i];
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&data;
if (p->type1.channel != I2S_ADC_CHANNEL) {
TEST_FAIL_MESSAGE("I2S-DMA data channel error!");
}
if (ideal_level == 1) { // high level 3.3v
TEST_ASSERT_EQUAL( 0xFFF, p->type1.data );
TEST_ASSERT_EQUAL(0xFFF, p->type1.data);
} else if (ideal_level == 0) { // low level 0v
TEST_ASSERT_LESS_THAN( 10, p->type1.data );
TEST_ASSERT_LESS_THAN(10, p->type1.data);
} else if (ideal_level == 2) { // middle level 1.4v
TEST_ASSERT_INT_WITHIN( 128, 1586, p->type1.data );
TEST_ASSERT_INT_WITHIN(128, 1586, p->type1.data);
} else if (ideal_level == 3) { // normal level
} else { // no check
}
@@ -209,7 +211,7 @@ static void adc_dma_read(uint8_t *buf, int length)
vTaskDelay(pdTICKS_TO_MS(100));
while (flash_wr_size < length) {
//read data from I2S bus, in this case, from ADC.
TEST_ESP_OK( i2s_read(EXAMPLE_I2S_NUM, (void *) buf + flash_wr_size, length - flash_wr_size, &bytes_read, portMAX_DELAY) );
TEST_ESP_OK(i2s_read(EXAMPLE_I2S_NUM, (void *) buf + flash_wr_size, length - flash_wr_size, &bytes_read, portMAX_DELAY));
flash_wr_size += bytes_read;
example_disp_buf((uint8_t *) buf, 128);
}
@@ -221,7 +223,7 @@ TEST_CASE("ADC_DMA_read", "[adc dma]")
char *i2s_read_buff = (char *) calloc(i2s_read_len, sizeof(char));
example_i2s_init();
TEST_ESP_OK( i2s_adc_enable(EXAMPLE_I2S_NUM) );
TEST_ESP_OK(i2s_adc_enable(EXAMPLE_I2S_NUM));
adc_fake_tie_low(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
adc_dma_read((uint8_t *)i2s_read_buff, i2s_read_len);
@@ -237,7 +239,7 @@ TEST_CASE("ADC_DMA_read", "[adc dma]")
adc_io_normal(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
TEST_ESP_OK( i2s_adc_disable(EXAMPLE_I2S_NUM) );
TEST_ESP_OK(i2s_adc_disable(EXAMPLE_I2S_NUM));
if (i2s_read_buff) {
free(i2s_read_buff);
i2s_read_buff = NULL;

View File

@@ -62,15 +62,15 @@ static void example_i2s_init(void)
.use_apll = 0,
};
//install and start i2s driver
TEST_ESP_OK( i2s_driver_install(i2s_num, &i2s_config, 0, NULL) );
TEST_ESP_OK(i2s_driver_install(i2s_num, &i2s_config, 0, NULL));
//init DAC pad
TEST_ESP_OK( i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN) );
TEST_ESP_OK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
}
static void example_i2s_deinit(void)
{
TEST_ESP_OK( i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE) );
TEST_ESP_OK( i2s_driver_uninstall(EXAMPLE_I2S_NUM) );
TEST_ESP_OK(i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE));
TEST_ESP_OK(i2s_driver_uninstall(EXAMPLE_I2S_NUM));
}
/**
@@ -78,7 +78,7 @@ static void example_i2s_deinit(void)
*/
static void example_set_file_play_mode(void)
{
TEST_ESP_OK( i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1) );
TEST_ESP_OK(i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1));
}
/**
@@ -125,7 +125,7 @@ static void example_disp_buf(uint8_t *buf, int length)
*/
static void example_reset_play_mode(void)
{
TEST_ESP_OK( i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM) );
TEST_ESP_OK(i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM));
}
TEST_CASE("DAC_DMA_output", "[dac]")

View File

@@ -223,8 +223,8 @@ static bool whether_contains_exapected_data(uint16_t *src, uint32_t src_len, uin
if (src[i] == val) {
if (val == start_val && i < src_len - 8) {
printf("start index: %d ---> \n%d %d %d %d %d %d %d %d\n", i,
src[i], src[i+1], src[i+2], src[i+3],
src[i+4], src[i+5], src[i+6], src[i+7]);
src[i], src[i + 1], src[i + 2], src[i + 3],
src[i + 4], src[i + 5], src[i + 6], src[i + 7]);
}
index_step = src_step;
val += val_step;
@@ -299,7 +299,7 @@ TEST_CASE("I2S_legacy_mono_stereo_loopback_test", "[i2s_legacy]")
uint32_t retry = 0;
bool is_failed = false;
for (int n = 0; n < WRITE_BUF_LEN / 2; n++) {
w_buf[n] = n%100;
w_buf[n] = n % 100;
}
/* rx right mono test
* tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ...
@@ -307,14 +307,14 @@ TEST_CASE("I2S_legacy_mono_stereo_loopback_test", "[i2s_legacy]")
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
r_buf[n] = r_buf[n + 1];
r_buf[n + 1] = temp;
}
#endif
#endif
/* Expected: 1 3 5 7 9 ... 97 99 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 1, 1, 2)) {
break;
@@ -385,14 +385,14 @@ TEST_CASE("I2S_legacy_mono_stereo_loopback_test", "[i2s_legacy]")
TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY));
for (retry = 0; retry < RETEY_TIMES; retry++) {
TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY));
#if CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32
/* The data of tx/rx channels are flipped on ESP32 */
for (int n = 0; n < READ_BUF_LEN / 2; n += 2) {
int16_t temp = r_buf[n];
r_buf[n] = r_buf[n+1];
r_buf[n+1] = temp;
r_buf[n] = r_buf[n + 1];
r_buf[n + 1] = temp;
}
#endif
#endif
/* Expected: 2 4 6 8 10 ... 96 98 */
if (whether_contains_exapected_data(r_buf, READ_BUF_LEN / 2, 1, 2, 2)) {
break;
@@ -877,7 +877,8 @@ static void i2s_test_common_sample_rate(i2s_port_t id)
const uint32_t test_freq[] = {
8000, 10000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000, 88200,
96000, 128000, 144000,196000};
96000, 128000, 144000, 196000
};
int real_pulse = 0;
#if CONFIG_IDF_ENV_FPGA
// Limit the test sample rate on FPGA platform due to the low frequency it supports.