3
3
4
4
#if defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
5
5
6
- #include " globals.h"
7
6
#include < SPI.h>
8
7
9
8
#define MIN_PITCH_HZ 65
10
9
#define MAX_PITCH_HZ 1600
11
10
#define TYP_PITCH_HZ 175
12
11
12
+ // Playback timer stuff - use TC3 on MONSTER M4SK (no TC4 on this board)
13
+ #define TIMER TC3
14
+ #define TIMER_IRQN TC3_IRQn
15
+ #define TIMER_IRQ_HANDLER TC3_Handler
16
+ #define TIMER_GCLK_ID TC3_GCLK_ID
17
+ #define TIMER_GCM_ID GCM_TC2_TC3
18
+
13
19
// PDM mic allows 1.0 to 3.25 MHz max clock (2.4 typical).
14
20
// SPI native max is is 24 MHz, so available speeds are 12, 6, 3 MHz.
15
21
#define SPI_BITRATE 3000000
@@ -97,7 +103,6 @@ static int16_t playbackIndexJumped;
97
103
static uint16_t nextOut = 2048 ;
98
104
99
105
float voicePitch (float p);
100
- static void playCallback (void );
101
106
102
107
// START PITCH SHIFT (no arguments) ----------------------------------------
103
108
@@ -110,8 +115,8 @@ bool voiceSetup(bool modEnable) {
110
115
111
116
// Allocate buffer for voice modulation, if enabled
112
117
if (modEnable) {
113
- // Second 16.0 comes from min period in voicePitch()
114
- modBuf = (uint8_t *)malloc ((int )(48000000.0 /16.0 / 16 .0 / MOD_MIN + 0.5 ));
118
+ // 250 comes from min period in voicePitch()
119
+ modBuf = (uint8_t *)malloc ((int )(48000000.0 / 250 .0 / MOD_MIN + 0.5 ));
115
120
// If malloc fails, program will continue without modulation
116
121
}
117
122
@@ -145,7 +150,37 @@ bool voiceSetup(bool modEnable) {
145
150
146
151
analogWriteResolution (12 );
147
152
148
- voicePitch (1.0 ); // Set timer interval & callback
153
+ // Feed TIMER off GCLK1 (already set to 48 MHz by Arduino core)
154
+ GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN = 0 ; // Disable channel
155
+ while (GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN ); // Wait for disable
156
+ GCLK_PCHCTRL_Type pchctrl;
157
+ pchctrl.bit .GEN = GCLK_PCHCTRL_GEN_GCLK1_Val;
158
+ pchctrl.bit .CHEN = 1 ;
159
+ GCLK->PCHCTRL [TIMER_GCLK_ID].reg = pchctrl.reg ;
160
+ while (!GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN ); // Wait for enable
161
+
162
+ // Disable timer before configuring it
163
+ TIMER->COUNT16 .CTRLA .bit .ENABLE = 0 ;
164
+ while (TIMER->COUNT16 .SYNCBUSY .bit .ENABLE );
165
+
166
+ // 16-bit counter mode, 1:1 prescale, match-frequency generation mode
167
+ TIMER->COUNT16 .CTRLA .bit .MODE = TC_CTRLA_MODE_COUNT16;
168
+ TIMER->COUNT16 .CTRLA .bit .PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val;
169
+ TIMER->COUNT16 .WAVE .bit .WAVEGEN = TC_WAVE_WAVEGEN_MFRQ_Val;
170
+
171
+ TIMER->COUNT16 .CTRLBCLR .reg = TC_CTRLBCLR_DIR; // Count up
172
+ while (TIMER->COUNT16 .SYNCBUSY .bit .CTRLB );
173
+
174
+ voicePitch (1.0 ); // Set timer interval
175
+
176
+ TIMER->COUNT16 .INTENSET .reg = TC_INTENSET_OVF; // Overflow interrupt
177
+ NVIC_DisableIRQ (TIMER_IRQN);
178
+ NVIC_ClearPendingIRQ (TIMER_IRQN);
179
+ NVIC_SetPriority (TIMER_IRQN, 0 ); // Top priority
180
+ NVIC_EnableIRQ (TIMER_IRQN);
181
+
182
+ TIMER->COUNT16 .CTRLA .bit .ENABLE = 1 ; // Enable timer
183
+ while (TIMER->COUNT16 .SYNCBUSY .bit .ENABLE ); // Wait for it
149
184
150
185
return true ; // Success
151
186
}
@@ -161,15 +196,14 @@ bool voiceSetup(bool modEnable) {
161
196
// adjustment (after appying constraints) will be returned.
162
197
float voicePitch (float p) {
163
198
float desiredPlaybackRate = sampleRate * p;
164
- int32_t period = (int32_t )(48000000.0 / 16.0 / desiredPlaybackRate);
165
- if (period > 160 ) period = 160 ; // Hard limit is 65536, 160 is a practical limit
166
- else if (period < 16 ) period = 16 ; // Leave some cycles for IRQ handler
167
- float actualPlaybackRate = 48000000.0 / 16.0 / (float )period;
199
+ int32_t period = (int32_t )(48000000.0 / desiredPlaybackRate + 0.5 );
200
+ if (period > 2500 ) period = 2500 ; // Hard limit is 65536, 2.5K is a practical limit
201
+ else if (period < 250 ) period = 250 ; // Leave some cycles for IRQ handler
202
+ TIMER->COUNT16 .CC [0 ].reg = period - 1 ;
203
+ while (TIMER->COUNT16 .SYNCBUSY .bit .CC0 );
204
+ float actualPlaybackRate = 48000000.0 / (float )period;
168
205
p = (actualPlaybackRate / sampleRate); // New pitch
169
206
jumpThreshold = (int )(jump * p + 0.5 );
170
-
171
- arcada.timerCallback ((int )actualPlaybackRate, playCallback);
172
-
173
207
return p;
174
208
}
175
209
@@ -189,12 +223,9 @@ void voiceGain(float g) {
189
223
void voiceMod (uint32_t freq, uint8_t waveform) {
190
224
if (modBuf) { // Ignore if no modulation buffer allocated
191
225
if (freq < MOD_MIN) freq = MOD_MIN;
192
- /*
193
- TO DO: FIX THIS NOW THAT USING ARCADA ZEROTIMER:
194
226
uint16_t period = TIMER->COUNT16 .CC [0 ].reg + 1 ; // Audio out timer ticks
195
- float playbackRate = 48000000.0 / 16.0 / (float)period; // Audio out samples/sec
227
+ float playbackRate = 48000000.0 / (float )period; // Audio out samples/sec
196
228
modLen = (int )(playbackRate / freq + 0.5 );
197
- */
198
229
if (modLen < 2 ) modLen = 2 ;
199
230
if (waveform > 4 ) waveform = 4 ;
200
231
modWave = waveform;
@@ -366,8 +397,10 @@ void PDM_SERCOM_HANDLER(void) {
366
397
evenWord ^= 1 ;
367
398
}
368
399
369
- // Playback timer callback
370
- static void playCallback (void ) {
400
+ // Playback timer interrupt
401
+ void TIMER_IRQ_HANDLER (void ) {
402
+ TIMER->COUNT16 .INTFLAG .reg = TC_INTFLAG_OVF;
403
+
371
404
// Modulation is done on the output (rather than the input) because
372
405
// pitch-shifting modulated input would cause weird waveform
373
406
// discontinuities. This does require recalculating the modulation table
0 commit comments