Skip to content

Commit 8b914f6

Browse files
hakehuangcarlescufi
authored andcommitted
samples: i2s: Add i2s_codec example
This example demonstrates I2S audio playback combined with the codec API. It can use the dmic API for audio input. Signed-off-by: Hake Huang <[email protected]> Signed-off-by: Vit Stanicek <[email protected]>
1 parent 446282c commit 8b914f6

File tree

10 files changed

+384
-0
lines changed

10 files changed

+384
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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_rx_tx)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2024 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
source "Kconfig.zephyr"
5+
6+
config I2S_INIT_BUFFERS
7+
int "Initial count of audio data blocks"
8+
default 2
9+
help
10+
Controls the initial count of audio data blocks, which are (optionally)
11+
filled by data from the DMIC peripheral and played back by the I2S
12+
output perihperal.
13+
14+
config SAMPLE_FREQ
15+
int "Sample rate"
16+
default 48000
17+
help
18+
Sample frequency of the system.
19+
20+
config USE_DMIC
21+
bool "Use DMIC as an audio input"
22+
23+
if USE_DMIC
24+
25+
config DMIC_CHANNELS
26+
int "Number of DMIC channels"
27+
default 1
28+
help
29+
Count of DMIC channels to capture and process.
30+
31+
endif
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.. zephyr:code-sample:: i2s_codec
2+
:name: I2S codec
3+
:relevant-api: i2s_interface
4+
5+
Process an audio stream to codec.
6+
7+
Overview
8+
********
9+
10+
This sample demonstrates how to use an I2S driver in a simple processing of
11+
an audio stream. It configures and starts from memory buffer or from DMIC to
12+
record i2s data and send to codec with DMA.
13+
14+
Requirements
15+
************
16+
17+
This sample has been tested on mimxrt595_evk/mimxrt595s/cm33
18+
19+
Building and Running
20+
********************
21+
22+
The code can be found in :zephyr_file:`samples/drivers/i2s/i2s_codec`.
23+
24+
To build and flash the application:
25+
26+
.. zephyr-app-commands::
27+
:zephyr-app: samples/drivers/i2s/i2s_codec
28+
:board: mimxrt595_evk/mimxrt595s/cm33
29+
:goals: build flash
30+
:compact:
31+
32+
To run you can connect earphones to the lineout connect and hear the sound
33+
from DMIC or from memory buffer.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Copyright 2024 NXP
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
CONFIG_DMA=y
8+
CONFIG_I2S_MCUX_FLEXCOMM=y
9+
CONFIG_I3C=y
10+
CONFIG_HEAP_MEM_POOL_SIZE=81920
11+
CONFIG_AUDIO=y
12+
CONFIG_AUDIO_CODEC=y
13+
CONFIG_AUDIO_CODEC_WM8904=y
14+
CONFIG_I2S_INIT_BUFFERS=4
15+
CONFIG_SAMPLE_FREQ=16000
16+
CONFIG_USE_DMIC=y
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/ {
2+
aliases {
3+
i2s-codec-rx = &i2s0;
4+
i2s-codec-tx = &i2s1;
5+
};
6+
};
7+
8+
&i3c0 {
9+
clk-divider = <20>;
10+
11+
status = "okay";
12+
};
13+
14+
&i2s0 {
15+
status = "okay";
16+
};
17+
18+
&i2s1 {
19+
status = "okay";
20+
};
21+
22+
dmic_dev: &dmic0 {
23+
status = "okay";
24+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Copyright 2024 NXP
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
CONFIG_DMA=y
8+
CONFIG_I2S_MCUX_FLEXCOMM=y
9+
CONFIG_I3C=y
10+
CONFIG_HEAP_MEM_POOL_SIZE=81920
11+
CONFIG_AUDIO=y
12+
CONFIG_AUDIO_CODEC=y
13+
CONFIG_AUDIO_CODEC_WM8904=y
14+
CONFIG_I2S_INIT_BUFFERS=4
15+
CONFIG_SAMPLE_FREQ=16000
16+
CONFIG_USE_DMIC=y
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/ {
2+
aliases {
3+
i2s-codec-rx = &i2s0;
4+
i2s-codec-tx = &i2s1;
5+
};
6+
};
7+
8+
&i3c0 {
9+
status = "okay";
10+
};
11+
12+
&i2s0 {
13+
status = "okay";
14+
};
15+
16+
&i2s1 {
17+
status = "okay";
18+
};
19+
20+
dmic_dev: &dmic0 {
21+
status = "okay";
22+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_I2S=y
2+
CONFIG_AUDIO=y
3+
CONFIG_AUDIO_DMIC=y
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
sample:
2+
name: codec sample
3+
tests:
4+
sample.drivers.i2s.codec:
5+
tags: i2s
6+
platform_allow:
7+
- mimxrt595_evk/mimxrt595s/cm33
8+
harness: console
9+
harness_config:
10+
type: one_line
11+
regex:
12+
- "start streams"
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Copyright 2024 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/sys/printk.h>
9+
#include <zephyr/audio/dmic.h>
10+
#include <zephyr/drivers/i2s.h>
11+
#include <zephyr/drivers/gpio.h>
12+
#include <zephyr/audio/codec.h>
13+
#include <string.h>
14+
15+
16+
#define I2S_CODEC_TX DT_ALIAS(i2s_codec_tx)
17+
18+
#define SAMPLE_FREQUENCY CONFIG_SAMPLE_FREQ
19+
#define SAMPLE_BIT_WIDTH (16U)
20+
#define BYTES_PER_SAMPLE sizeof(int16_t)
21+
#if CONFIG_USE_DMIC
22+
#define NUMBER_OF_CHANNELS CONFIG_DMIC_CHANNELS
23+
#else
24+
#define NUMBER_OF_CHANNELS (2U)
25+
#endif
26+
/* Such block length provides an echo with the delay of 100 ms. */
27+
#define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS)
28+
#define INITIAL_BLOCKS CONFIG_I2S_INIT_BUFFERS
29+
#define TIMEOUT (2000U)
30+
31+
#define BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
32+
#define BLOCK_COUNT (INITIAL_BLOCKS + 32)
33+
K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);
34+
35+
static bool configure_tx_streams(const struct device *i2s_dev, struct i2s_config *config)
36+
{
37+
int ret;
38+
39+
ret = i2s_configure(i2s_dev, I2S_DIR_TX, config);
40+
if (ret < 0) {
41+
printk("Failed to configure codec stream: %d\n", ret);
42+
return false;
43+
}
44+
45+
return true;
46+
}
47+
48+
static bool trigger_command(const struct device *i2s_dev_codec,
49+
enum i2s_trigger_cmd cmd)
50+
{
51+
int ret;
52+
53+
ret = i2s_trigger(i2s_dev_codec, I2S_DIR_TX, cmd);
54+
if (ret < 0) {
55+
printk("Failed to trigger command %d on TX: %d\n", cmd, ret);
56+
return false;
57+
}
58+
59+
return true;
60+
}
61+
62+
int main(void)
63+
{
64+
const struct device *const i2s_dev_codec = DEVICE_DT_GET(I2S_CODEC_TX);
65+
#if CONFIG_USE_DMIC
66+
const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
67+
#endif
68+
const struct device *const codec_dev = DEVICE_DT_GET(DT_NODELABEL(audio_codec));
69+
struct i2s_config config;
70+
struct audio_codec_cfg audio_cfg;
71+
int ret;
72+
73+
#if CONFIG_USE_DMIC
74+
struct pcm_stream_cfg stream = {
75+
.pcm_width = SAMPLE_BIT_WIDTH,
76+
.mem_slab = &mem_slab,
77+
};
78+
struct dmic_cfg cfg = {
79+
.io = {
80+
/* These fields can be used to limit the PDM clock
81+
* configurations that the driver is allowed to use
82+
* to those supported by the microphone.
83+
*/
84+
.min_pdm_clk_freq = 1000000,
85+
.max_pdm_clk_freq = 3500000,
86+
.min_pdm_clk_dc = 40,
87+
.max_pdm_clk_dc = 60,
88+
},
89+
.streams = &stream,
90+
.channel = {
91+
.req_num_streams = 1,
92+
},
93+
};
94+
#endif
95+
printk("codec sample\n");
96+
97+
#if CONFIG_USE_DMIC
98+
if (!device_is_ready(dmic_dev)) {
99+
printk("%s is not ready", dmic_dev->name);
100+
return 0;
101+
}
102+
#endif
103+
104+
if (!device_is_ready(i2s_dev_codec)) {
105+
printk("%s is not ready\n", i2s_dev_codec->name);
106+
return 0;
107+
}
108+
109+
110+
if (!device_is_ready(codec_dev)) {
111+
printk("%s is not ready", codec_dev->name);
112+
return 0;
113+
}
114+
audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK;
115+
audio_cfg.dai_type = AUDIO_DAI_TYPE_I2S;
116+
audio_cfg.dai_cfg.i2s.word_size = SAMPLE_BIT_WIDTH;
117+
audio_cfg.dai_cfg.i2s.channels = 2;
118+
audio_cfg.dai_cfg.i2s.format = I2S_FMT_DATA_FORMAT_I2S;
119+
audio_cfg.dai_cfg.i2s.options = I2S_OPT_FRAME_CLK_MASTER;
120+
audio_cfg.dai_cfg.i2s.frame_clk_freq = SAMPLE_FREQUENCY;
121+
audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab;
122+
audio_cfg.dai_cfg.i2s.block_size = BLOCK_SIZE;
123+
audio_codec_configure(codec_dev, &audio_cfg);
124+
k_msleep(1000);
125+
126+
#if CONFIG_USE_DMIC
127+
cfg.channel.req_num_chan = 2;
128+
cfg.channel.req_chan_map_lo =
129+
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) |
130+
dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
131+
cfg.streams[0].pcm_rate = SAMPLE_FREQUENCY;
132+
cfg.streams[0].block_size = BLOCK_SIZE;
133+
134+
printk("PCM output rate: %u, channels: %u\n",
135+
cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
136+
137+
ret = dmic_configure(dmic_dev, &cfg);
138+
if (ret < 0) {
139+
printk("Failed to configure the driver: %d", ret);
140+
return ret;
141+
}
142+
#endif
143+
144+
config.word_size = SAMPLE_BIT_WIDTH;
145+
config.channels = NUMBER_OF_CHANNELS;
146+
config.format = I2S_FMT_DATA_FORMAT_I2S;
147+
config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
148+
config.frame_clk_freq = SAMPLE_FREQUENCY;
149+
config.mem_slab = &mem_slab;
150+
config.block_size = BLOCK_SIZE;
151+
config.timeout = TIMEOUT;
152+
if (!configure_tx_streams(i2s_dev_codec, &config)) {
153+
printk("failure to config streams\n");
154+
return 0;
155+
}
156+
157+
printk("start streams\n");
158+
for (;;) {
159+
bool started = false;
160+
#if CONFIG_USE_DMIC
161+
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
162+
if (ret < 0) {
163+
printk("START trigger failed: %d", ret);
164+
return ret;
165+
}
166+
#endif
167+
while (1) {
168+
void *mem_block;
169+
uint32_t block_size = BLOCK_SIZE;
170+
int ret;
171+
int i;
172+
173+
for (i = 0; i < 2; i++) {
174+
#if CONFIG_USE_DMIC
175+
ret = dmic_read(dmic_dev, 0,
176+
&mem_block, &block_size, TIMEOUT);
177+
if (ret < 0) {
178+
printk("read failed: %d", ret);
179+
break;
180+
}
181+
#else
182+
ret = k_mem_slab_alloc(&mem_slab,
183+
&mem_block, Z_TIMEOUT_TICKS(TIMEOUT));
184+
if (ret < 0) {
185+
printk("Failed to allocate TX block\n");
186+
return 0;
187+
}
188+
#endif
189+
ret = i2s_write(i2s_dev_codec, mem_block, block_size);
190+
if (ret < 0) {
191+
printk("Failed to write data: %d\n", ret);
192+
break;
193+
}
194+
}
195+
if (ret < 0) {
196+
printk("error %d\n", ret);
197+
break;
198+
}
199+
if (!started) {
200+
i2s_trigger(i2s_dev_codec, I2S_DIR_TX, I2S_TRIGGER_START);
201+
started = true;
202+
}
203+
}
204+
if (!trigger_command(i2s_dev_codec,
205+
I2S_TRIGGER_DROP)) {
206+
printk("Send I2S trigger DRAIN failed: %d", ret);
207+
return 0;
208+
}
209+
#if CONFIG_USE_DMIC
210+
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
211+
if (ret < 0) {
212+
printk("STOP trigger failed: %d", ret);
213+
return 0;
214+
}
215+
#endif
216+
printk("Streams stopped\n");
217+
return 0;
218+
}
219+
}

0 commit comments

Comments
 (0)