Skip to content

Commit 52fa753

Browse files
nordic-seglnordicjm
authored andcommitted
tests: drivers: audio: Add test that collects audio from PDM microphone
Verify operation of PDM driver with physical microphone and manual sound analysis. Signed-off-by: Sebastian Głąb <[email protected]>
1 parent 67750a2 commit 52fa753

File tree

12 files changed

+327
-2
lines changed

12 files changed

+327
-2
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@
832832
/tests/bluetooth/bsim/nrf_auraconfig/ @nrfconnect/ncs-audio
833833
/tests/bluetooth/tester/ @carlescufi @nrfconnect/ncs-paladin
834834
/tests/crypto/ @stephen-nordic @magnev
835+
/tests/drivers/audio/dmic_dump_buffer/ @nrfconnect/ncs-low-level-test
835836
/tests/drivers/audio/pdm_loopback/ @nrfconnect/ncs-low-level-test
836837
/tests/drivers/gpio/ @nrfconnect/ncs-low-level-test @nrfconnect/ncs-ll-ursus
837838
/tests/drivers/flash/flash_ipuc/ @nrfconnect/ncs-charon
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(dmic)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
source "Kconfig.zephyr"
8+
9+
config TEST_USE_DMM
10+
bool "Use of DMM pre-allocation"
11+
help
12+
The test will use it to determine whether to prealocate DMM
13+
buffer or use regular mem slab and allocate dmm buffer inside
14+
PDM driver.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
This test uses DMIC driver to collect sound from PDM microphone.
2+
Audio samples are output on serial port @921600 bit/s in binary mode.
3+
4+
5+
Follow these steps to collect and analyse sound:
6+
7+
1. Connect PDM microphone (tested with https://www.adafruit.com/product/3492) to PDM_CLK and PDM_DIN as defined in the board overlay.
8+
9+
2. Compile and run dmic_dump_buffer test:
10+
west build -b nrf54l15dk/nrf54l15/cpuapp --pristine --test-item drivers.audio.dmic_dump_buffer .
11+
west flash --erase
12+
13+
3. Store output from serial port to a file:
14+
- press RESET button on DK to stop execution
15+
- start data capture, f.e.
16+
picocom -f n -b 921600 /dev/serial/by-id/usb-SEGGER_J-Link_00105xxx-if02 > /home/user/sound_capture.raw
17+
- release RESET button
18+
19+
4. Collect sound with the microphone. Then, stop data acquisition (f.e. Ctr+a, Ctrl+x)
20+
21+
5. If using picocom, remove text at the beginning and at the end of the file (that is added by picocom).
22+
23+
6. Convert end line symbols
24+
dos2unix -f /home/user/sound_capture.raw
25+
26+
7. Import raw data to Audacity (Signed 16-bit PCM; little-endian; mono; 16000Hz).
27+
28+
This test is based on https://docs.zephyrproject.org/latest/samples/shields/x_nucleo_iks02a1/microphone/README.html
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
&pinctrl {
8+
pdm0_default_alt: pdm0_default_alt {
9+
group1 {
10+
psels = <NRF_PSEL(PDM_CLK, 0, 30)>,
11+
<NRF_PSEL(PDM_DIN, 0, 31)>;
12+
};
13+
};
14+
};
15+
16+
dmic_dev: &pdm0 {
17+
status = "okay";
18+
pinctrl-0 = <&pdm0_default_alt>;
19+
pinctrl-names = "default";
20+
clock-source = "PCLK32M_HFXO";
21+
};
22+
23+
&uart0 {
24+
current-speed = < 921600 >;
25+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
&clock {
8+
hfclkaudio-frequency = <12288000>;
9+
};
10+
11+
&pinctrl {
12+
pdm0_default_alt: pdm0_default_alt {
13+
group1 {
14+
psels = <NRF_PSEL(PDM_CLK, 0, 25)>,
15+
<NRF_PSEL(PDM_DIN, 0, 26)>;
16+
};
17+
};
18+
};
19+
20+
dmic_dev: &pdm0 {
21+
status = "okay";
22+
pinctrl-0 = <&pdm0_default_alt>;
23+
pinctrl-names = "default";
24+
clock-source = "ACLK";
25+
};
26+
27+
&uart0 {
28+
current-speed = < 921600 >;
29+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
&pinctrl {
8+
pdm0_default_alt: pdm0_default_alt {
9+
group1 {
10+
psels = <NRF_PSEL(PDM_CLK, 1, 2)>,
11+
<NRF_PSEL(PDM_DIN, 1, 4)>;
12+
};
13+
};
14+
};
15+
16+
dmic_dev: &pdm0 {
17+
status = "okay";
18+
pinctrl-0 = <&pdm0_default_alt>;
19+
pinctrl-names = "default";
20+
clock-source = "PCLK32M";
21+
memory-regions = <&cpuapp_dma_region>;
22+
};
23+
24+
&uart136 {
25+
current-speed = < 921600 >;
26+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
&pinctrl {
8+
pdm20_default_alt: pdm20_default_alt {
9+
group1 {
10+
psels = <NRF_PSEL(PDM_CLK, 1, 12)>,
11+
<NRF_PSEL(PDM_DIN, 1, 13)>;
12+
};
13+
};
14+
};
15+
16+
dmic_dev: &pdm20 {
17+
status = "okay";
18+
pinctrl-0 = <&pdm20_default_alt>;
19+
pinctrl-names = "default";
20+
clock-source = "PCLK32M";
21+
};
22+
23+
&uart20 {
24+
current-speed = < 921600 >;
25+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CONFIG_BOOT_BANNER=n
2+
CONFIG_NCS_BOOT_BANNER=n
3+
4+
CONFIG_AUDIO=y
5+
CONFIG_AUDIO_DMIC=y
6+
7+
CONFIG_LOG=n
8+
CONFIG_LOG_MODE_IMMEDIATE=n
9+
CONFIG_PRINTK=y
10+
11+
CONFIG_MAIN_STACK_SIZE=16384
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <string.h>
8+
#include <zephyr/kernel.h>
9+
#include <zephyr/audio/dmic.h>
10+
11+
#if defined(CONFIG_HAS_NORDIC_DMM)
12+
#include <dmm.h>
13+
#endif
14+
15+
#include <zephyr/logging/log.h>
16+
LOG_MODULE_REGISTER(dmic_sample);
17+
18+
#define SAMPLE_RATE 16000
19+
#define SAMPLE_BIT_WIDTH 16
20+
#define BYTES_PER_SAMPLE (SAMPLE_BIT_WIDTH / 8)
21+
#define NO_OF_CHANNELS 1
22+
23+
/* Milliseconds to wait for a block to be captured by PCM peripheral. */
24+
#define READ_TIMEOUT 1200
25+
26+
/* Driver will allocate blocks from this slab to receive audio data into them.
27+
* Application, after getting a given block from the driver and processing its
28+
* data, needs to free that block.
29+
*/
30+
#define AUDIO_BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLE_RATE * NO_OF_CHANNELS / 40)
31+
/* Driver allocates memory "in advance" therefore 2 blocks may be not enough. */
32+
#define BLOCK_COUNT 4
33+
34+
#if CONFIG_TEST_USE_DMM
35+
struct k_mem_slab mem_slab;
36+
char __aligned(WB_UP(4)) mem_slab_buffer[BLOCK_COUNT * WB_UP(AUDIO_BLOCK_SIZE)]
37+
DMM_MEMORY_SECTION(DT_NODELABEL(dmic_dev));
38+
#else
39+
K_MEM_SLAB_DEFINE_STATIC(mem_slab, AUDIO_BLOCK_SIZE, BLOCK_COUNT, 4);
40+
#endif
41+
42+
int main(void)
43+
{
44+
const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
45+
int ret;
46+
int loop_counter = 1;
47+
void *buffer;
48+
uint32_t size;
49+
50+
struct pcm_stream_cfg stream = {
51+
.pcm_rate = SAMPLE_RATE,
52+
.pcm_width = SAMPLE_BIT_WIDTH,
53+
.block_size = AUDIO_BLOCK_SIZE,
54+
.mem_slab = &mem_slab,
55+
};
56+
57+
struct dmic_cfg cfg = {
58+
.io = {
59+
/* These fields can be used to limit the PDM clock
60+
* configurations that the driver is allowed to use
61+
* to those supported by the microphone.
62+
*/
63+
.min_pdm_clk_freq = 1000000,
64+
.max_pdm_clk_freq = 3250000,
65+
.min_pdm_clk_dc = 40,
66+
.max_pdm_clk_dc = 60,
67+
},
68+
.streams = &stream,
69+
.channel = {
70+
.req_num_chan = 1,
71+
.req_num_streams = 1,
72+
.req_chan_map_lo = dmic_build_channel_map(0, 0, PDM_CHAN_LEFT),
73+
},
74+
};
75+
76+
if (!device_is_ready(dmic_dev)) {
77+
LOG_ERR("%s is not ready", dmic_dev->name);
78+
return 0;
79+
}
80+
81+
#if CONFIG_TEST_USE_DMM
82+
ret = k_mem_slab_init(&mem_slab, mem_slab_buffer, WB_UP(AUDIO_BLOCK_SIZE), BLOCK_COUNT);
83+
if (ret < 0) {
84+
LOG_ERR("Memory slab initialization failed, return code = %d", ret);
85+
return ret;
86+
}
87+
#endif
88+
89+
ret = dmic_configure(dmic_dev, &cfg);
90+
if (ret < 0) {
91+
LOG_ERR("Failed to configure the driver: %d", ret);
92+
return ret;
93+
}
94+
95+
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
96+
if (ret < 0) {
97+
LOG_ERR("START trigger failed: %d", ret);
98+
return ret;
99+
}
100+
101+
while (1) {
102+
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
103+
if (ret < 0) {
104+
LOG_ERR("%d - read failed: %d", loop_counter, ret);
105+
return ret;
106+
}
107+
108+
/* Print buffer on serial port (in binary mode). */
109+
unsigned char pcm_l, pcm_h;
110+
int j;
111+
112+
uint16_t *pcm_out = buffer;
113+
114+
for (j = 0; j < size/2; j++) {
115+
pcm_l = (char)(pcm_out[j] & 0xFF);
116+
pcm_h = (char)((pcm_out[j] >> 8) & 0xFF);
117+
118+
z_impl_k_str_out(&pcm_l, 1);
119+
z_impl_k_str_out(&pcm_h, 1);
120+
}
121+
122+
k_mem_slab_free(&mem_slab, buffer);
123+
124+
loop_counter++;
125+
}
126+
127+
/* Dead code; left for reference. */
128+
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
129+
if (ret < 0) {
130+
LOG_ERR("STOP trigger failed: %d", ret);
131+
return ret;
132+
}
133+
134+
return 0;
135+
}

0 commit comments

Comments
 (0)