Skip to content

Commit 618c8d3

Browse files
committed
samples/shields: Add example code to test x_nucleo_cca02m1 shield
This sample is made to demonstrate use of shield x-nucleo-cca02m1. It uses the default shield soldering configuration, which means both microphones in stereo mode. It has been tested using nucleo F411RE board. Signed-off-by: Armando Visconti <[email protected]>
1 parent 6d76af3 commit 618c8d3

File tree

7 files changed

+370
-0
lines changed

7 files changed

+370
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
cmake_minimum_required(VERSION 3.13.1)
3+
4+
# This sample is specific to x_nucleo_cca02m1 shield. Enforce -DSHIELD option
5+
set(SHIELD x_nucleo_cca02m1)
6+
7+
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
8+
project(x_nucleo_cca02m1)
9+
10+
FILE(GLOB app_sources src/*.c)
11+
target_sources(app PRIVATE ${app_sources})
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
.. _x-nucleo-cca02m1-sample:
2+
3+
X-NUCLEO-CCA02M1: Digital MEMS microphones SHIELD for STM32 Nucleo
4+
##################################################################
5+
6+
Overview
7+
********
8+
This sample enables the two digital MEMS microphones on X-NUCLEO-CCA02M1
9+
shields
10+
11+
This sample provides an example of how to acquire audio through
12+
the two digital MEMS microphones on X-NUCLEO-CCA02M1 shield.
13+
The microphone generates a PDM stream which is acquired through I2S.
14+
The PDM stream is then converted to PCM using the OpenPDMFilter external
15+
library.
16+
17+
Requirements
18+
************
19+
20+
This sample communicates over I2C with the X-NUCLEO-CCA02M1 shield
21+
stacked on a board with an Arduino connector. The board's I2C must be
22+
configured for the I2C Arduino connector (both for pin muxing
23+
and device tree). See for example the :ref:`nucleo_f411re_board` board
24+
source code:
25+
26+
- :zephyr_file:`boards/arm/nucleo_f411re/nucleo_f411re.dts`
27+
- :zephyr_file:`boards/arm/nucleo_f411re/pinmux.c`
28+
29+
References
30+
**********
31+
32+
- X-NUCLEO-CCA02M1: https://www.st.com/en/ecosystems/x-nucleo-cca02m1.html
33+
34+
Building and Running
35+
********************
36+
37+
This sample runs with X-NUCLEO-CCA02M1 stacked on any board with a matching
38+
Arduino connector. For this example, we use a :ref:`nucleo_f411re_board` board.
39+
40+
.. zephyr-app-commands::
41+
:zephyr-app: samples/shields/x_nucleo_cca02m1
42+
:board: nucleo_f411re
43+
:goals: build
44+
:compact:
45+
46+
Sample Output
47+
=============
48+
49+
The example acquires one second of audio and prints out the PCM stream on COM port.
50+
The acquisition starts immediately after the reset button is pressed.
51+
52+
The characteristics of the PCM audio are hardcoded in the example:
53+
54+
- 16KHz sample rate
55+
- 16 bits per sample
56+
- 2 channel (stereo)
57+
58+
One second of acquisition at a 2 channels 16KHz sampling rate yields 32,000 16-bit samples.
59+
The microphone PDM requested clock should lead the MP34DT05 driver to select an
60+
oversampling/decimation factor to result in approximately a 2MHz bit clock.
61+
62+
See PCM and PDM configuration in file :zephyr_file:`samples/shields/x_nucleo_cca02m1/src/main.c`.
63+
64+
.. note:: It is possible to change the AUDIO_FREQ to 32000 acquiring only 500 ms.
65+
66+
At the end of the acquisition the PCM data will be printed on the terminal
67+
emulator in either binary or ASCII format. The output is controlled by the
68+
:c:macro:`PCM_OUTPUT_IN_ASCII` macro, off by default, in
69+
:zephyr_file:`samples/shields/x_nucleo_cca02m1/src/main.c`.
70+
71+
Binary PCM Output
72+
-----------------
73+
74+
The Nucleo F411RE board presents itself to the host
75+
as a USB CDC class, and will use ``/dev/ttyACM0``
76+
device for communication. The ``/dev/ttyACM0`` port
77+
must be configured in raw mode to avoid having
78+
special characters (such as :kbd:`CNTL-Z` or :kbd:`CNTL-D`)
79+
processed or 'cooked' out.
80+
81+
.. code-block:: console
82+
83+
stty -F /dev/ttyACM0 115200 raw
84+
cat /dev/ttyACM0 > /tmp/sound.raw
85+
86+
.. note:: In case the character 0x0a is interpreted as NL and an 0x0d (CR) is added,
87+
you may need to remove it::
88+
89+
dos2unix -f /tmp/sound.raw
90+
91+
ASCII PCM Output
92+
----------------
93+
94+
It is also possible to recompile and to have PCM output in ASCII, which needs
95+
to be converted to binary later on. The output format is the following:
96+
97+
.. code-block:: console
98+
99+
-- start
100+
0xfbe0,
101+
0xfbf0,
102+
0xfc0c,
103+
0xfc24,
104+
0xfc3c,
105+
0xfc4c,
106+
0xfc68,
107+
0xfc48,
108+
109+
[...]
110+
111+
0xfb98,
112+
0xfb98,
113+
0xfbb8,
114+
0xfbac,
115+
0xfbc4,
116+
0xfbe8,
117+
0xfbf4,
118+
-- end
119+
120+
Play PCM Audio
121+
--------------
122+
123+
Now that we have a binary PCM file (say sound.raw), you can use,
124+
for example, the audacity open source editor/player to load and play it.
125+
126+
Use the 'Import->Raw Data' menu to load the sound.raw file as
127+
signed 16 bit PCM, Little Endian, stereo format @16KHz:
128+
129+
.. image:: img/audio_import.png
130+
:width: 266px
131+
:height: 287px
132+
:align: center
133+
:alt: audio_import
134+
135+
After the file is imported you can analyze and play the one second audio file:
136+
137+
.. image:: img/audio_file.png
138+
:width: 1627px
139+
:height: 505px
140+
:align: center
141+
:alt: audio_file
69.1 KB
Loading
27.7 KB
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CONFIG_STDOUT_CONSOLE=y
2+
CONFIG_PRINTK=y
3+
CONFIG_I2S=y
4+
CONFIG_GPIO=y
5+
CONFIG_AUDIO=y
6+
CONFIG_AUDIO_DMIC=y
7+
CONFIG_AUDIO_MPXXDTYY=y
8+
9+
# When on-board microphone is used the DMA must run w/o things
10+
# that could slow it down. So DMA logging must be completely
11+
# disabled and DMA interrupts must run at maximum priority.
12+
13+
CONFIG_DMA=y
14+
CONFIG_DMA_0_IRQ_PRI=0
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
sample:
2+
name: X-NUCLEO-CCA02M1 sensor shield
3+
tests:
4+
test:
5+
harness: shield
6+
tags: shield
7+
depends_on: arduino_i2s
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright (c) 2019 STMicroelectronics
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <zephyr.h>
9+
#include <misc/printk.h>
10+
11+
#include <gpio.h>
12+
#include <led.h>
13+
14+
#include <audio/dmic.h>
15+
16+
/* uncomment if you want PCM output in ascii */
17+
/*#define PCM_OUTPUT_IN_ASCII 1 */
18+
19+
#define STEREO 1
20+
21+
#define AUDIO_FREQ 16000
22+
#define CHAN_SIZE 16
23+
#ifdef STEREO
24+
#define PCM_BLK_SIZE_MS (((AUDIO_FREQ/1000) * sizeof(s16_t)) * 2)
25+
#else
26+
#define PCM_BLK_SIZE_MS ((AUDIO_FREQ/1000) * sizeof(s16_t))
27+
#endif
28+
29+
#define NUM_MS 1000
30+
31+
K_MEM_SLAB_DEFINE(rx_mem_slab, PCM_BLK_SIZE_MS, NUM_MS, 1);
32+
33+
struct pcm_stream_cfg mic_streams = {
34+
.pcm_rate = AUDIO_FREQ,
35+
.pcm_width = CHAN_SIZE,
36+
.block_size = PCM_BLK_SIZE_MS,
37+
.mem_slab = &rx_mem_slab,
38+
};
39+
40+
struct dmic_cfg cfg = {
41+
.io = {
42+
/* requesting a pdm freq around 2MHz */
43+
.min_pdm_clk_freq = 1800000,
44+
.max_pdm_clk_freq = 2500000,
45+
},
46+
.streams = &mic_streams,
47+
.channel = {
48+
#ifdef STEREO
49+
.req_num_chan = 2,
50+
#else
51+
.req_num_chan = 1,
52+
#endif
53+
},
54+
};
55+
56+
void *rx_block[NUM_MS];
57+
size_t rx_size = PCM_BLK_SIZE_MS;
58+
59+
#ifdef STEREO
60+
#include <stm32f4xx_ll_tim.h>
61+
62+
/*
63+
* MIC_CLK_x2: PB4 (TIM3_CH1) - IN
64+
* MIC_CLK_NUCLEO: PB5 (TIM3_CH2) - OUT
65+
*/
66+
static int AUDIO_IN_Timer_Init(void)
67+
{
68+
TIM_TypeDef *tim3 = (TIM_TypeDef *)DT_TIM_STM32_3_BASE_ADDRESS;
69+
LL_TIM_IC_InitTypeDef sICConfig;
70+
LL_TIM_InitTypeDef sConfig;
71+
LL_TIM_OC_InitTypeDef ocConfig;
72+
73+
/* Enable TIM3 peripheral clock */
74+
LL_APB1_GRP1_EnableClock(DT_TIM_STM32_3_CLOCK_BITS);
75+
76+
/* Configure the Input: channel_1 */
77+
LL_TIM_IC_StructInit(&sICConfig);
78+
sICConfig.ICPolarity = LL_TIM_IC_POLARITY_RISING;
79+
sICConfig.ICActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
80+
sICConfig.ICPrescaler = LL_TIM_ICPSC_DIV1;
81+
sICConfig.ICFilter = 0;
82+
if (LL_TIM_IC_Init(tim3, LL_TIM_CHANNEL_CH1, &sICConfig) == ERROR) {
83+
return -1;
84+
}
85+
86+
/* Configure TIM3 in Gated Slave mode for the external trigger
87+
* (Filtered Timer Input 1) */
88+
LL_TIM_SetSlaveMode(tim3, LL_TIM_CLOCKSOURCE_EXT_MODE1);
89+
LL_TIM_SetTriggerInput(tim3, LL_TIM_TS_TI1FP1);
90+
91+
/* Initialize TIM3 counter */
92+
LL_TIM_StructInit(&sConfig);
93+
sConfig.Prescaler = 0;
94+
sConfig.ClockDivision = 0;
95+
sConfig.Autoreload = 1;
96+
sConfig.CounterMode = LL_TIM_COUNTERMODE_UP;
97+
sConfig.RepetitionCounter = 0;
98+
if (LL_TIM_Init(tim3, &sConfig) == ERROR) {
99+
return -1;
100+
}
101+
102+
LL_TIM_SetTriggerOutput(tim3, LL_TIM_TRGO_RESET);
103+
104+
/* Initialize TIM3 peripheral in PWM mode */
105+
LL_TIM_OC_StructInit(&ocConfig);
106+
ocConfig.OCMode = LL_TIM_OCMODE_PWM1;
107+
ocConfig.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
108+
if (LL_TIM_OC_Init(tim3, LL_TIM_CHANNEL_CH2, &ocConfig) == ERROR) {
109+
return -1;
110+
}
111+
112+
LL_TIM_OC_SetCompareCH2(tim3, 1);
113+
114+
/* Enable TIM3 */
115+
LL_TIM_CC_EnableChannel(tim3, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH2);
116+
LL_TIM_EnableCounter(tim3);
117+
118+
return 0;
119+
}
120+
#endif
121+
122+
void main(void)
123+
{
124+
int i;
125+
u32_t ms;
126+
127+
printk("x_nucleo_cca02m1 test!!\n");
128+
129+
int ret;
130+
131+
struct device *mic_dev = device_get_binding(DT_ST_MPXXDTYY_0_LABEL);
132+
133+
if (!mic_dev) {
134+
printk("Could not get pointer to %s device\n",
135+
DT_ST_MPXXDTYY_0_LABEL);
136+
return;
137+
}
138+
139+
#ifdef STEREO
140+
ret = AUDIO_IN_Timer_Init();
141+
if (ret < 0) {
142+
printk("timer init error\n");
143+
return;
144+
}
145+
#endif
146+
147+
ret = dmic_configure(mic_dev, &cfg);
148+
if (ret < 0) {
149+
printk("microphone configuration error\n");
150+
return;
151+
}
152+
153+
ret = dmic_trigger(mic_dev, DMIC_TRIGGER_START);
154+
if (ret < 0) {
155+
printk("microphone start trigger error\n");
156+
return;
157+
}
158+
159+
/* Acquire microphone audio */
160+
for (ms = 0; ms < NUM_MS; ms++) {
161+
ret = dmic_read(mic_dev, 0, &rx_block[ms], &rx_size, 2000);
162+
if (ret < 0) {
163+
printk("microphone audio read error\n");
164+
return;
165+
}
166+
}
167+
168+
/* print PCM stream */
169+
#ifdef PCM_OUTPUT_IN_ASCII
170+
printk("-- start\n");
171+
int j;
172+
173+
for (i = 0; i < NUM_MS; i++) {
174+
u16_t *pcm_out = rx_block[i];
175+
176+
for (j = 0; j < rx_size/2; j++) {
177+
printk("0x%04x,\n", pcm_out[j]);
178+
}
179+
}
180+
printk("-- end\n");
181+
#else
182+
unsigned char pcm_l, pcm_h;
183+
int j;
184+
185+
for (i = 0; i < NUM_MS; i++) {
186+
u16_t *pcm_out = rx_block[i];
187+
188+
for (j = 0; j < rx_size/2; j++) {
189+
pcm_l = (char)(pcm_out[j] & 0xFF);
190+
pcm_h = (char)((pcm_out[j] >> 8) & 0xFF);
191+
192+
z_impl_k_str_out(&pcm_l, 1);
193+
z_impl_k_str_out(&pcm_h, 1);
194+
}
195+
}
196+
#endif
197+
}

0 commit comments

Comments
 (0)