Skip to content

Commit ad9db01

Browse files
committed
Implements PDMIn for STM32L4 using the SAI peripheral and decimation/filtering in software.
1 parent ae2bbbb commit ad9db01

File tree

17 files changed

+1502
-3
lines changed

17 files changed

+1502
-3
lines changed

locale/circuitpython.pot

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3714,10 +3714,20 @@ msgid "offset out of bounds"
37143714
msgstr ""
37153715

37163716
#: ports/nrf/common-hal/audiobusio/PDMIn.c
3717+
#: ports/stm/common-hal/audiobusio/PDMIn.c
37173718
msgid "only bit_depth=16 is supported"
37183719
msgstr ""
37193720

3721+
#: ports/stm/common-hal/audiobusio/PDMIn.c
3722+
msgid "only mono is supported"
3723+
msgstr ""
3724+
3725+
#: ports/stm/common-hal/audiobusio/PDMIn.c
3726+
msgid "only oversample=64 is supported"
3727+
msgstr ""
3728+
37203729
#: ports/nrf/common-hal/audiobusio/PDMIn.c
3730+
#: ports/stm/common-hal/audiobusio/PDMIn.c
37213731
msgid "only sample_rate=16000 is supported"
37223732
msgstr ""
37233733

ports/stm/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,17 @@ SRC_C += \
234234
peripherals/stm32$(MCU_SERIES_LOWER)/$(MCU_VARIANT_LOWER)/periph.c \
235235
packages/$(MCU_PACKAGE).c
236236

237+
ifneq ($(CIRCUITPY_AUDIOBUSIO_PDMIN),0)
238+
SRC_C += \
239+
common-hal/audiobusio/MEMS_Audio.c \
240+
common-hal/audiobusio/MEMS_Audio_ll_stm32l4.c \
241+
common-hal/audiobusio/OpenPDMFilter.c
242+
243+
SRC_STM32 += \
244+
$(HAL_DIR)/Src/stm32$(MCU_SERIES_LOWER)xx_hal_sai.c
245+
246+
endif
247+
237248
ifneq ($(CIRCUITPY_USB),0)
238249
SRC_C += lib/tinyusb/src/portable/st/synopsys/dcd_synopsys.c
239250
endif

ports/stm/boards/swan_r5/mpconfigboard.mk

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,9 @@ CIRCUITPY_PULSEIO = 1
4141
CIRCUITPY_PWMIO = 1
4242
CIRCUITPY_AUDIOPWMIO = 1
4343
CIRCUITPY_CANIO = 0
44-
CIRCUITPY_AUDIOBUSIO = 0
4544
CIRCUITPY_I2CTARGET = 0
4645
# Requires SPI, PulseIO (stub ok):
47-
CIRCUITPY_DISPLAYIO = 0
46+
CIRCUITPY_DISPLAYIO = 1
4847

4948
# These modules are implemented in shared-module/ - they can be included in
5049
# any port once their prerequisites in common-hal are complete.
@@ -71,5 +70,8 @@ CIRCUITPY_BUSDEVICE = 0
7170
CIRCUITPY_KEYPAD = 1
7271
CIRCUITPY_RGBMATRIX = 0
7372
CIRCUITPY_RTC = 1
74-
7573
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
74+
CIRCUITPY_ENABLE_MPY_NATIVE = 1
75+
CIRCUITPY_AUDIOBUSIO = 1
76+
CIRCUITPY_AUDIOBUSIO_I2SOUT = 0
77+
CIRCUITPY_AUDIOBUSIO_PDMIN = 1

