5
5
6
6
#include " globals.h"
7
7
#include < SPI.h>
8
+ #include < Adafruit_ZeroPDMSPI.h>
8
9
9
10
#define MIN_PITCH_HZ 65
10
11
#define MAX_PITCH_HZ 1600
@@ -16,7 +17,6 @@ static float actualPlaybackRate;
16
17
// PDM mic allows 1.0 to 3.25 MHz max clock (2.4 typical).
17
18
// SPI native max is is 24 MHz, so available speeds are 12, 6, 3 MHz.
18
19
#define SPI_BITRATE 3000000
19
- static SPISettings settings (SPI_BITRATE, LSBFIRST, SPI_MODE0);
20
20
// 3 MHz / 32 bits = 93,750 Hz interrupt frequency
21
21
// 2 interrupts/sample = 46,875 Hz audio sample rate
22
22
const float sampleRate = (float )SPI_BITRATE / 64.0 ;
@@ -26,29 +26,10 @@ const float sampleRate = (float)SPI_BITRATE / 64.0;
26
26
// Although SPI lib now has an option to get an SPI object's SERCOM number
27
27
// at run time, the interrupt handler MUST be declared at compile time...
28
28
// so it's necessary to know the SERCOM # ahead of time anyway, oh well.
29
- #define PDM_SERCOM SERCOM3 // PDM mic SPI SERCOM on MONSTER M4SK
30
29
#define PDM_SPI SPI2 // PDM mic SPI peripheral
31
30
#define PDM_SERCOM_HANDLER SERCOM3_0_Handler
32
- #define PDM_SERCOM_IRQn SERCOM3_0_IRQn // _0_IRQn is DRE interrupt
33
-
34
- static Sercom *sercom;
35
- static volatile uint32_t *dataReg;
36
-
37
- Sercom * const sercomList[] = {
38
- SERCOM0, SERCOM1, SERCOM2, SERCOM3,
39
- #if defined(SERCOM4)
40
- SERCOM4,
41
- #endif
42
- #if defined(SERCOM5)
43
- SERCOM5,
44
- #endif
45
- #if defined(SERCOM6)
46
- SERCOM6,
47
- #endif
48
- #if defined(SERCOM7)
49
- SERCOM7,
50
- #endif
51
- };
31
+
32
+ Adafruit_ZeroPDMSPI pdmspi (&PDM_SPI);
52
33
53
34
static float playbackRate = sampleRate;
54
35
static uint16_t *recBuf = NULL ;
@@ -65,16 +46,6 @@ volatile uint16_t voiceLastReading = 32768;
65
46
volatile uint16_t voiceMin = 32768 ;
66
47
volatile uint16_t voiceMax = 32768 ;
67
48
68
- #define DC_PERIOD 4096 // Recalculate DC offset this many samplings
69
- // DC_PERIOD does NOT need to be a power of 2, but might save a few cycles.
70
- // PDM rate is 46875, so 4096 = 11.44 times/sec
71
- static uint16_t dcCounter = 0 ; // Rolls over every DC_PERIOD samples
72
- static uint32_t dcSum = 0 ; // Accumulates DC_PERIOD samples
73
- static uint16_t dcOffsetPrior = 32768 ; // DC offset interpolates linearly
74
- static uint16_t dcOffsetNext = 32768 ; // between these two values
75
-
76
- static uint16_t micGain = 256 ; // 1:1
77
-
78
49
#define MOD_MIN 20 // Lowest supported modulation frequency (lower = more RAM use)
79
50
static uint8_t modWave = 0 ; // Modulation wave type (none, sine, square, tri, saw)
80
51
static uint8_t *modBuf = NULL ; // Modulation waveform buffer
@@ -119,37 +90,9 @@ bool voiceSetup(bool modEnable) {
119
90
// If malloc fails, program will continue without modulation
120
91
}
121
92
122
- // Set up PDM microphone input -------------------------------------------
123
-
124
- PDM_SPI.begin ();
125
- PDM_SPI.beginTransaction (settings); // this SPI transaction is left open
126
- sercom = sercomList[PDM_SPI.getSercomIndex ()];
127
- dataReg = PDM_SPI.getDataRegister ();
128
-
129
- // Enabling 32-bit SPI must be done AFTER SPI.begin() which
130
- // resets registers. But SPI.CTRLC (where 32-bit mode is set) is
131
- // enable-protected, so peripheral must be disabled temporarily...
132
- sercom->SPI .CTRLA .bit .ENABLE = 0 ; // Disable SPI
133
- while (sercom->SPI .SYNCBUSY .bit .ENABLE ); // Wait for disable
134
- sercom->SPI .CTRLC .bit .DATA32B = 1 ; // Enable 32-bit mode
135
- sercom->SPI .CTRLA .bit .ENABLE = 1 ; // Re-enable SPI
136
- while (sercom->SPI .SYNCBUSY .bit .ENABLE ); // Wait for enable
137
- // 4-byte word length is implicit in 32-bit mode,
138
- // no need to set up LENGTH register.
139
-
140
- sercom->SPI .INTENSET .bit .DRE = 1 ; // Data-register-empty interrupt
141
- NVIC_DisableIRQ (PDM_SERCOM_IRQn);
142
- NVIC_ClearPendingIRQ (PDM_SERCOM_IRQn);
143
- NVIC_SetPriority (PDM_SERCOM_IRQn, 0 ); // Top priority
144
- NVIC_EnableIRQ (PDM_SERCOM_IRQn);
145
-
146
- sercom->SPI .DATA .bit .DATA = 0 ; // Kick off SPI free-run
147
-
148
- // Set up analog output & timer ------------------------------------------
149
-
150
- analogWriteResolution (12 );
151
-
152
- voicePitch (1.0 ); // Set timer interval
93
+ pdmspi.begin (sampleRate); // Set up PDM microphone
94
+ analogWriteResolution (12 ); // Set up analog output
95
+ voicePitch (1.0 ); // Set timer interval
153
96
154
97
return true ; // Success
155
98
}
@@ -180,9 +123,7 @@ float voicePitch(float p) {
180
123
// SET GAIN ----------------------------------------------------------------
181
124
182
125
void voiceGain (float g) {
183
- if (g >= (65535.0 /256.0 )) micGain = 65535 ;
184
- else if (g < 0.0 ) micGain = 0 ;
185
- else micGain = (uint16_t )(g * 256.0 + 0.5 );
126
+ pdmspi.setMicGain (g); // Handles its own clipping
186
127
}
187
128
188
129
// SET MODULATION ----------------------------------------------------------
@@ -226,119 +167,9 @@ void voiceMod(uint32_t freq, uint8_t waveform) {
226
167
227
168
// INTERRUPT HANDLERS ------------------------------------------------------
228
169
229
- static uint16_t const sincfilter[64 ] = { 0 , 2 , 9 , 21 , 39 , 63 , 94 , 132 , 179 , 236 , 302 , 379 , 467 , 565 , 674 , 792 , 920 , 1055 , 1196 , 1341 , 1487 , 1633 , 1776 , 1913 , 2042 , 2159 , 2263 , 2352 , 2422 , 2474 , 2506 , 2516 , 2506 , 2474 , 2422 , 2352 , 2263 , 2159 , 2042 , 1913 , 1776 , 1633 , 1487 , 1341 , 1196 , 1055 , 920 , 792 , 674 , 565 , 467 , 379 , 302 , 236 , 179 , 132 , 94 , 63 , 39 , 21 , 9 , 2 , 0 , 0 };
230
-
231
170
void PDM_SERCOM_HANDLER (void ) {
232
- static bool evenWord = 1 ; // Alternates 0/1 with each interrupt call
233
- static uint32_t sumTemp = 0 ; // Temp. value used across 2 interrupt calls
234
- // Shenanigans: SPI data read/write are shadowed...even though it appears
235
- // the same register here, it's legit to write new MOSI value before
236
- // reading the received MISO value from the same location. This helps
237
- // avoid a gap between words...provides a steady stream of bits.
238
- *dataReg = 0 ; // Write clears DRE flag, starts next xfer
239
- uint32_t sample = *dataReg; // Read last-received word
240
-
241
- uint32_t sum = 0 ; // local var = register = faster than sumTemp
242
- if (evenWord) { // Even-numbered 32-bit word...
243
- // At default speed and optimization settings (120 MHz -Os), the PDM-
244
- // servicing interrupt consumes about 12.5% of CPU time. Though this
245
- // code looks bulky, it's actually reasonably efficient (sincfilter[] is
246
- // const, so these compile down to constants, there is no array lookup,
247
- // any any zero-value element refs will be removed by the compiler).
248
- // Tested MANY methods and this was hard to beat. One managed just under
249
- // 10% load, but required 4KB of tables...not worth it for small boost.
250
- // Can get an easy boost with overclock and optimizer tweaks.
251
- if (sample & 0x00000001 ) sum += sincfilter[ 0 ];
252
- if (sample & 0x00000002 ) sum += sincfilter[ 1 ];
253
- if (sample & 0x00000004 ) sum += sincfilter[ 2 ];
254
- if (sample & 0x00000008 ) sum += sincfilter[ 3 ];
255
- if (sample & 0x00000010 ) sum += sincfilter[ 4 ];
256
- if (sample & 0x00000020 ) sum += sincfilter[ 5 ];
257
- if (sample & 0x00000040 ) sum += sincfilter[ 6 ];
258
- if (sample & 0x00000080 ) sum += sincfilter[ 7 ];
259
- if (sample & 0x00000100 ) sum += sincfilter[ 8 ];
260
- if (sample & 0x00000200 ) sum += sincfilter[ 9 ];
261
- if (sample & 0x00000400 ) sum += sincfilter[10 ];
262
- if (sample & 0x00000800 ) sum += sincfilter[11 ];
263
- if (sample & 0x00001000 ) sum += sincfilter[12 ];
264
- if (sample & 0x00002000 ) sum += sincfilter[13 ];
265
- if (sample & 0x00004000 ) sum += sincfilter[14 ];
266
- if (sample & 0x00008000 ) sum += sincfilter[15 ];
267
- if (sample & 0x00010000 ) sum += sincfilter[16 ];
268
- if (sample & 0x00020000 ) sum += sincfilter[17 ];
269
- if (sample & 0x00040000 ) sum += sincfilter[18 ];
270
- if (sample & 0x00080000 ) sum += sincfilter[19 ];
271
- if (sample & 0x00100000 ) sum += sincfilter[20 ];
272
- if (sample & 0x00200000 ) sum += sincfilter[21 ];
273
- if (sample & 0x00400000 ) sum += sincfilter[22 ];
274
- if (sample & 0x00800000 ) sum += sincfilter[23 ];
275
- if (sample & 0x01000000 ) sum += sincfilter[24 ];
276
- if (sample & 0x02000000 ) sum += sincfilter[25 ];
277
- if (sample & 0x04000000 ) sum += sincfilter[26 ];
278
- if (sample & 0x08000000 ) sum += sincfilter[27 ];
279
- if (sample & 0x10000000 ) sum += sincfilter[28 ];
280
- if (sample & 0x20000000 ) sum += sincfilter[29 ];
281
- if (sample & 0x40000000 ) sum += sincfilter[30 ];
282
- if (sample & 0x80000000 ) sum += sincfilter[31 ];
283
- sumTemp = sum; // Copy register to static var for next call
284
- } else {
285
- if (sample & 0x00000001 ) sum += sincfilter[32 ];
286
- if (sample & 0x00000002 ) sum += sincfilter[33 ];
287
- if (sample & 0x00000004 ) sum += sincfilter[34 ];
288
- if (sample & 0x00000008 ) sum += sincfilter[35 ];
289
- if (sample & 0x00000010 ) sum += sincfilter[36 ];
290
- if (sample & 0x00000020 ) sum += sincfilter[37 ];
291
- if (sample & 0x00000040 ) sum += sincfilter[38 ];
292
- if (sample & 0x00000080 ) sum += sincfilter[39 ];
293
- if (sample & 0x00000100 ) sum += sincfilter[40 ];
294
- if (sample & 0x00000200 ) sum += sincfilter[41 ];
295
- if (sample & 0x00000400 ) sum += sincfilter[42 ];
296
- if (sample & 0x00000800 ) sum += sincfilter[43 ];
297
- if (sample & 0x00001000 ) sum += sincfilter[44 ];
298
- if (sample & 0x00002000 ) sum += sincfilter[45 ];
299
- if (sample & 0x00004000 ) sum += sincfilter[46 ];
300
- if (sample & 0x00008000 ) sum += sincfilter[47 ];
301
- if (sample & 0x00010000 ) sum += sincfilter[48 ];
302
- if (sample & 0x00020000 ) sum += sincfilter[49 ];
303
- if (sample & 0x00040000 ) sum += sincfilter[50 ];
304
- if (sample & 0x00080000 ) sum += sincfilter[51 ];
305
- if (sample & 0x00100000 ) sum += sincfilter[52 ];
306
- if (sample & 0x00200000 ) sum += sincfilter[53 ];
307
- if (sample & 0x00400000 ) sum += sincfilter[54 ];
308
- if (sample & 0x00800000 ) sum += sincfilter[55 ];
309
- if (sample & 0x01000000 ) sum += sincfilter[56 ];
310
- if (sample & 0x02000000 ) sum += sincfilter[57 ];
311
- if (sample & 0x04000000 ) sum += sincfilter[58 ];
312
- if (sample & 0x08000000 ) sum += sincfilter[59 ];
313
- if (sample & 0x10000000 ) sum += sincfilter[60 ];
314
- if (sample & 0x20000000 ) sum += sincfilter[61 ];
315
- if (sample & 0x40000000 ) sum += sincfilter[62 ];
316
- if (sample & 0x80000000 ) sum += sincfilter[63 ];
317
- sum += sumTemp; // Add static var from last call
318
-
319
- // 'sum' is new raw audio value -- process it --------------------------
320
-
321
- uint16_t dcOffset;
322
-
323
- dcSum += sum; // Accumulate long-term average for DC offset correction
324
- if (++dcCounter < DC_PERIOD) {
325
- // Interpolate between dcOffsetPrior and dcOffsetNext
326
- dcOffset = dcOffsetPrior + (dcOffsetNext - dcOffsetPrior) * dcCounter / DC_PERIOD;
327
- } else {
328
- // End of period reached, move 'next' to 'previous,' calc new 'next' from avg
329
- dcOffsetPrior = dcOffset = dcOffsetNext;
330
- dcOffsetNext = dcSum / DC_PERIOD;
331
- dcCounter = dcSum = 0 ;
332
- }
333
-
334
- // Adjust raw reading by DC offset to center (ish) it, scale by mic gain
335
- int32_t adjusted = ((int32_t )sum - dcOffset) * micGain / 256 ;
336
-
337
- // Go back to uint16_t space and clip to 16-bit range
338
- adjusted += 32768 ;
339
- if (adjusted > 65535 ) adjusted = 65535 ;
340
- else if (adjusted < 0 ) adjusted = 0 ;
341
-
171
+ uint16_t micReading = 0 ;
172
+ if (pdmspi.decimateFilterWord (&micReading, true )) {
342
173
// So, the theory is, in the future some basic pitch detection could be
343
174
// added right about here, which could be used to improve the seam
344
175
// transitions in the playback interrupt (and possibly other things,
@@ -352,23 +183,22 @@ void PDM_SERCOM_HANDLER(void) {
352
183
// project" code here, but it's pulled out for now for the sake of
353
184
// getting something not-broken in folks' hands in a sensible timeframe.
354
185
if (++recIndex >= recBufSize) recIndex = 0 ;
355
- recBuf[recIndex] = adjusted ;
186
+ recBuf[recIndex] = micReading ;
356
187
357
188
// Outside code can use the value of voiceLastReading if you want to
358
189
// do an approximate live waveform display, or dynamic gain adjustment
359
190
// based on mic input, or other stuff. This won't give you every single
360
191
// sample in the recording buffer one-by-one sequentially...it's just
361
192
// the last thing that was stored prior to whatever time you polled it,
362
193
// but may still have some uses.
363
- voiceLastReading = adjusted ;
194
+ voiceLastReading = micReading ;
364
195
365
196
// Similarly, user code can extern these variables and monitor the
366
197
// peak-to-peak range. They are never reset in the voice code itself,
367
198
// it's the duty of the user code to reset both to 32768 periodically.
368
- if (adjusted < voiceMin) voiceMin = adjusted ;
369
- else if (adjusted > voiceMax) voiceMax = adjusted ;
199
+ if (micReading < voiceMin) voiceMin = micReading ;
200
+ else if (micReading > voiceMax) voiceMax = micReading ;
370
201
}
371
- evenWord ^= 1 ;
372
202
}
373
203
374
204
static void voiceOutCallback (void ) {
0 commit comments