forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
1 parent
446282c
commit 8b914f6
Showing
10 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(i2s_rx_tx) | ||
|
||
target_sources(app PRIVATE src/main.c) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Copyright (c) 2024 NXP | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
source "Kconfig.zephyr" | ||
|
||
config I2S_INIT_BUFFERS | ||
int "Initial count of audio data blocks" | ||
default 2 | ||
help | ||
Controls the initial count of audio data blocks, which are (optionally) | ||
filled by data from the DMIC peripheral and played back by the I2S | ||
output perihperal. | ||
|
||
config SAMPLE_FREQ | ||
int "Sample rate" | ||
default 48000 | ||
help | ||
Sample frequency of the system. | ||
|
||
config USE_DMIC | ||
bool "Use DMIC as an audio input" | ||
|
||
if USE_DMIC | ||
|
||
config DMIC_CHANNELS | ||
int "Number of DMIC channels" | ||
default 1 | ||
help | ||
Count of DMIC channels to capture and process. | ||
|
||
endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
.. zephyr:code-sample:: i2s_codec | ||
:name: I2S codec | ||
:relevant-api: i2s_interface | ||
|
||
Process an audio stream to codec. | ||
|
||
Overview | ||
******** | ||
|
||
This sample demonstrates how to use an I2S driver in a simple processing of | ||
an audio stream. It configures and starts from memory buffer or from DMIC to | ||
record i2s data and send to codec with DMA. | ||
|
||
Requirements | ||
************ | ||
|
||
This sample has been tested on mimxrt595_evk/mimxrt595s/cm33 | ||
|
||
Building and Running | ||
******************** | ||
|
||
The code can be found in :zephyr_file:`samples/drivers/i2s/i2s_codec`. | ||
|
||
To build and flash the application: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/drivers/i2s/i2s_codec | ||
:board: mimxrt595_evk/mimxrt595s/cm33 | ||
:goals: build flash | ||
:compact: | ||
|
||
To run you can connect earphones to the lineout connect and hear the sound | ||
from DMIC or from memory buffer. |
16 changes: 16 additions & 0 deletions
16
samples/drivers/i2s/i2s_codec/boards/mimxrt595_evk_mimxrt595s_cm33.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# | ||
# Copyright 2024 NXP | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
|
||
CONFIG_DMA=y | ||
CONFIG_I2S_MCUX_FLEXCOMM=y | ||
CONFIG_I3C=y | ||
CONFIG_HEAP_MEM_POOL_SIZE=81920 | ||
CONFIG_AUDIO=y | ||
CONFIG_AUDIO_CODEC=y | ||
CONFIG_AUDIO_CODEC_WM8904=y | ||
CONFIG_I2S_INIT_BUFFERS=4 | ||
CONFIG_SAMPLE_FREQ=16000 | ||
CONFIG_USE_DMIC=y |
24 changes: 24 additions & 0 deletions
24
samples/drivers/i2s/i2s_codec/boards/mimxrt595_evk_mimxrt595s_cm33.overlay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/ { | ||
aliases { | ||
i2s-codec-rx = &i2s0; | ||
i2s-codec-tx = &i2s1; | ||
}; | ||
}; | ||
|
||
&i3c0 { | ||
clk-divider = <20>; | ||
|
||
status = "okay"; | ||
}; | ||
|
||
&i2s0 { | ||
status = "okay"; | ||
}; | ||
|
||
&i2s1 { | ||
status = "okay"; | ||
}; | ||
|
||
dmic_dev: &dmic0 { | ||
status = "okay"; | ||
}; |
16 changes: 16 additions & 0 deletions
16
samples/drivers/i2s/i2s_codec/boards/mimxrt685_evk_mimxrt685s_cm33.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# | ||
# Copyright 2024 NXP | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
|
||
CONFIG_DMA=y | ||
CONFIG_I2S_MCUX_FLEXCOMM=y | ||
CONFIG_I3C=y | ||
CONFIG_HEAP_MEM_POOL_SIZE=81920 | ||
CONFIG_AUDIO=y | ||
CONFIG_AUDIO_CODEC=y | ||
CONFIG_AUDIO_CODEC_WM8904=y | ||
CONFIG_I2S_INIT_BUFFERS=4 | ||
CONFIG_SAMPLE_FREQ=16000 | ||
CONFIG_USE_DMIC=y |
22 changes: 22 additions & 0 deletions
22
samples/drivers/i2s/i2s_codec/boards/mimxrt685_evk_mimxrt685s_cm33.overlay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/ { | ||
aliases { | ||
i2s-codec-rx = &i2s0; | ||
i2s-codec-tx = &i2s1; | ||
}; | ||
}; | ||
|
||
&i3c0 { | ||
status = "okay"; | ||
}; | ||
|
||
&i2s0 { | ||
status = "okay"; | ||
}; | ||
|
||
&i2s1 { | ||
status = "okay"; | ||
}; | ||
|
||
dmic_dev: &dmic0 { | ||
status = "okay"; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CONFIG_I2S=y | ||
CONFIG_AUDIO=y | ||
CONFIG_AUDIO_DMIC=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
sample: | ||
name: codec sample | ||
tests: | ||
sample.drivers.i2s.codec: | ||
tags: i2s | ||
platform_allow: | ||
- mimxrt595_evk/mimxrt595s/cm33 | ||
harness: console | ||
harness_config: | ||
type: one_line | ||
regex: | ||
- "start streams" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
/* | ||
* Copyright 2024 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys/printk.h> | ||
#include <zephyr/audio/dmic.h> | ||
#include <zephyr/drivers/i2s.h> | ||
#include <zephyr/drivers/gpio.h> | ||
#include <zephyr/audio/codec.h> | ||
#include <string.h> | ||
|
||
|
||
#define I2S_CODEC_TX DT_ALIAS(i2s_codec_tx) | ||
|
||
#define SAMPLE_FREQUENCY CONFIG_SAMPLE_FREQ | ||
#define SAMPLE_BIT_WIDTH (16U) | ||
#define BYTES_PER_SAMPLE sizeof(int16_t) | ||
#if CONFIG_USE_DMIC | ||
#define NUMBER_OF_CHANNELS CONFIG_DMIC_CHANNELS | ||
#else | ||
#define NUMBER_OF_CHANNELS (2U) | ||
#endif | ||
/* Such block length provides an echo with the delay of 100 ms. */ | ||
#define SAMPLES_PER_BLOCK ((SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS) | ||
#define INITIAL_BLOCKS CONFIG_I2S_INIT_BUFFERS | ||
#define TIMEOUT (2000U) | ||
|
||
#define BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK) | ||
#define BLOCK_COUNT (INITIAL_BLOCKS + 32) | ||
K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4); | ||
|
||
static bool configure_tx_streams(const struct device *i2s_dev, struct i2s_config *config) | ||
{ | ||
int ret; | ||
|
||
ret = i2s_configure(i2s_dev, I2S_DIR_TX, config); | ||
if (ret < 0) { | ||
printk("Failed to configure codec stream: %d\n", ret); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
static bool trigger_command(const struct device *i2s_dev_codec, | ||
enum i2s_trigger_cmd cmd) | ||
{ | ||
int ret; | ||
|
||
ret = i2s_trigger(i2s_dev_codec, I2S_DIR_TX, cmd); | ||
if (ret < 0) { | ||
printk("Failed to trigger command %d on TX: %d\n", cmd, ret); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
int main(void) | ||
{ | ||
const struct device *const i2s_dev_codec = DEVICE_DT_GET(I2S_CODEC_TX); | ||
#if CONFIG_USE_DMIC | ||
const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev)); | ||
#endif | ||
const struct device *const codec_dev = DEVICE_DT_GET(DT_NODELABEL(audio_codec)); | ||
struct i2s_config config; | ||
struct audio_codec_cfg audio_cfg; | ||
int ret; | ||
|
||
#if CONFIG_USE_DMIC | ||
struct pcm_stream_cfg stream = { | ||
.pcm_width = SAMPLE_BIT_WIDTH, | ||
.mem_slab = &mem_slab, | ||
}; | ||
struct dmic_cfg cfg = { | ||
.io = { | ||
/* These fields can be used to limit the PDM clock | ||
* configurations that the driver is allowed to use | ||
* to those supported by the microphone. | ||
*/ | ||
.min_pdm_clk_freq = 1000000, | ||
.max_pdm_clk_freq = 3500000, | ||
.min_pdm_clk_dc = 40, | ||
.max_pdm_clk_dc = 60, | ||
}, | ||
.streams = &stream, | ||
.channel = { | ||
.req_num_streams = 1, | ||
}, | ||
}; | ||
#endif | ||
printk("codec sample\n"); | ||
|
||
#if CONFIG_USE_DMIC | ||
if (!device_is_ready(dmic_dev)) { | ||
printk("%s is not ready", dmic_dev->name); | ||
return 0; | ||
} | ||
#endif | ||
|
||
if (!device_is_ready(i2s_dev_codec)) { | ||
printk("%s is not ready\n", i2s_dev_codec->name); | ||
return 0; | ||
} | ||
|
||
|
||
if (!device_is_ready(codec_dev)) { | ||
printk("%s is not ready", codec_dev->name); | ||
return 0; | ||
} | ||
audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK; | ||
audio_cfg.dai_type = AUDIO_DAI_TYPE_I2S; | ||
audio_cfg.dai_cfg.i2s.word_size = SAMPLE_BIT_WIDTH; | ||
audio_cfg.dai_cfg.i2s.channels = 2; | ||
audio_cfg.dai_cfg.i2s.format = I2S_FMT_DATA_FORMAT_I2S; | ||
audio_cfg.dai_cfg.i2s.options = I2S_OPT_FRAME_CLK_MASTER; | ||
audio_cfg.dai_cfg.i2s.frame_clk_freq = SAMPLE_FREQUENCY; | ||
audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab; | ||
audio_cfg.dai_cfg.i2s.block_size = BLOCK_SIZE; | ||
audio_codec_configure(codec_dev, &audio_cfg); | ||
k_msleep(1000); | ||
|
||
#if CONFIG_USE_DMIC | ||
cfg.channel.req_num_chan = 2; | ||
cfg.channel.req_chan_map_lo = | ||
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) | | ||
dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT); | ||
cfg.streams[0].pcm_rate = SAMPLE_FREQUENCY; | ||
cfg.streams[0].block_size = BLOCK_SIZE; | ||
|
||
printk("PCM output rate: %u, channels: %u\n", | ||
cfg.streams[0].pcm_rate, cfg.channel.req_num_chan); | ||
|
||
ret = dmic_configure(dmic_dev, &cfg); | ||
if (ret < 0) { | ||
printk("Failed to configure the driver: %d", ret); | ||
return ret; | ||
} | ||
#endif | ||
|
||
config.word_size = SAMPLE_BIT_WIDTH; | ||
config.channels = NUMBER_OF_CHANNELS; | ||
config.format = I2S_FMT_DATA_FORMAT_I2S; | ||
config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER; | ||
config.frame_clk_freq = SAMPLE_FREQUENCY; | ||
config.mem_slab = &mem_slab; | ||
config.block_size = BLOCK_SIZE; | ||
config.timeout = TIMEOUT; | ||
if (!configure_tx_streams(i2s_dev_codec, &config)) { | ||
printk("failure to config streams\n"); | ||
return 0; | ||
} | ||
|
||
printk("start streams\n"); | ||
for (;;) { | ||
bool started = false; | ||
#if CONFIG_USE_DMIC | ||
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START); | ||
if (ret < 0) { | ||
printk("START trigger failed: %d", ret); | ||
return ret; | ||
} | ||
#endif | ||
while (1) { | ||
void *mem_block; | ||
uint32_t block_size = BLOCK_SIZE; | ||
int ret; | ||
int i; | ||
|
||
for (i = 0; i < 2; i++) { | ||
#if CONFIG_USE_DMIC | ||
ret = dmic_read(dmic_dev, 0, | ||
&mem_block, &block_size, TIMEOUT); | ||
if (ret < 0) { | ||
printk("read failed: %d", ret); | ||
break; | ||
} | ||
#else | ||
ret = k_mem_slab_alloc(&mem_slab, | ||
&mem_block, Z_TIMEOUT_TICKS(TIMEOUT)); | ||
if (ret < 0) { | ||
printk("Failed to allocate TX block\n"); | ||
return 0; | ||
} | ||
#endif | ||
ret = i2s_write(i2s_dev_codec, mem_block, block_size); | ||
if (ret < 0) { | ||
printk("Failed to write data: %d\n", ret); | ||
break; | ||
} | ||
} | ||
if (ret < 0) { | ||
printk("error %d\n", ret); | ||
break; | ||
} | ||
if (!started) { | ||
i2s_trigger(i2s_dev_codec, I2S_DIR_TX, I2S_TRIGGER_START); | ||
started = true; | ||
} | ||
} | ||
if (!trigger_command(i2s_dev_codec, | ||
I2S_TRIGGER_DROP)) { | ||
printk("Send I2S trigger DRAIN failed: %d", ret); | ||
return 0; | ||
} | ||
#if CONFIG_USE_DMIC | ||
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP); | ||
if (ret < 0) { | ||
printk("STOP trigger failed: %d", ret); | ||
return 0; | ||
} | ||
#endif | ||
printk("Streams stopped\n"); | ||
return 0; | ||
} | ||
} |