ports/stm/boards/swan_r5/pins.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
129129
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
130130
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
131131
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
132+
133+
{ MP_ROM_QSTR(MP_QSTR_MICROPHONE_CLOCK), MP_ROM_PTR(&pin_PA03) },
134+
{ MP_ROM_QSTR(MP_QSTR_MICROPHONE_DATA), MP_ROM_PTR(&pin_PC03) },
132135
};
133136
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Although IS2Out is not enabled on the STM32L4 family, this file is still required for the build to pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Although IS2Out is not enabled on the STM32L4 family, this file is still required for the build to pass
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <assert.h>
2+
#include <stm32l4xx_hal.h>
3+
4+
#include "MEMS_Audio.h"
5+
#include "MEMS_Audio_ll.h"
6+
7+
static void default_pcm_data_available(MemsAudio *audio, pcm_sample_t *pcmSamples, size_t pcmLength) {
8+
}
9+
10+
11+
12+
/**
13+
* @brief Initializes the MemsAudio instance. Only one instance can be initialized and used at a given time.
14+
*
15+
* @param audio
16+
* @return mems_audio_err_t
17+
*/
18+
mems_audio_err_t mems_audio_init(MemsAudio *audio) {
19+
if (!audio->pcm_data_available) {
20+
audio->pcm_data_available = default_pcm_data_available;
21+
}
22+
return mems_audio_ll_init(audio);
23+
}
24+
25+
/**
26+
* @brief Uninitializes the MemsAudio instance.
27+
*
28+
* @param audio
29+
* @return mems_audio_err_t
30+
*/
31+
mems_audio_err_t mems_audio_uninit(MemsAudio *audio) {
32+
return mems_audio_ll_uninit(audio);
33+
}
34+
35+
/**
36+
* @brief Asynchronously records audio.
37+
*
38+
* @param audio
39+
* @param pdmBuffer
40+
* @param pdmBufferLength
41+
* @return mems_audio_err_t
42+
*/
43+
mems_audio_err_t mems_audio_record(MemsAudio *audio) {
44+
return mems_audio_ll_record(audio);
45+
}
46+
47+
/**
48+
* @brief Pause recording audio.
49+
*/
50+
mems_audio_err_t mems_audio_pause(MemsAudio *audio) {
51+
return mems_audio_ll_pause(audio);
52+
}
53+
54+
/**
55+
* @brief Resume recording audio.
56+
*
57+
* @param audio
58+
* @return mems_audio_err_t
59+
*/
60+
mems_audio_err_t mems_audio_resume(MemsAudio *audio) {
61+
return mems_audio_ll_resume(audio);
62+
}
63+
64+
/**
65+
* @brief Stop recording audio and
66+
*
67+
* @param audio
68+
* @return mems_audio_err_t
69+
*/
70+
mems_audio_err_t mems_audio_stop(MemsAudio *audio) {
71+
return mems_audio_ll_stop(audio);
72+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#ifndef _MEMS_AUDIO_H_
2+
#define _MEMS_AUDIO_H_
3+
4+
#include <stdint.h>
5+
#include <stddef.h>
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
12+
/**
13+
* @brief How many milliseconds of audio can fit in the audio buffer(s).
14+
* Interrupts for recieved data fire at half this duration / twice the frequency.
15+
*/
16+
#ifndef MEMS_AUDIO_MS_BUFFER
17+
#define MEMS_AUDIO_MS_BUFFER (1)
18+
#endif
19+
20+
21+
/**
22+
* @brief The number of bits per sample of the PCM output
23+
*/
24+
#define PCM_OUT_RESOLUTION 16
25+
26+
/**
27+
* @brief The output frequency of PCM samples in Hz.
28+
*/
29+
#define PCM_OUT_SAMPLING_FREQUENCY 16000
30+
31+
/**
32+
* @brief type for describing error conditions.
33+
*/
34+
typedef int32_t mems_audio_err_t;
35+
36+
/**
37+
* @brief The datatype that holds an output PCM sample.
38+
*/
39+
typedef int16_t pcm_sample_t;
40+
_Static_assert(PCM_OUT_RESOLUTION==16, "Output PCM resolution must be 16-bits");
41+
42+
43+
typedef enum {
44+
MEMS_AUDIO_OK = 0,
45+
MEMS_AUDIO_ERROR_ALREADY_INITIALIZED = -1,
46+
MEMS_AUDIO_ERROR_NOT_INITIALIZED = -2
47+
} mems_audio_err_enum_t;
48+
49+
#define IS_MEMS_AUDIO_ERROR(e) (e)
50+
#define CHECK_MEMS_AUDIO_ERROR(e) { if (IS_MEMS_AUDIO_ERROR(e)) return e; }
51+
#define CHECK_MEMS_AUDIO_INITIALIZED(x) { if (!x) return MEMS_AUDIO_ERROR_NOT_INITIALIZED; }
52+
53+
typedef struct MemsAudio_t MemsAudio;
54+
55+
/**
56+
* @brief Callback informing that PCM samples are available for processing.
57+
*/
58+
typedef void (*pcm_data_available_t)(MemsAudio* audio, pcm_sample_t* pcmSamples, size_t pcmLength);
59+
60+
/**
61+
* @brief MemsAudio manages the filter, buffers and callbacks used to capture PDM audio samples and convert to PCM.
62+
*
63+
*/
64+
typedef struct MemsAudio_t {
65+
66+
/**
67+
* @brief The buffer to store PCM audio samples
68+
*/
69+
volatile pcm_sample_t* volatile pcmOutputBuffer;
70+
71+
/**
72+
* @brief The length of the PCM buffer. SHould be at least MEMS_AUDIO_PCM_BUFFER_LENGTH
73+
*/
74+
volatile size_t pcmOutputBufferLength;
75+
76+
/**
77+
* @brief Optional callback for when PCM data is available.
78+
*/
79+
pcm_data_available_t pcm_data_available;
80+
81+
void* audioImpl;
82+
void* userData;
83+
} MemsAudio;
84+
85+
86+
mems_audio_err_t mems_audio_init(MemsAudio* audio);
87+
88+
/**
89+
* @brief Uninitializes the MemsAudio instance.
90+
*
91+
* @param audio
92+
* @return mems_audio_err_t
93+
*/
94+
mems_audio_err_t mems_audio_uninit(MemsAudio* audio);
95+
96+
/**
97+
* @brief Asynchronously records audio.
98+
*
99+
* @param audio
100+
* @param pdmBuffer
101+
* @param pdmBufferLength
102+
* @return mems_audio_err_t
103+
*/
104+
mems_audio_err_t mems_audio_record(MemsAudio* audio);
105+
106+
/**
107+
* @brief Pause recording audio.
108+
*/
109+
mems_audio_err_t mems_audio_pause(MemsAudio* audio);
110+
111+
/**
112+
* @brief Resume recording audio.
113+
*
114+
* @param audio
115+
* @return mems_audio_err_t
116+
*/
117+
mems_audio_err_t mems_audio_resume(MemsAudio* audio);
118+
119+
/**
120+
* @brief Stop recording audio and
121+
*
122+
* @param audio
123+
* @return mems_audio_err_t
124+
*/
125+
mems_audio_err_t mems_audio_stop(MemsAudio* audio);
126+
127+
#ifdef __cplusplus
128+
}
129+
#endif
130+
131+
132+
#endif // _MEMS_AUDIO_H_
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#ifndef _MEMS_AUDIO_LL_H_
2+
#define _MEMS_AUDIO_LL_H_
3+
4+
#include "MEMS_Audio.h"
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
11+
mems_audio_err_t mems_audio_ll_init(MemsAudio *audio);
12+
mems_audio_err_t mems_audio_ll_uninit(MemsAudio *audio);
13+
14+
/**
15+
* @brief Asynchronously records audio.
16+
*
17+
* @param audio
18+
* @param pdmBuffer
19+
* @param pdmBufferLength
20+
* @return mems_audio_err_t
21+
*/
22+
mems_audio_err_t mems_audio_ll_record(MemsAudio *audio);
23+
24+
/**
25+
* @brief Pause recording audio.
26+
*/
27+
mems_audio_err_t mems_audio_ll_pause(MemsAudio *audio);
28+
29+
/**
30+
* @brief Resume recording audio.
31+
*
32+
* @param audio
33+
* @return mems_audio_err_t
34+
*/
35+
mems_audio_err_t mems_audio_ll_resume(MemsAudio *audio);
36+
37+
/**
38+
* @brief Stop recording audio and
39+
*
40+
* @param audio
41+
* @return mems_audio_err_t
42+
*/
43+
mems_audio_err_t mems_audio_ll_stop(MemsAudio *audio);
44+
45+
#ifdef __cplusplus
46+
}
47+
#endif
48+
49+
50+
#endif // _MEMS_AUDIO_LL_H_

0 commit comments

Comments
 (0)