Skip to content

Commit afc2dec

Browse files
committed
2 parents 325428c + 9a01da0 commit afc2dec

File tree

3 files changed

+20
-187
lines changed

3 files changed

+20
-187
lines changed

M4_Eyes/HeatSensor.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#if 0 // Change to 1 to enable this code (referenced by user_watch.cpp)
2+
// CORRESPONDING LINE IN user_watch.cpp MUST ALSO BE ENABLED!
3+
14
/* Read the IR sensor and try to figure out where the heat is located.
25
*/
3-
46
#include "HeatSensor.h"
57

68
#include <Wire.h>
@@ -107,4 +109,5 @@ void loop() {
107109

108110
delay(200);
109111
}
110-
*/
112+
*/
113+
#endif

M4_Eyes/pdmvoice.cpp

Lines changed: 13 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
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
2222
const 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

5334
static float playbackRate = sampleRate;
5435
static uint16_t *recBuf = NULL;
@@ -65,16 +46,6 @@ volatile uint16_t voiceLastReading = 32768;
6546
volatile uint16_t voiceMin = 32768;
6647
volatile 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)
7950
static uint8_t modWave = 0; // Modulation wave type (none, sine, square, tri, saw)
8051
static 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

182125
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
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-
231170
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)) {
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

374204
static void voiceOutCallback(void) {

M4_Eyes/user_watch.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#if 0 // Change to 0 to disable this code (must enable ONE user*.cpp only!)
1+
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
2+
// CORRESPONDING LINE IN HeatSensor.cpp MUST ALSO BE ENABLED!
23

34
#include "globals.h"
45
#include "heatSensor.h"
56

6-
77
// For heat sensing
88
HeatSensor heatSensor;
99

0 commit comments

Comments
 (0)