mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
refactor(i2s): make i2s driver as component
This commit is contained in:
@@ -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()
|
@@ -1,2 +0,0 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
@@ -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)
|
@@ -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();
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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
|
@@ -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()
|
@@ -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
|
@@ -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
|
@@ -1,2 +0,0 @@
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
@@ -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)
|
@@ -1,3 +0,0 @@
|
||||
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
@@ -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
|
||||
)
|
@@ -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();
|
||||
}
|
@@ -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);
|
@@ -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)
|
@@ -1,3 +0,0 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_I2S_ENABLE_DEBUG_LOG=y
|
@@ -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;
|
||||
|
@@ -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]")
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user