samples: drivers: Add simple application showing how to use DMIC API

Add a very simple application intended to show how to use the Audio
DMIC API and also to be an aid in developing drivers to implement
this API.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2021-08-19 17:18:37 +02:00 committed by Anas Nashif
commit 1518cc8337
7 changed files with 219 additions and 0 deletions

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(dmic)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,34 @@
.. _dmic_sample:
DMIC Sample
###########
Overview
********
This is a very simple application intended to show how to use the Audio DMIC
API and also to be an aid in developing drivers to implement this API.
It performs two PDM transfers with different configurations (using one channel
and two channels) but does not in any way process the received audio data.
Requirements
************
The device to be used by the sample is specified by defining a devicetree node
label named ``dmic_dev``.
The sample has been tested on :ref:`nrf52840dk_nrf52840` (nrf52840dk_nrf52840)
and :ref:`nrf5340dk_nrf5340` (nrf5340dk_nrf5340_cpuapp), and provides overlay
files for both of these boards.
Building and Running
********************
The code can be found in :zephyr_file:`samples/drivers/audio/dmic`.
To build and flash the application:
.. zephyr-app-commands::
:zephyr-app: samples/drivers/audio/dmic
:board: nrf52840dk_nrf52840
:goals: build flash
:compact:

View file

@ -0,0 +1,12 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
dmic_dev: &pdm0 {
status = "okay";
clk-pin = <30>;
din-pin = <31>;
clock-source = "PCLK32M_HFXO";
};

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
&clock {
hfclkaudio-frequency = <12288000>;
};
dmic_dev: &pdm0 {
status = "okay";
clk-pin = <25>;
din-pin = <26>;
clock-source = "ACLK";
};

View file

@ -0,0 +1,4 @@
CONFIG_AUDIO=y
CONFIG_AUDIO_DMIC=y
CONFIG_LOG=y

View file

@ -0,0 +1,9 @@
sample:
name: DMIC sample
tests:
sample.drivers.audio.dmic:
tags: DMIC
filter: dt_nodelabel_enabled("dmic_dev")
integration_platforms:
- nrf52840dk_nrf52840
- nrf5340dk_nrf5340_cpuapp

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <audio/dmic.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(dmic_sample);
#define MAX_SAMPLE_RATE 16000
#define SAMPLE_BIT_WIDTH 16
#define BYTES_PER_SAMPLE sizeof(int16_t)
/* Milliseconds to wait for a block to be read. */
#define READ_TIMEOUT 1000
/* Size of a block for 100 ms of audio data. */
#define BLOCK_SIZE(_sample_rate, _number_of_channels) \
(BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)
/* Driver will allocate blocks from this slab to receive audio data into them.
* Application, after getting a given block from the driver and processing its
* data, needs to free that block.
*/
#define MAX_BLOCK_SIZE BLOCK_SIZE(MAX_SAMPLE_RATE, 2)
#define BLOCK_COUNT 4
static K_MEM_SLAB_DEFINE(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);
static int do_pdm_transfer(const struct device *dmic_dev,
struct dmic_cfg *cfg,
size_t block_count)
{
int ret;
LOG_INF("PCM output rate: %u, channels: %u",
cfg->streams[0].pcm_rate, cfg->channel.req_num_chan);
ret = dmic_configure(dmic_dev, cfg);
if (ret < 0) {
LOG_ERR("Failed to configure the driver: %d", ret);
return ret;
}
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
if (ret < 0) {
LOG_ERR("START trigger failed: %d", ret);
return ret;
}
for (int i = 0; i < block_count; ++i) {
void *buffer;
uint32_t size;
int ret;
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
if (ret < 0) {
LOG_ERR("%d - read failed: %d", i, ret);
return ret;
}
LOG_INF("%d - got buffer %p of %u bytes", i, buffer, size);
k_mem_slab_free(&mem_slab, &buffer);
}
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
if (ret < 0) {
LOG_ERR("STOP trigger failed: %d", ret);
return ret;
}
return ret;
}
void main(void)
{
const struct device *dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
int ret;
LOG_INF("DMIC sample");
if (!device_is_ready(dmic_dev)) {
LOG_ERR("%s is not ready", dmic_dev->name);
return;
}
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,
},
};
cfg.channel.req_num_chan = 1;
cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
cfg.streams[0].block_size =
BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
if (ret < 0) {
return;
}
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 = MAX_SAMPLE_RATE;
cfg.streams[0].block_size =
BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
if (ret < 0) {
return;
}
LOG_INF("Exiting");
}