Skip to content

Commit 43650b6

Browse files
dhalberttannewt
authored andcommitted
Fix PDMIn MEMS microphone support (adafruit#479)
Changes: * New faster filter loop, by @ladyada. New filter coefficients as well. * Turn on microphone clock when PDMIn object is created, and run it all the time, so the user code doesn't have to wait for microphone startup, which can be 10ms or even 100ms. * Wait for microphone startup when PDMIn is first created, based on new optional parameter microphone_startup in seconds (takes a float). * record() returns number of samples actually recorded, so you can see if it's not keeping up. * Fix buffer overflow errors when buffer size was not a multiple of 16 or something like that. * Tweak a few peripheral settings. * Minimum sampling frequency is now 16kHZ or so, because 8kHz runs microphone at only 0.5MHz, which is too slow for many mics. Note: I tried 128x oversampling instead of 64x, but the code cannot keep up at 24kHz or above sampling. 128x would reduce the high-frequency noise by 6db.
1 parent 3c49f53 commit 43650b6

File tree

2 files changed

+129
-77
lines changed
  • atmel-samd/common-hal/audiobusio
  • shared-bindings/audiobusio

2 files changed

+129
-77
lines changed

atmel-samd/common-hal/audiobusio/PDMIn.c

+92-61
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include <stdint.h>
2828
#include <string.h>
29+
#include <math.h>
2930

3031
#include "py/gc.h"
3132
#include "py/mperrno.h"
@@ -42,6 +43,12 @@
4243
#include "shared_dma.h"
4344
#include "tick.h"
4445

46+
#define OVERSAMPLING 64
47+
#define SAMPLES_PER_BUFFER 32
48+
49+
// MEMS microphones must be clocked at at least 1MHz.
50+
#define MIN_MIC_CLOCK 1000000
51+
4552
void pdmin_reset(void) {
4653
while (I2S->SYNCBUSY.reg & I2S_SYNCBUSY_ENABLE) {}
4754
I2S->INTENCLR.reg = I2S_INTENCLR_MASK;
@@ -96,8 +103,8 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
96103
mp_raise_RuntimeError("Unable to allocate audio DMA block counter.");
97104
}
98105

99-
if (!(bit_depth == 16 || bit_depth == 8) || !mono || oversample != 64) {
100-
mp_raise_NotImplementedError("Only 8 or 16 bit mono with 64 oversample is supported.");
106+
if (!(bit_depth == 16 || bit_depth == 8) || !mono || oversample != OVERSAMPLING) {
107+
mp_raise_NotImplementedError("Only 8 or 16 bit mono with " MP_STRINGIFY(OVERSAMPLING) "x oversampling is supported.");
101108
}
102109

103110
// TODO(tannewt): Use the DPLL to get a more precise sampling rate.
@@ -112,12 +119,17 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
112119
config_clock_unit.clock.mck_out_enable = false;
113120

114121
config_clock_unit.clock.sck_src = I2S_SERIAL_CLOCK_SOURCE_MCKDIV;
115-
config_clock_unit.clock.sck_div = 8000000 / frequency / oversample;
116-
self->frequency = 8000000 / config_clock_unit.clock.sck_div / oversample;
122+
uint32_t clock_divisor = (uint32_t) roundf( 8000000.0f / frequency / oversample);
123+
config_clock_unit.clock.sck_div = clock_divisor;
124+
float mic_clock_freq = 8000000.0f / clock_divisor;
125+
self->frequency = mic_clock_freq / oversample;
126+
if (mic_clock_freq < MIN_MIC_CLOCK || clock_divisor == 0 || clock_divisor > 255) {
127+
mp_raise_ValueError("sampling frequency out of range");
128+
}
117129

118130
config_clock_unit.frame.number_slots = 2;
119131
config_clock_unit.frame.slot_size = I2S_SLOT_SIZE_16_BIT;
120-
config_clock_unit.frame.data_delay = I2S_DATA_DELAY_1;
132+
config_clock_unit.frame.data_delay = I2S_DATA_DELAY_0;
121133

122134
config_clock_unit.frame.frame_sync.width = I2S_FRAME_SYNC_WIDTH_SLOT;
123135

@@ -141,6 +153,10 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
141153
i2s_serializer_set_config(&self->i2s_instance, self->serializer, &config_serializer);
142154
i2s_enable(&self->i2s_instance);
143155

156+
// Run the serializer all the time. This eliminates startup delay for the microphone.
157+
i2s_clock_unit_enable(&self->i2s_instance, self->clock_unit);
158+
i2s_serializer_enable(&self->i2s_instance, self->serializer);
159+
144160
self->bytes_per_sample = oversample >> 3;
145161
self->bit_depth = bit_depth;
146162
}
@@ -154,6 +170,8 @@ void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self) {
154170
return;
155171
}
156172
i2s_disable(&self->i2s_instance);
173+
i2s_serializer_disable(&self->i2s_instance, self->serializer);
174+
i2s_clock_unit_disable(&self->i2s_instance, self->clock_unit);
157175
i2s_reset(&self->i2s_instance);
158176
reset_pin(self->clock_pin->pin);
159177
reset_pin(self->data_pin->pin);
@@ -213,74 +231,82 @@ static void setup_dma(audiobusio_pdmin_obj_t* self, uint32_t length,
213231
void start_dma(audiobusio_pdmin_obj_t* self) {
214232
dma_start_transfer_job(&audio_dma);
215233
tc_start_counter(MP_STATE_VM(audiodma_block_counter));
216-
i2s_clock_unit_enable(&self->i2s_instance, self->clock_unit);
217-
i2s_serializer_enable(&self->i2s_instance, self->serializer);
218234
I2S->DATA[1].reg = I2S->DATA[1].reg;
219235
}
220236

221237
void stop_dma(audiobusio_pdmin_obj_t* self) {
222-
// Turn off the I2S clock and serializer. Peripheral is still enabled.
223-
i2s_serializer_disable(&self->i2s_instance, self->serializer);
224-
i2s_clock_unit_disable(&self->i2s_instance, self->clock_unit);
225-
226-
// Shutdown the DMA
238+
// Shutdown the DMA: serializer keeps running.
227239
tc_stop_counter(MP_STATE_VM(audiodma_block_counter));
228240
dma_abort_job(&audio_dma);
229241
}
230242

231-
static const uint16_t sinc_filter[64] = {
232-
0, 1, 6, 16, 29, 49, 75, 108,
233-
149, 200, 261, 334, 418, 514, 622, 742,
234-
872, 1012, 1161, 1315, 1472, 1631, 1787, 1938,
235-
2081, 2212, 2329, 2429, 2509, 2568, 2604, 2616,
236-
2604, 2568, 2509, 2429, 2329, 2212, 2081, 1938,
237-
1787, 1631, 1472, 1315, 1161, 1012, 872, 742,
238-
622, 514, 418, 334, 261, 200, 149, 108,
239-
75, 49, 29, 16, 6, 1, 0, 0
243+
// a windowed sinc filter for 44 khz, 64 samples
244+
//
245+
// This filter is good enough to use for lower sample rates as
246+
// well. It does not increase the noise enough to be a problem.
247+
//
248+
// In the long run we could use a fast filter like this to do the
249+
// decimation and initial filtering in real time, filtering to a
250+
// higher sample rate than specified. Then after the audio is
251+
// recorded, a more expensive filter non-real-time filter could be
252+
// used to down-sample and low-pass.
253+
uint16_t sinc_filter [OVERSAMPLING] = {
254+
0, 2, 9, 21, 39, 63, 94, 132,
255+
179, 236, 302, 379, 467, 565, 674, 792,
256+
920, 1055, 1196, 1341, 1487, 1633, 1776, 1913,
257+
2042, 2159, 2263, 2352, 2422, 2474, 2506, 2516,
258+
2506, 2474, 2422, 2352, 2263, 2159, 2042, 1913,
259+
1776, 1633, 1487, 1341, 1196, 1055, 920, 792,
260+
674, 565, 467, 379, 302, 236, 179, 132,
261+
94, 63, 39, 21, 9, 2, 0, 0
240262
};
241263

264+
#define REPEAT_16_TIMES(X) X X X X X X X X X X X X X X X X
265+
242266
static uint16_t filter_sample(uint32_t pdm_samples[4]) {
243-
uint16_t sample = 0;
244-
for (uint8_t i = 0; i < 4; i++) {
245-
uint16_t pdm = pdm_samples[i] & 0xffff;
246-
for (uint8_t j = 0; j < 16; j++) {
247-
if ((pdm & 0x8000) != 0) {
248-
sample += sinc_filter[i * 16 + j];
267+
uint16_t running_sum = 0;
268+
const uint16_t *filter_ptr = sinc_filter;
269+
for (uint8_t i = 0; i < OVERSAMPLING/16; i++) {
270+
// The sample is 16-bits right channel in the upper two bytes and 16-bits left channel
271+
// in the lower two bytes.
272+
// We just ignore the upper bits
273+
uint32_t pdm_sample = pdm_samples[i];
274+
REPEAT_16_TIMES( {
275+
if (pdm_sample & 0x8000) {
276+
running_sum += *filter_ptr++;
277+
}
278+
pdm_sample <<= 1;
249279
}
250-
pdm <<= 1;
251-
}
280+
)
252281
}
253-
return sample;
282+
return running_sum;
254283
}
255284

285+
// output_buffer may be a byte buffer or a halfword buffer.
286+
// output_buffer_length is the number of slots, not the number of bytes.
256287
uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
257-
uint16_t* output_buffer, uint32_t length) {
258-
// Write the wave file header.
259-
260-
// We allocate two 256 byte buffers on the stack to use for double buffering.
261-
// Our oversample rate is 64 (bits) so each buffer produces 32 samples.
262-
// TODO(tannewt): Can the compiler optimize better if we fix the size of
263-
// these buffers?
264-
uint8_t samples_per_buffer = 32;
288+
uint16_t* output_buffer, uint32_t output_buffer_length) {
289+
// We allocate two buffers on the stack to use for double buffering.
290+
const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER;
265291
// For every word we record, we throw away 2 bytes of a phantom second channel.
266-
uint8_t words_per_sample = self->bytes_per_sample / 2;
267-
uint8_t words_per_buffer = samples_per_buffer * words_per_sample;
292+
const uint8_t words_per_sample = self->bytes_per_sample / 2;
293+
const uint8_t words_per_buffer = samples_per_buffer * words_per_sample;
268294
uint32_t first_buffer[words_per_buffer];
269295
uint32_t second_buffer[words_per_buffer];
270296

271297
COMPILER_ALIGNED(16) DmacDescriptor second_descriptor;
272298

273-
setup_dma(self, length, &second_descriptor, words_per_buffer,
299+
setup_dma(self, output_buffer_length, &second_descriptor, words_per_buffer,
274300
words_per_sample, first_buffer, second_buffer);
275301

276302
start_dma(self);
277303

278304
// Record
279305
uint32_t buffers_processed = 0;
280-
uint32_t total_bytes = 0;
306+
uint32_t values_output = 0;
281307

282-
uint64_t start_ticks = ticks_ms;
283-
while (total_bytes < length) {
308+
uint32_t remaining_samples_needed = output_buffer_length;
309+
while (values_output < output_buffer_length) {
284310
// Wait for the next buffer to fill
285311
while (tc_get_count_value(MP_STATE_VM(audiodma_block_counter)) == buffers_processed) {
286312
#ifdef MICROPY_VM_HOOK_LOOP
@@ -290,40 +316,45 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
290316
if (tc_get_count_value(MP_STATE_VM(audiodma_block_counter)) != (buffers_processed + 1)) {
291317
break;
292318
}
293-
// Throw away the first ~10ms of data because thats during mic start up.
294-
if (ticks_ms - start_ticks < 10) {
295-
buffers_processed++;
296-
continue;
297-
}
298-
uint32_t* buffer = first_buffer;
319+
320+
// The mic is running all the time, so we don't need to wait the usual 10msec or 100msec
321+
// for it to start up.
322+
323+
uint32_t *buffer = first_buffer;
299324
DmacDescriptor* descriptor = audio_dma.descriptor;
300325
if (buffers_processed % 2 == 1) {
301326
buffer = second_buffer;
302327
descriptor = &second_descriptor;
303328
}
304329
// Decimate and filter the last buffer
305-
int32_t samples_gathered = descriptor->BTCNT.reg / words_per_sample;
306-
for (uint16_t i = 0; i < samples_gathered; i++) {
330+
uint32_t samples_gathered = descriptor->BTCNT.reg / words_per_sample;
331+
// Don't run off the end of output buffer. Process only as many as needed.
332+
uint32_t samples_to_process = min(remaining_samples_needed, samples_gathered);
333+
for (uint32_t i = 0; i < samples_to_process; i++) {
334+
// Call filter_sample just one place so it can be inlined.
335+
uint16_t value = filter_sample(buffer + i * words_per_sample);
307336
if (self->bit_depth == 8) {
308-
((uint8_t*) output_buffer)[total_bytes] = filter_sample(buffer + i * words_per_sample) >> 8;
309-
total_bytes += 1;
310-
} else if (self->bit_depth == 16) {
311-
output_buffer[total_bytes / 2] = filter_sample(buffer + i * words_per_sample);
312-
total_bytes += 2;
337+
// Truncate to 8 bits.
338+
((uint8_t*) output_buffer)[values_output] = value >> 8;
339+
} else {
340+
output_buffer[values_output] = value;
313341
}
342+
values_output++;
314343
}
315344
buffers_processed++;
316345

317-
if (length - total_bytes < samples_per_buffer) {
318-
descriptor->BTCNT.reg = (length - total_bytes) * words_per_sample;
319-
descriptor->DSTADDR.reg = ((uint32_t) buffer) + (length - total_bytes) * self->bytes_per_sample;
346+
// See if we need to transfer less than a full buffer for the remaining needed samples.
347+
remaining_samples_needed = output_buffer_length - values_output;
348+
if (remaining_samples_needed > 0 && remaining_samples_needed < samples_per_buffer) {
349+
descriptor->BTCNT.reg = remaining_samples_needed;
350+
descriptor->DSTADDR.reg = ((uint32_t) buffer) + remaining_samples_needed * words_per_sample;
320351
descriptor->DESCADDR.reg = 0;
321352
}
322353
}
323354

324355
stop_dma(self);
325356

326-
return total_bytes;
357+
return values_output;
327358
}
328359

329360
void common_hal_audiobusio_pdmin_record_to_file(audiobusio_pdmin_obj_t* self, uint8_t* buffer, uint32_t length) {

shared-bindings/audiobusio/PDMIn.c

+37-16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "lib/utils/context_manager_helpers.h"
3030
#include "py/binary.h"
31+
#include "py/mphal.h"
3132
#include "py/objproperty.h"
3233
#include "py/runtime.h"
3334
#include "shared-bindings/microcontroller/Pin.h"
@@ -41,19 +42,24 @@
4142
//|
4243
//| PDMIn can be used to record an input audio signal on a given set of pins.
4344
//|
44-
//| .. class:: PDMIn(clock_pin, data_pin, \*, frequency=8000, bit_depth=8, mono=True, oversample=64)
45+
//| .. class:: PDMIn(clock_pin, data_pin, \*, frequency=16000, bit_depth=8, mono=True, oversample=64, startup_delay=0.11)
4546
//|
4647
//| Create a PDMIn object associated with the given pins. This allows you to
4748
//| record audio signals from the given pins. Individual ports may put further
4849
//| restrictions on the recording parameters.
4950
//|
5051
//| :param ~microcontroller.Pin clock_pin: The pin to output the clock to
5152
//| :param ~microcontroller.Pin data_pin: The pin to read the data from
52-
//| :param int frequency: Target frequency of the resulting samples. Check `frequency` for real value.
53+
//| :param int frequency: Target frequency of the resulting samples. Check `frequency` for actual value.
54+
//| Minimum frequency is about 16000 Hz.
5355
//| :param int bit_depth: Final number of bits per sample. Must be divisible by 8
5456
//| :param bool mono: True when capturing a single channel of audio, captures two channels otherwise
5557
//| :param int oversample: Number of single bit samples to decimate into a final sample. Must be divisible by 8
58+
//| :param float startup_delay: seconds to wait after starting microphone clock
59+
//| to allow microphone to turn on. Most require only 0.01s; some require 0.1s. Longer is safer.
60+
//| Must be in range 0.0-1.0 seconds.
5661
//|
62+
5763
//| Record 8-bit unsigned samples to buffer::
5864
//|
5965
//| import audiobusio
@@ -79,15 +85,19 @@
7985
//| mic.record(b, len(b))
8086
//|
8187
STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
82-
enum { ARG_frequency, ARG_bit_depth, ARG_mono, ARG_oversample };
88+
enum { ARG_frequency, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay };
8389
mp_map_t kw_args;
8490
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
8591
static const mp_arg_t allowed_args[] = {
86-
{ MP_QSTR_frequency, MP_ARG_INT, {.u_int = 8000} },
87-
{ MP_QSTR_bit_depth, MP_ARG_INT, {.u_int = 8} },
88-
{ MP_QSTR_mono, MP_ARG_BOOL,{.u_bool = true} },
89-
{ MP_QSTR_oversample, MP_ARG_INT, {.u_int = 64} },
92+
{ MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16000} },
93+
{ MP_QSTR_bit_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
94+
{ MP_QSTR_mono, MP_ARG_KW_ONLY | MP_ARG_BOOL,{.u_bool = true} },
95+
{ MP_QSTR_oversample, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
96+
{ MP_QSTR_startup_delay, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
9097
};
98+
// Default microphone startup delay is 110msecs. Have seen mics that need 100 msecs plus a bit.
99+
static const float STARTUP_DELAY_DEFAULT = 0.110F;
100+
91101
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
92102
mp_arg_parse_all(n_args - 2, pos_args + 2, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
93103

@@ -116,8 +126,18 @@ STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar
116126
}
117127
bool mono = args[ARG_mono].u_bool;
118128

129+
float startup_delay = (args[ARG_startup_delay].u_obj == MP_OBJ_NULL)
130+
? STARTUP_DELAY_DEFAULT
131+
: mp_obj_get_float(args[ARG_startup_delay].u_obj);
132+
if (startup_delay < 0.0 || startup_delay > 1.0) {
133+
mp_raise_ValueError("Microphone startup delay must be in range 0.0 to 1.0");
134+
}
135+
119136
common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, frequency,
120-
bit_depth, mono, oversample);
137+
bit_depth, mono, oversample);
138+
139+
// Wait for the microphone to start up. Some start in 10 msecs; some take as much as 100 msecs.
140+
mp_hal_delay_ms(startup_delay * 1000);
121141

122142
return MP_OBJ_FROM_PTR(self);
123143
}
@@ -160,33 +180,34 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_pdmin___exit___obj, 4, 4,
160180
//| audio at the given rate. For internal flash, writing all 1s to the file
161181
//| before recording is recommended to speed up writes.
162182
//|
183+
//| :return: The number of samples recorded. If this is less than `destination_length`,
184+
//| some samples were missed due to processing time.
185+
//|
163186
STATIC mp_obj_t audiobusio_pdmin_obj_record(mp_obj_t self_obj, mp_obj_t destination, mp_obj_t destination_length) {
164187
audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_obj);
165188
raise_error_if_deinited(common_hal_audiobusio_pdmin_deinited(self));
166-
if (!MP_OBJ_IS_SMALL_INT(destination_length)) {
167-
mp_raise_TypeError("destination_length must be int");
189+
if (!MP_OBJ_IS_SMALL_INT(destination_length) || MP_OBJ_SMALL_INT_VALUE(destination_length) < 0) {
190+
mp_raise_TypeError("destination_length must be an int >= 0");
168191
}
169192
uint32_t length = MP_OBJ_SMALL_INT_VALUE(destination_length);
170193

171194
mp_buffer_info_t bufinfo;
172195
if (MP_OBJ_IS_TYPE(destination, &fatfs_type_fileio)) {
173196
mp_raise_NotImplementedError("");
174197
} else if (mp_get_buffer(destination, &bufinfo, MP_BUFFER_WRITE)) {
175-
if (bufinfo.len < length) {
176-
mp_raise_ValueError("Target buffer cannot hold destination_length bytes.");
198+
if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) {
199+
mp_raise_ValueError("Destination capacity is smaller than destination_length.");
177200
}
178201
uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self);
179202
if (bufinfo.typecode != 'H' && bit_depth == 16) {
180203
mp_raise_ValueError("destination buffer must be an array of type 'H' for bit_depth = 16");
181204
} else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) {
182205
mp_raise_ValueError("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8");
183206
}
184-
length *= bit_depth / 8;
207+
// length is the buffer length in slots, not bytes.
185208
uint32_t length_written =
186209
common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length);
187-
if (length_written != length) {
188-
mp_printf(&mp_plat_print, "length mismatch %d %d\n", length_written, length);
189-
}
210+
return MP_OBJ_NEW_SMALL_INT(length_written);
190211
}
191212
return mp_const_none;
192213
}

0 commit comments

Comments
 (0)