26
26
27
27
#include <stdint.h>
28
28
#include <string.h>
29
+ #include <math.h>
29
30
30
31
#include "py/gc.h"
31
32
#include "py/mperrno.h"
42
43
#include "shared_dma.h"
43
44
#include "tick.h"
44
45
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
+
45
52
void pdmin_reset (void ) {
46
53
while (I2S -> SYNCBUSY .reg & I2S_SYNCBUSY_ENABLE ) {}
47
54
I2S -> INTENCLR .reg = I2S_INTENCLR_MASK ;
@@ -96,8 +103,8 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
96
103
mp_raise_RuntimeError ("Unable to allocate audio DMA block counter." );
97
104
}
98
105
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." );
101
108
}
102
109
103
110
// 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,
112
119
config_clock_unit .clock .mck_out_enable = false;
113
120
114
121
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
+ }
117
129
118
130
config_clock_unit .frame .number_slots = 2 ;
119
131
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 ;
121
133
122
134
config_clock_unit .frame .frame_sync .width = I2S_FRAME_SYNC_WIDTH_SLOT ;
123
135
@@ -141,6 +153,10 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
141
153
i2s_serializer_set_config (& self -> i2s_instance , self -> serializer , & config_serializer );
142
154
i2s_enable (& self -> i2s_instance );
143
155
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
+
144
160
self -> bytes_per_sample = oversample >> 3 ;
145
161
self -> bit_depth = bit_depth ;
146
162
}
@@ -154,6 +170,8 @@ void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self) {
154
170
return ;
155
171
}
156
172
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 );
157
175
i2s_reset (& self -> i2s_instance );
158
176
reset_pin (self -> clock_pin -> pin );
159
177
reset_pin (self -> data_pin -> pin );
@@ -213,74 +231,82 @@ static void setup_dma(audiobusio_pdmin_obj_t* self, uint32_t length,
213
231
void start_dma (audiobusio_pdmin_obj_t * self ) {
214
232
dma_start_transfer_job (& audio_dma );
215
233
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 );
218
234
I2S -> DATA [1 ].reg = I2S -> DATA [1 ].reg ;
219
235
}
220
236
221
237
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.
227
239
tc_stop_counter (MP_STATE_VM (audiodma_block_counter ));
228
240
dma_abort_job (& audio_dma );
229
241
}
230
242
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
240
262
};
241
263
264
+ #define REPEAT_16_TIMES (X ) X X X X X X X X X X X X X X X X
265
+
242
266
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 ;
249
279
}
250
- pdm <<= 1 ;
251
- }
280
+ )
252
281
}
253
- return sample ;
282
+ return running_sum ;
254
283
}
255
284
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.
256
287
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 ;
265
291
// 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 ;
268
294
uint32_t first_buffer [words_per_buffer ];
269
295
uint32_t second_buffer [words_per_buffer ];
270
296
271
297
COMPILER_ALIGNED (16 ) DmacDescriptor second_descriptor ;
272
298
273
- setup_dma (self , length , & second_descriptor , words_per_buffer ,
299
+ setup_dma (self , output_buffer_length , & second_descriptor , words_per_buffer ,
274
300
words_per_sample , first_buffer , second_buffer );
275
301
276
302
start_dma (self );
277
303
278
304
// Record
279
305
uint32_t buffers_processed = 0 ;
280
- uint32_t total_bytes = 0 ;
306
+ uint32_t values_output = 0 ;
281
307
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 ) {
284
310
// Wait for the next buffer to fill
285
311
while (tc_get_count_value (MP_STATE_VM (audiodma_block_counter )) == buffers_processed ) {
286
312
#ifdef MICROPY_VM_HOOK_LOOP
@@ -290,40 +316,45 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
290
316
if (tc_get_count_value (MP_STATE_VM (audiodma_block_counter )) != (buffers_processed + 1 )) {
291
317
break ;
292
318
}
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 ;
299
324
DmacDescriptor * descriptor = audio_dma .descriptor ;
300
325
if (buffers_processed % 2 == 1 ) {
301
326
buffer = second_buffer ;
302
327
descriptor = & second_descriptor ;
303
328
}
304
329
// 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 );
307
336
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 ;
313
341
}
342
+ values_output ++ ;
314
343
}
315
344
buffers_processed ++ ;
316
345
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 ;
320
351
descriptor -> DESCADDR .reg = 0 ;
321
352
}
322
353
}
323
354
324
355
stop_dma (self );
325
356
326
- return total_bytes ;
357
+ return values_output ;
327
358
}
328
359
329
360
void common_hal_audiobusio_pdmin_record_to_file (audiobusio_pdmin_obj_t * self , uint8_t * buffer , uint32_t length ) {
0 commit comments