55
66#include " globals.h"
77#include < SPI.h>
8+ #include < Adafruit_ZeroPDMSPI.h>
89
910#define MIN_PITCH_HZ 65
1011#define MAX_PITCH_HZ 1600
@@ -16,7 +17,6 @@ static float actualPlaybackRate;
1617// PDM mic allows 1.0 to 3.25 MHz max clock (2.4 typical).
1718// SPI native max is is 24 MHz, so available speeds are 12, 6, 3 MHz.
1819#define SPI_BITRATE 3000000
19- static SPISettings settings (SPI_BITRATE, LSBFIRST, SPI_MODE0);
2020// 3 MHz / 32 bits = 93,750 Hz interrupt frequency
2121// 2 interrupts/sample = 46,875 Hz audio sample rate
2222const float sampleRate = (float )SPI_BITRATE / 64.0 ;
@@ -26,29 +26,10 @@ const float sampleRate = (float)SPI_BITRATE / 64.0;
2626// Although SPI lib now has an option to get an SPI object's SERCOM number
2727// at run time, the interrupt handler MUST be declared at compile time...
2828// 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
3029#define PDM_SPI SPI2 // PDM mic SPI peripheral
3130#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);
5233
5334static float playbackRate = sampleRate;
5435static uint16_t *recBuf = NULL ;
@@ -65,16 +46,6 @@ volatile uint16_t voiceLastReading = 32768;
6546volatile uint16_t voiceMin = 32768 ;
6647volatile uint16_t voiceMax = 32768 ;
6748
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-
7849#define MOD_MIN 20 // Lowest supported modulation frequency (lower = more RAM use)
7950static uint8_t modWave = 0 ; // Modulation wave type (none, sine, square, tri, saw)
8051static uint8_t *modBuf = NULL ; // Modulation waveform buffer
@@ -119,37 +90,9 @@ bool voiceSetup(bool modEnable) {
11990 // If malloc fails, program will continue without modulation
12091 }
12192
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
15396
15497 return true ; // Success
15598}
@@ -180,9 +123,7 @@ float voicePitch(float p) {
180123// SET GAIN ----------------------------------------------------------------
181124
182125void 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
186127}
187128
188129// SET MODULATION ----------------------------------------------------------
@@ -226,119 +167,9 @@ void voiceMod(uint32_t freq, uint8_t waveform) {
226167
227168// INTERRUPT HANDLERS ------------------------------------------------------
228169
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-
231170void 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 )) {
342173 // So, the theory is, in the future some basic pitch detection could be
343174 // added right about here, which could be used to improve the seam
344175 // transitions in the playback interrupt (and possibly other things,
@@ -352,23 +183,22 @@ void PDM_SERCOM_HANDLER(void) {
352183 // project" code here, but it's pulled out for now for the sake of
353184 // getting something not-broken in folks' hands in a sensible timeframe.
354185 if (++recIndex >= recBufSize) recIndex = 0 ;
355- recBuf[recIndex] = adjusted ;
186+ recBuf[recIndex] = micReading ;
356187
357188 // Outside code can use the value of voiceLastReading if you want to
358189 // do an approximate live waveform display, or dynamic gain adjustment
359190 // based on mic input, or other stuff. This won't give you every single
360191 // sample in the recording buffer one-by-one sequentially...it's just
361192 // the last thing that was stored prior to whatever time you polled it,
362193 // but may still have some uses.
363- voiceLastReading = adjusted ;
194+ voiceLastReading = micReading ;
364195
365196 // Similarly, user code can extern these variables and monitor the
366197 // peak-to-peak range. They are never reset in the voice code itself,
367198 // 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 ;
370201 }
371- evenWord ^= 1 ;
372202}
373203
374204static void voiceOutCallback (void ) {
0 commit comments