Explorar o código

ci(i2s): add external clock input multi_dev test cases

laokaiyao %!s(int64=2) %!d(string=hai) anos
pai
achega
22bb5729a4

+ 2 - 2
components/driver/.build-test-rules.yml

@@ -24,9 +24,9 @@ components/driver/test_apps/i2s_test_apps/i2s:
   disable:
     - if: SOC_I2S_SUPPORTED != 1
 
-components/driver/test_apps/i2s_test_apps/i2s_tdm:
+components/driver/test_apps/i2s_test_apps/i2s_multi_dev:
   disable:
-    - if: SOC_I2S_SUPPORTS_TDM != 1
+    - if: SOC_I2S_HW_VERSION_2 != 1
 
 components/driver/test_apps/i2s_test_apps/legacy_i2s_adc_dac:
   disable:

+ 2 - 2
components/driver/i2s/include/driver/i2s_std.h

@@ -238,11 +238,11 @@ typedef struct {
     /* General fields */
     uint32_t                sample_rate_hz;     /*!< I2S sample rate */
     i2s_clock_src_t         clk_src;            /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources.
-                                                 *   selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock inputted via MCLK pin,
+                                                 *   selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock input via MCLK pin,
                                                  */
 #if SOC_I2S_HW_VERSION_2
     uint32_t                ext_clk_freq_hz;    /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored,
-                                                 *   Please make sure the frequency inputted is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2'
+                                                 *   Please make sure the frequency input is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2'
                                                  */
 #endif
     i2s_mclk_multiple_t     mclk_multiple;      /*!< The multiple of mclk to the sample rate

+ 1 - 1
components/driver/test_apps/i2s_test_apps/i2s_tdm/CMakeLists.txt → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/CMakeLists.txt

@@ -10,4 +10,4 @@ set(EXTRA_COMPONENT_DIRS
 )
 
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-project(i2s_tdm_full_duplex_test)
+project(i2s_multi_dev_test)

+ 0 - 0
components/driver/test_apps/i2s_test_apps/i2s_tdm/README.md → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/README.md


+ 1 - 1
components/driver/test_apps/i2s_test_apps/i2s_tdm/main/CMakeLists.txt → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/CMakeLists.txt

@@ -1,4 +1,4 @@
-idf_component_register(SRCS "test_app_main.c" "test_i2s_tdm_full_duplex.c"
+idf_component_register(SRCS "test_app_main.c" "test_i2s_multi_dev.c"
                        PRIV_REQUIRES unity driver test_utils
                        INCLUDE_DIRS "."
                        WHOLE_ARCHIVE

+ 0 - 0
components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_app_main.c → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_app_main.c


+ 179 - 121
components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_i2s_tdm_full_duplex.c → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_i2s_multi_dev.c

@@ -5,6 +5,7 @@
  */
 #include <stdio.h>
 #include <string.h>
+#include <inttypes.h>
 #include "sdkconfig.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/queue.h"
@@ -15,15 +16,19 @@
 #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_tdm_full_duplex_test";
+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
@@ -34,53 +39,17 @@ static const char *TAG = "i2s_tdm_full_duplex_test";
 #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 {
-    TaskHandle_t maintask_handle;
-    QueueHandle_t tx_queue;
-    i2s_chan_handle_t tx_channel_handle;
-    i2s_data_bit_width_t tx_data_bit_width;
-    i2s_tdm_slot_mask_t tdm_slot_mask;
-} test_i2s_tdm_write_task_args_t;
-
 typedef struct {
     uint32_t *buffer;
     uint32_t buffer_size;
 } test_i2s_tdm_write_buffer_t;
 
