2626
2727#include <stdint.h>
2828#include <string.h>
29+ #include <math.h>
2930
3031#include "py/gc.h"
3132#include "py/mperrno.h"
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+
4552void 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,
213231void 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
221237void 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+
242266static 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.
256287uint32_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
329360void common_hal_audiobusio_pdmin_record_to_file (audiobusio_pdmin_obj_t * self , uint8_t * buffer , uint32_t length ) {
0 commit comments