33
44#if defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
55
6- #include " globals.h"
76#include < SPI.h>
87
98#define MIN_PITCH_HZ 65
109#define MAX_PITCH_HZ 1600
1110#define TYP_PITCH_HZ 175
1211
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+
1319// PDM mic allows 1.0 to 3.25 MHz max clock (2.4 typical).
1420// SPI native max is is 24 MHz, so available speeds are 12, 6, 3 MHz.
1521#define SPI_BITRATE 3000000
@@ -97,7 +103,6 @@ static int16_t playbackIndexJumped;
97103static uint16_t nextOut = 2048 ;
98104
99105float voicePitch (float p);
100- static void playCallback (void );
101106
102107// START PITCH SHIFT (no arguments) ----------------------------------------
103108
@@ -110,8 +115,8 @@ bool voiceSetup(bool modEnable) {
110115
111116 // Allocate buffer for voice modulation, if enabled
112117 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 ));
115120 // If malloc fails, program will continue without modulation
116121 }
117122
@@ -145,7 +150,37 @@ bool voiceSetup(bool modEnable) {
145150
146151 analogWriteResolution (12 );
147152
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
149184
150185 return true ; // Success
151186}
@@ -161,15 +196,14 @@ bool voiceSetup(bool modEnable) {
161196// adjustment (after appying constraints) will be returned.
162197float voicePitch (float p) {
163198 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;
168205 p = (actualPlaybackRate / sampleRate); // New pitch
169206 jumpThreshold = (int )(jump * p + 0.5 );
170-
171- arcada.timerCallback ((int )actualPlaybackRate, playCallback);
172-
173207 return p;
174208}
175209
@@ -189,12 +223,9 @@ void voiceGain(float g) {
189223void voiceMod (uint32_t freq, uint8_t waveform) {
190224 if (modBuf) { // Ignore if no modulation buffer allocated
191225 if (freq < MOD_MIN) freq = MOD_MIN;
192- /*
193- TO DO: FIX THIS NOW THAT USING ARCADA ZEROTIMER:
194226 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
196228 modLen = (int )(playbackRate / freq + 0.5 );
197- */
198229 if (modLen < 2 ) modLen = 2 ;
199230 if (waveform > 4 ) waveform = 4 ;
200231 modWave = waveform;
@@ -366,8 +397,10 @@ void PDM_SERCOM_HANDLER(void) {
366397 evenWord ^= 1 ;
367398}
368399
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+
371404 // Modulation is done on the output (rather than the input) because
372405 // pitch-shifting modulated input would cause weird waveform
373406 // discontinuities. This does require recalculating the modulation table
0 commit comments