-static void test_i2s_tdm_master_write_task(void *args)
-{
-    test_i2s_tdm_write_task_args_t *task_args = (test_i2s_tdm_write_task_args_t*)args;
-
-    /* Allocate I2S tx buffer */
-    uint32_t channel_count = 32 - __builtin_clz(task_args->tdm_slot_mask);
-    uint32_t tx_buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (task_args->tx_data_bit_width / 8);
-    ESP_LOGI(TAG, "Allocating I2S TDM master tx buffer, size=%ld", tx_buffer_size);
-    uint32_t *tx_buffer = malloc(tx_buffer_size);
-    TEST_ASSERT(tx_buffer);
-
-    uint32_t data_cnt = 0;
-    size_t bytes_written = 0;
-
-    /* Block here waiting for the main thread receiving Slave Ready signals */
-    xTaskNotifyWait(0, ULONG_MAX, NULL, portMAX_DELAY);
-
-    ESP_LOGI(TAG, "I2S TDM master send start");
-    while (xTaskNotifyWait(0, ULONG_MAX, NULL, 0) == pdFALSE) { // if main task sends terminate signal, exit the loop
-        /* Fill in the tx buffer */
-        for (uint32_t i = 0; i < tx_buffer_size / sizeof(uint32_t); i ++) {
-            tx_buffer[i] = data_cnt;
-            data_cnt++;
-            data_cnt %= TEST_I2S_ARRAY_LENGTH;
-        }
-        TEST_ESP_OK(i2s_channel_write(task_args->tx_channel_handle, tx_buffer, tx_buffer_size,
-                                      &bytes_written, 1000));
-    }
-
-    ESP_LOGI(TAG, "Freeing I2S TDM master tx buffer");
-    free(tx_buffer);
-
-    xTaskNotifyGive(task_args->maintask_handle); // notify main task that cleanup is done
-    vTaskSuspend(NULL); // wait to be deleted
+#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)
@@ -102,93 +71,70 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w
     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 = {
-            .mclk = GPIO_NUM_NC,
-            .bclk = TEST_I2S_BCK_IO,
-            .ws = TEST_I2S_WS_IO,
-            .dout = TEST_I2S_DO_IO,
-            .din = TEST_I2S_DI_IO
-        },
+        .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));
 
-    /* Create TDM write task */
-    TaskHandle_t subtask_handle = NULL;
-    /* Make the variable static in case it become invalid in the write task */
-    static test_i2s_tdm_write_task_args_t task_args;
-    task_args.tx_channel_handle = i2s_tdm_tx_handle;
-    task_args.maintask_handle = xTaskGetCurrentTaskHandle();
-    task_args.tx_data_bit_width = bit_width;
-    task_args.tdm_slot_mask = slot_mask;
-    xTaskCreate(test_i2s_tdm_master_write_task, "I2S TDM Write Task", 4096, &task_args, 5, &subtask_handle);
-
-    /* Allocate I2S rx buffer */
     uint32_t channel_count = 32 - __builtin_clz(slot_mask);
-    uint32_t rx_buffer_size = channel_count * TEST_I2S_FRAME_SIZE * (bit_width / 8);
-    ESP_LOGI(TAG, "Allocating I2S TDM master rx buffer, size=%"PRIu32, rx_buffer_size);
-    uint32_t *rx_buffer = malloc(rx_buffer_size);
+    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));
+    }
 
-    uint32_t count = 1;
-    bool is_start = false;
-    uint8_t fail_cnt = 0;
-    size_t bytes_read = 0;
-    float time = 0;
+    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");
-    unity_wait_for_signal("Slave Ready");
 
     /* Slave is ready, start the writing task */
     ESP_LOGI(TAG, "I2S TDM master receive & send start");
     esp_err_t read_ret = ESP_OK;
-    xTaskNotifyGive(subtask_handle);
-    while (count < TEST_I2S_ARRAY_LENGTH && fail_cnt < TEST_I2S_MAX_FAIL_CNT && time < TEST_I2S_FRAME_TIMEOUT_SEC) {
-        read_ret = i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, &bytes_read, 1000);
-        if (read_ret != ESP_OK) {
-            break;
+    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 < rx_buffer_size / sizeof(uint32_t); i++) {
+        for (int i = 0; i < buf_size && count < TEST_I2S_MAX_DATA; i++) {
             if (rx_buffer[i] == count) {
                 count++;
-                if (count >= TEST_I2S_ARRAY_LENGTH) {
-                    break;
-                }
-                if (!is_start) {
-                    is_start = true;
-                }
-            } else if (is_start) {
+            } else if (count != 1) {
                 ESP_LOGE(TAG, "Failed at index: %d real: %" PRIu32 " expect: %" PRIu32, i, rx_buffer[i], count);
-                is_start = false;
                 count = 1;
-                fail_cnt++;
             }
         }
-        time += (float)TEST_I2S_MAX_FAIL_CNT / (float)sample_rate;
     }
