Skip to content

Commit f0fc0d7

Browse files
committed
tests: boards: esp32: add simplified i2s test
Adds simplified i2s test for: - esp32 - esp32s2 Signed-off-by: Marcio Ribeiro <[email protected]>
1 parent fb66629 commit f0fc0d7

File tree

9 files changed

+383
-0
lines changed

9 files changed

+383
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(i2s_test)
7+
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})

tests/boards/espressif/i2s/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#
2+
# Copyright (c) 2025 Espressif Systems (Shanghai) CO LTD
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
mainmenu "ESP32 I2S Loopback Test"
8+
9+
source "Kconfig.zephyr"
10+
11+
config I2S_TEST_SEPARATE_DEVICES
12+
bool "Use two separate I2S ports for loopback"
13+
help
14+
Use separate I2S ports for transmit and receive.
15+
16+
config I2S_TEST_USE_GPIO_LOOPBACK
17+
bool "Use GPIO loopback"
18+
help
19+
Use wiring between the data-out and data-in pins for looping back data.
20+
21+
config I2S_TEST_ALLOWED_DATA_DISCARD
22+
int "Allowed discard in received data"
23+
default 0
24+
help
25+
Maximum allowed discard between sent and received samples
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y
5+
CONFIG_I2S_TEST_ALLOWED_DATA_DISCARD=1
6+
CONFIG_I2S_ESP32_ALLOWED_EMPTY_TX_QUEUE_DEFERRAL_TIME_MS=1
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/* i2s-node0 is the transmitter/receiver */
8+
9+
/ {
10+
aliases {
11+
i2s-node0 = &i2s0;
12+
};
13+
};
14+
15+
&pinctrl {
16+
i2s0_default: i2s0_default {
17+
group1 {
18+
pinmux = <I2S0_O_WS_GPIO33>,
19+
<I2S0_O_BCK_GPIO32>;
20+
};
21+
22+
group2 {
23+
pinmux = <I2S0_O_SD_GPIO25>;
24+
input-enable;
25+
};
26+
27+
group3 {
28+
pinmux = <I2S0_I_SD_GPIO25>;
29+
output-enable;
30+
};
31+
};
32+
};
33+
34+
&i2s0 {
35+
status = "okay";
36+
pinctrl-0 = <&i2s0_default>;
37+
pinctrl-names = "default";
38+
39+
interrupts = <I2S0_INTR_SOURCE IRQ_DEFAULT_PRIORITY 0>,
40+
<I2S0_INTR_SOURCE IRQ_DEFAULT_PRIORITY 0>;
41+
interrupt-names = "tx", "rx";
42+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y
5+
CONFIG_I2S_TEST_ALLOWED_DATA_DISCARD=1
6+
CONFIG_I2S_ESP32_ALLOWED_EMPTY_TX_QUEUE_DEFERRAL_TIME_MS=1
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/* i2s-node0 is the transmitter/receiver */
8+
9+
/ {
10+
aliases {
11+
i2s-node0 = &i2s0;
12+
};
13+
};
14+
15+
&i2s0_default {
16+
group1 {
17+
pinmux = <I2S0_O_WS_GPIO5>,
18+
<I2S0_O_BCK_GPIO6>;
19+
};
20+
21+
group2 {
22+
pinmux = <I2S0_O_SD_GPIO18>;
23+
input-enable;
24+
};
25+
26+
group3 {
27+
pinmux = <I2S0_I_SD_GPIO18>;
28+
output-enable;
29+
};
30+
};
31+
32+
&i2s0 {
33+
status = "okay";
34+
35+
interrupts = <I2S0_INTR_SOURCE IRQ_DEFAULT_PRIORITY 0>,
36+
<I2S0_INTR_SOURCE IRQ_DEFAULT_PRIORITY 0>;
37+
interrupt-names = "rx", "tx";
38+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_I2S=y
2+
CONFIG_ZTEST=y
3+
CONFIG_TEST_USERSPACE=y
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/ztest.h>
9+
#include <zephyr/device.h>
10+
#include <zephyr/drivers/i2s.h>
11+
12+
#define SAMPLE_NO 32
13+
#define TIMEOUT 2000
14+
#define FRAME_CLK_FREQ 8000
15+
16+
#define NUM_RX_BLOCKS 4
17+
#define NUM_TX_BLOCKS 4
18+
19+
#define VAL_L 11
20+
#define VAL_R 22
21+
22+
#define TRANSFER_REPEAT_COUNT 100
23+
24+
#define I2S_DEV_NODE_RX DT_ALIAS(i2s_node0)
25+
#ifdef CONFIG_I2S_TEST_SEPARATE_DEVICES
26+
#define I2S_DEV_NODE_TX DT_ALIAS(i2s_node1)
27+
#else
28+
#define I2S_DEV_NODE_TX DT_ALIAS(i2s_node0)
29+
#endif
30+
31+
ZTEST_DMEM const struct device *dev_i2s_rx = DEVICE_DT_GET_OR_NULL(I2S_DEV_NODE_RX);
32+
ZTEST_DMEM const struct device *dev_i2s_tx = DEVICE_DT_GET_OR_NULL(I2S_DEV_NODE_TX);
33+
ZTEST_DMEM const struct device *dev_i2s = DEVICE_DT_GET_OR_NULL(I2S_DEV_NODE_RX);
34+
35+
#define BLOCK_SIZE (2 * SAMPLE_NO * sizeof(int16_t))
36+
37+
K_MEM_SLAB_DEFINE(rx_mem_slab, BLOCK_SIZE, NUM_RX_BLOCKS, 32);
38+
K_MEM_SLAB_DEFINE(tx_mem_slab, BLOCK_SIZE, NUM_TX_BLOCKS, 32);
39+
40+
void fill_buf(int16_t *tx_block, int16_t val_l, int16_t val_r)
41+
{
42+
for (int16_t i = 0; i < SAMPLE_NO; i++) {
43+
tx_block[2 * i] = val_l + i;
44+
tx_block[2 * i + 1] = val_r + i;
45+
}
46+
}
47+
48+
int verify_buf(int16_t *rx_block, int16_t val_l, int16_t val_r)
49+
{
50+
int sample_no = SAMPLE_NO;
51+
52+
#if (CONFIG_I2S_TEST_ALLOWED_DATA_DISCARD > 0)
53+
static ZTEST_DMEM int offset = -1;
54+
55+
if (offset < 0) {
56+
do {
57+
++offset;
58+
if (offset > CONFIG_I2S_TEST_ALLOWED_DATA_DISCARD) {
59+
TC_PRINT("Allowed data discard exceeded\n");
60+
return -TC_FAIL;
61+
}
62+
} while ((rx_block[0] != val_l + offset) || (rx_block[1] != val_r + offset));
63+
}
64+
65+
val_l += offset;
66+
val_r += offset;
67+
sample_no -= offset;
68+
#endif
69+
70+
for (int16_t i = 0; i < sample_no; i++) {
71+
if (rx_block[2 * i] != (val_l + i)) {
72+
TC_PRINT("Error: data_l mismatch at position "
73+
"%d, expected %d, actual %d\n",
74+
i, (int)(val_l + i), (int)rx_block[2 * i]);
75+
return -TC_FAIL;
76+
}
77+
if (rx_block[2 * i + 1] != (val_r + i)) {
78+
TC_PRINT("Error: data_r mismatch at position "
79+
"%d, expected %d, actual %d\n",
80+
i, (int)(val_r + i), (int)rx_block[2 * i + 1]);
81+
return -TC_FAIL;
82+
}
83+
}
84+
85+
return TC_PASS;
86+
}
87+
88+
static int tx_block_write_slab(const struct device *i2s_dev, int16_t val_l, int16_t val_r, int err,
89+
struct k_mem_slab *slab)
90+
{
91+
char tx_block[BLOCK_SIZE];
92+
int ret;
93+
94+
fill_buf((uint16_t *)tx_block, val_l, val_r);
95+
ret = i2s_buf_write(i2s_dev, tx_block, BLOCK_SIZE);
96+
if (ret != err) {
97+
TC_PRINT("Error: i2s_write failed expected %d, actual %d\n", err, ret);
98+
return -TC_FAIL;
99+
}
100+
101+
return TC_PASS;
102+
}
103+
104+
int tx_block_write(const struct device *i2s_dev, int16_t val_l, int16_t val_r, int err)
105+
{
106+
return tx_block_write_slab(i2s_dev, val_l, val_r, err, &tx_mem_slab);
107+
}
108+
109+
static int rx_block_read_slab(const struct device *i2s_dev, int16_t val_l, int16_t val_r,
110+
struct k_mem_slab *slab)
111+
{
112+
char rx_block[BLOCK_SIZE];
113+
size_t rx_size;
114+
int ret;
115+
116+
ret = i2s_buf_read(i2s_dev, rx_block, &rx_size);
117+
if (ret < 0 || rx_size != BLOCK_SIZE) {
118+
TC_PRINT("Error: Read failed\n");
119+
return -TC_FAIL;
120+
}
121+
ret = verify_buf((uint16_t *)rx_block, val_l, val_r);
122+
if (ret < 0) {
123+
TC_PRINT("Error: Verify failed\n");
124+
return -TC_FAIL;
125+
}
126+
127+
return TC_PASS;
128+
}
129+
130+
int rx_block_read(const struct device *i2s_dev, int16_t val_l, int16_t val_r)
131+
{
132+
return rx_block_read_slab(i2s_dev, val_l, val_r, &rx_mem_slab);
133+
}
134+
135+
int configure_stream(const struct device *i2s_dev, enum i2s_dir dir)
136+
{
137+
int ret;
138+
struct i2s_config i2s_cfg = {0};
139+
140+
i2s_cfg.word_size = 16U;
141+
i2s_cfg.channels = 2U;
142+
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
143+
i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ;
144+
i2s_cfg.block_size = BLOCK_SIZE;
145+
i2s_cfg.timeout = TIMEOUT;
146+
147+
if (dir == I2S_DIR_TX) {
148+
/* Configure the Transmit port as Master */
149+
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
150+
} else if (dir == I2S_DIR_RX) {
151+
/* Configure the Receive port as Slave */
152+
i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE;
153+
} else { /* dir == I2S_DIR_BOTH */
154+
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
155+
}
156+
157+
if (!IS_ENABLED(CONFIG_I2S_TEST_USE_GPIO_LOOPBACK)) {
158+
i2s_cfg.options |= I2S_OPT_LOOPBACK;
159+
}
160+
161+
if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) {
162+
i2s_cfg.mem_slab = &tx_mem_slab;
163+
ret = i2s_configure(i2s_dev, I2S_DIR_TX, &i2s_cfg);
164+
if (ret < 0) {
165+
TC_PRINT("Failed to configure I2S TX stream (%d)\n", ret);
166+
return -TC_FAIL;
167+
}
168+
}
169+
170+
if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) {
171+
i2s_cfg.mem_slab = &rx_mem_slab;
172+
ret = i2s_configure(i2s_dev, I2S_DIR_RX, &i2s_cfg);
173+
if (ret < 0) {
174+
TC_PRINT("Failed to configure I2S RX stream (%d)\n", ret);
175+
return -TC_FAIL;
176+
}
177+
}
178+
179+
return TC_PASS;
180+
}
181+
182+
static void *setup(void)
183+
{
184+
k_thread_access_grant(k_current_get(), &rx_mem_slab, &tx_mem_slab);
185+
k_object_access_grant(dev_i2s_rx, k_current_get());
186+
k_object_access_grant(dev_i2s_tx, k_current_get());
187+
188+
return NULL;
189+
}
190+
191+
static void before(void *fixture)
192+
{
193+
ARG_UNUSED(fixture);
194+
195+
int ret;
196+
197+
zassert_not_null(dev_i2s_rx, "RX device not found");
198+
zassert_true(device_is_ready(dev_i2s_rx), "device %s is not ready", dev_i2s_rx->name);
199+
200+
zassert_not_null(dev_i2s_tx, "TX device not found");
201+
zassert_true(device_is_ready(dev_i2s_tx), "device %s is not ready", dev_i2s_tx->name);
202+
203+
ret = configure_stream(dev_i2s_rx, I2S_DIR_RX);
204+
zassert_equal(ret, TC_PASS);
205+
206+
ret = configure_stream(dev_i2s_tx, I2S_DIR_TX);
207+
zassert_equal(ret, TC_PASS);
208+
}
209+
210+
/** @brief I2S transfer.
211+
*
212+
* - START trigger starts both the transmission and reception.
213+
* - sending / receiving a sequence of data returns success.
214+
* - DRAIN trigger empties the transmit queue and stops both streams.
215+
*/
216+
ZTEST_USER(i2s_loopback, test_i2s_transfer)
217+
{
218+
int ret;
219+
220+
/* Prefill TX queue */
221+
ret = tx_block_write(dev_i2s, VAL_L, VAL_R, 0);
222+
zassert_equal(ret, TC_PASS);
223+
224+
ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START);
225+
zassert_equal(ret, 0, "RX/TX START trigger failed\n");
226+
227+
for (int i = 0; i < TRANSFER_REPEAT_COUNT; i++) {
228+
ret = tx_block_write(dev_i2s_tx, VAL_L, VAL_R, 0);
229+
zassert_equal(ret, TC_PASS);
230+
231+
ret = rx_block_read(dev_i2s_rx, VAL_L, VAL_R);
232+
zassert_equal(ret, TC_PASS);
233+
}
234+
235+
/* All data written, all but one data block read, flush TX queue
236+
* and stop both streams.
237+
*/
238+
ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN);
239+
zassert_equal(ret, 0, "RX/TX DRAIN trigger failed");
240+
}
241+
242+
ZTEST_SUITE(i2s_loopback, NULL, setup, before, NULL, NULL);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests:
2+
boards.esp32.i2s.gpio_loopback:
3+
depends_on:
4+
- i2s
5+
- gpio
6+
tags:
7+
- drivers
8+
- userspace
9+
filter: CONFIG_I2S_TEST_USE_GPIO_LOOPBACK
10+
harness: ztest
11+
harness_config:
12+
fixture: gpio_loopback

0 commit comments

Comments
 (0)