-    unity_send_signal("Master Finished");
 
-    ESP_LOGI(TAG, "Send signal to terminate subtask");
-    xTaskNotifyGive(subtask_handle); // notify subtask to exit
-    xTaskNotifyWait(0, ULONG_MAX, NULL, portMAX_DELAY); // wait subtask to do some cleanups
-    ESP_LOGI(TAG, "Deleting subtask");
-    unity_utils_task_delete(subtask_handle); // delete subtask
-
-    ESP_LOGI(TAG, "I2S TDM master send stop");
+    ESP_LOGI(TAG, "I2S TDM master stop");
     TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle));
-    ESP_LOGI(TAG, "I2S TDM master receive stop");
     TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle));
-
-    ESP_LOGI(TAG, "Freeing I2S TDM master rx buffer");
     free(rx_buffer);
-    ESP_LOGI(TAG, "Deleting i2s tx and rx channels");
+    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, "Broken data received ");
-    TEST_ASSERT_TRUE_MESSAGE(time < TEST_I2S_FRAME_TIMEOUT_SEC, "Waiting for valid data 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)
@@ -210,13 +156,7 @@ static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_wi
     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 = {
-            .mclk = GPIO_NUM_NC,
-            .bclk = TEST_I2S_BCK_IO,
-            .ws = TEST_I2S_WS_IO,
-            .dout = TEST_I2S_DI_IO,
-            .din = TEST_I2S_DO_IO // on slave, swap DI and DO pin
-        },
+        .gpio_cfg = TEST_I2S_DEFAULT_GPIO(GPIO_NUM_NC, false),
     };
     if (sample_rate >= 96000) {
         i2s_tdm_config.clk_cfg.bclk_div = 12;
@@ -226,39 +166,35 @@ static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_wi
 
     /* Allocate I2S rx buffer */
     uint32_t channel_count = 32 - __builtin_clz(slot_mask);
-    uint32_t rx_buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (bit_width / 8);
-    ESP_LOGI(TAG, "Allocating I2S TDM slave buffer, size=%ld", rx_buffer_size);
-    uint32_t *rx_buffer = malloc(rx_buffer_size);
-    TEST_ASSERT(rx_buffer);
+    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);
 
-    TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle));
-    TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle));
     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, rx_buffer, rx_buffer_size, &bytes_read, 500) != ESP_OK) {
+        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, rx_buffer, rx_buffer_size, &bytes_written, 500) != ESP_OK) {
+        if (i2s_channel_write(i2s_tdm_tx_handle, echo_buffer, buffer_size, &bytes_written, 500) != ESP_OK) {
             break;
         }
     }
-    unity_wait_for_signal("Master Finished");
 
     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));
-
-    ESP_LOGI(TAG, "Freeing I2S TDM slave buffer");
-    free(rx_buffer);
-
-    ESP_LOGI(TAG, "Deleting i2s tx and rx channels");
+    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)
@@ -334,3 +270,125 @@ static void test_i2s_tdm_slave_96k_16bits_4slots(void)
 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);

+ 2 - 2
components/driver/test_apps/i2s_test_apps/i2s_tdm/pytest_i2s_tdm_full_duplex.py → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/pytest_i2s_multi_dev.py

@@ -11,5 +11,5 @@ import pytest
 @pytest.mark.parametrize('count', [
     2,
 ], indirect=True)
-def test_i2s_tdm_full_duplex(case_tester) -> None:  # type: ignore
-    case_tester.run_all_cases(timeout=30)
+def test_i2s_multi_dev(case_tester) -> None:  # type: ignore
+    case_tester.run_all_multi_dev_cases(reset=True, timeout=30)

+ 0 - 0
components/driver/test_apps/i2s_test_apps/i2s_tdm/sdkconfig.defaults → components/driver/test_apps/i2s_test_apps/i2s_multi_dev/sdkconfig.defaults