forked from sensorium/Mozzi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMozziGuts_impl_AVR.hpp
377 lines (334 loc) · 11.7 KB
/
MozziGuts_impl_AVR.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
* MozziGuts.cpp
*
* Copyright 2012 Tim Barrass.
*
* This file is part of Mozzi.
*
* Mozzi by Tim Barrass is licensed under a Creative Commons
* Attribution-NonCommercial-ShareAlike 4.0 International License.
*
*/
#include "FrequencyTimer2.h"
#include "TimerOne.h"
#if (F_CPU != 16000000)
#warning \
"Mozzi has been tested with a cpu clock speed of 16MHz on Arduino! Results may vary with other speeds."
#endif
////// BEGIN analog input code ////////
#define MOZZI_FAST_ANALOG_IMPLEMENTED
extern uint8_t analog_reference;
#define getADCReading() ADC /* officially (ADCL | (ADCH << 8)) but the compiler works it out */
#define channelNumToIndex(channel) channel
uint8_t adcPinToChannelNum(uint8_t pin) {
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
if (pin >= 18) pin -= 18; // allow for channel or pin numbers
pin = analogPinToChannel(pin); // moved from extra #if which was below in Arduino code, and redefined in mozzi_analog.h, with notes
#elif defined(__AVR_ATmega1284__)
if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif
return pin;
}
void adcStartConversion(uint8_t channel) {
#if defined(__AVR_ATmega32U4__)
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((channel >> 3) & 0x01) << MUX5);
#elif defined(ADCSRB) && defined(MUX5)
// the MUX5 bit of ADCSRB selects whether we're reading from channels
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((channel >> 3) & 0x01) << MUX5);
#endif
// from wiring_analog.c:
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).
#if defined(ADMUX)
# if defined(TEENSYDUINO) // analog_reference is not part TEENSY 2.0 codebase
ADMUX = (1 << REFS0) | (channel & 0x07); // TB2017 this overwrote analog_reference
# else
ADMUX = (analog_reference << 6) | (channel & 0x07);
# endif
#endif
#if defined(ADCSRA) && defined(ADCL)
// start the conversion
ADCSRA |= (1 << ADSC);
#endif
}
static void startSecondADCReadOnCurrentChannel() {
ADCSRA |= (1 << ADSC); // start a second conversion on the current channel
}
/*
void adcEnableInterrupt(){
ADCSRA |= (1 << ADIE);
}
*/
ISR(ADC_vect, ISR_BLOCK)
{
advanceADCStep();
}
void setupFastAnalogRead(int8_t speed) {
if (speed == FAST_ADC){ // divide by 16
ADCSRA |= (1 << ADPS2);
ADCSRA &= ~(1 << ADPS1);
ADCSRA &= ~(1 << ADPS0);
} else if(speed == FASTER_ADC){ // divide by 8
ADCSRA &= ~(1 << ADPS2);
ADCSRA |= (1 << ADPS1);
ADCSRA |= (1 << ADPS0);
} else if(speed == FASTEST_ADC){ // divide by 4
ADCSRA &= ~(1 << ADPS2);
ADCSRA |= (1 << ADPS1);
ADCSRA &= ~(1 << ADPS0);
}
}
void setupMozziADC(int8_t speed) {
ADCSRA |= (1 << ADIE); // adc Enable Interrupt
adcDisconnectAllDigitalIns();
}
////// END analog input code ////////
//// BEGIN AUDIO OUTPUT code ///////
/*
ATmega328 technical manual, Section 12.7.4:
The dual-slope operation [of phase correct pwm] has lower maximum operation
frequency than single slope operation. However, due to the symmetric feature
of the dual-slope PWM modes, these modes are preferred for motor control
applications.
Due to the single-slope operation, the operating frequency of the
fast PWM mode can be twice as high as the phase correct PWM mode that use
dual-slope operation. This high frequency makes the fast PWM mode well suited
for power regulation, rectification, and DAC applications. High frequency allows
physically small sized external components (coils, capacitors)..
DAC, that's us! Fast PWM.
PWM frequency tests
62500Hz, single 8 or dual 16 bits, bad aliasing
125000Hz dual 14 bits, sweet
250000Hz dual 12 bits, gritty, if you're gonna have 2 pins, have 14 bits
500000Hz dual 10 bits, grittier
16384Hz single nearly 9 bits (original mode) not bad for a single pin, but
carrier freq noise can be an issue
*/
// to store backups of timer registers so Mozzi can be stopped and pre_mozzi
// timer values can be restored
static uint8_t pre_mozzi_TCCR1A, pre_mozzi_TCCR1B, pre_mozzi_OCR1A,
pre_mozzi_TIMSK1;
#if (AUDIO_MODE == HIFI)
#if defined(TCCR2A)
static uint8_t pre_mozzi_TCCR2A, pre_mozzi_TCCR2B, pre_mozzi_OCR2A,
pre_mozzi_TIMSK2;
#elif defined(TCCR2)
static uint8_t pre_mozzi_TCCR2, pre_mozzi_OCR2, pre_mozzi_TIMSK;
#elif defined(TCCR4A)
static uint8_t pre_mozzi_TCCR4A, pre_mozzi_TCCR4B, pre_mozzi_TCCR4C,
pre_mozzi_TCCR4D, pre_mozzi_TCCR4E, pre_mozzi_OCR4C, pre_mozzi_TIMSK4;
#endif
#endif
static void backupPreMozziTimer1() {
// backup pre-mozzi register values for pausing later
pre_mozzi_TCCR1A = TCCR1A;
pre_mozzi_TCCR1B = TCCR1B;
pre_mozzi_OCR1A = OCR1A;
pre_mozzi_TIMSK1 = TIMSK1;
}
#if (AUDIO_MODE == HIFI)
#if defined(TCCR2A)
static uint8_t mozzi_TCCR2A, mozzi_TCCR2B, mozzi_OCR2A, mozzi_TIMSK2;
#elif defined(TCCR2)
static uint8_t mozzi_TCCR2, mozzi_OCR2, mozzi_TIMSK;
#elif defined(TCCR4A)
static uint8_t mozzi_TCCR4A, mozzi_TCCR4B, mozzi_TCCR4C, mozzi_TCCR4D,
mozzi_TCCR4E, mozzi_OCR4C, mozzi_TIMSK4;
#endif
#endif
#if (EXTERNAL_AUDIO_OUTPUT == true)
static void startAudio() {
backupPreMozziTimer1();
Timer1.initializeCPUCycles(
F_CPU / AUDIO_RATE,
PHASE_FREQ_CORRECT); // set period, phase and frequency correct
TIMSK1 = _BV(TOIE1); // Overflow Interrupt Enable (when not using
// Timer1.attachInterrupt())
}
ISR(TIMER1_OVF_vect, ISR_BLOCK) {
defaultAudioOutput();
}
#elif (AUDIO_MODE == STANDARD) || (AUDIO_MODE == STANDARD_PLUS)
# if (AUDIO_MODE == STANDARD_PLUS)
# include "AudioConfigStandardPlus.h"
# else
# include "AudioConfigStandard9bitPwm.h"
# endif
inline void audioOutput(const AudioOutput f)
{
AUDIO_CHANNEL_1_OUTPUT_REGISTER = f.l()+AUDIO_BIAS;
# if (AUDIO_CHANNELS > 1)
AUDIO_CHANNEL_2_OUTPUT_REGISTER = f.r()+AUDIO_BIAS;
# endif
}
static void startAudio() {
backupPreMozziTimer1();
pinMode(AUDIO_CHANNEL_1_PIN, OUTPUT); // set pin to output for audio
// pinMode(AUDIO_CHANNEL_2_PIN, OUTPUT); // set pin to output for audio
# if (AUDIO_MODE == STANDARD)
Timer1.initializeCPUCycles(
F_CPU / AUDIO_RATE,
PHASE_FREQ_CORRECT); // set period, phase and frequency correct
# else // (AUDIO_MODE == STANDARD_PLUS)
Timer1.initializeCPUCycles(F_CPU / PWM_RATE,
FAST); // fast mode enables higher PWM rate
# endif
Timer1.pwm(AUDIO_CHANNEL_1_PIN,
AUDIO_BIAS); // pwm pin, 50% of Mozzi's duty cycle, ie. 0 signal
# if (AUDIO_CHANNELS > 1)
Timer1.pwm(AUDIO_CHANNEL_2_PIN, AUDIO_BIAS); // sets pin to output
# endif
TIMSK1 = _BV(TOIE1); // Overflow Interrupt Enable (when not using
// Timer1.attachInterrupt())
}
/* Interrupt service routine moves sound data from the output buffer to the
Arduino output register, running at AUDIO_RATE. */
ISR(TIMER1_OVF_vect, ISR_BLOCK) {
# if (AUDIO_MODE == STANDARD_PLUS) && (AUDIO_RATE == 16384) // only update every second ISR, if lower audio rate
static boolean alternate;
alternate = !alternate;
if (alternate) return;
# endif
defaultAudioOutput();
}
#elif (AUDIO_MODE == HIFI)
# if (EXTERNAL_AUDIO_OUTPUT != true)
# include "AudioConfigHiSpeed14bitPwm.h"
inline void audioOutput(const AudioOutput f) {
// read about dual pwm at
// http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/
// sketches at http://wiki.openmusiclabs.com/wiki/PWMDAC,
// http://wiki.openmusiclabs.com/wiki/MiniArDSP
// if (!output_buffer.isEmpty()){
//unsigned int out = output_buffer.read();
// 14 bit, 7 bits on each pin
// AUDIO_CHANNEL_1_highByte_REGISTER = out >> 7; // B00111111 10000000 becomes
// B1111111
// try to avoid looping over 7 shifts - need to check timing or disassemble to
// see what really happens unsigned int out_high = out<<1; // B00111111
// 10000000 becomes B01111111 00000000
// AUDIO_CHANNEL_1_highByte_REGISTER = out_high >> 8; // B01111111 00000000
// produces B01111111 AUDIO_CHANNEL_1_lowByte_REGISTER = out & 127;
/* Atmega manual, p123
The high byte (OCR1xH) has to be written first.
When the high byte I/O location is written by the CPU,
the TEMP Register will be updated by the value written.
Then when the low byte (OCR1xL) is written to the lower eight bits,
the high byte will be copied into the upper 8-bits of
either the OCR1x buffer or OCR1x Compare Register in
the same system clock cycle.
*/
AUDIO_CHANNEL_1_highByte_REGISTER = (f.l()+AUDIO_BIAS) >> AUDIO_BITS_PER_REGISTER;
AUDIO_CHANNEL_1_lowByte_REGISTER = (f.l()+AUDIO_BIAS) & ((1 << AUDIO_BITS_PER_REGISTER) - 1);
}
# endif
static void setupTimer2();
static void startAudio() {
backupPreMozziTimer1();
// pwm on timer 1
pinMode(AUDIO_CHANNEL_1_highByte_PIN,
OUTPUT); // set pin to output for audio, use 3.9k resistor
pinMode(AUDIO_CHANNEL_1_lowByte_PIN,
OUTPUT); // set pin to output for audio, use 499k resistor
Timer1.initializeCPUCycles(
F_CPU / 125000,
FAST); // set period for 125000 Hz fast pwm carrier frequency = 14 bits
Timer1.pwm(AUDIO_CHANNEL_1_highByte_PIN,
0); // pwm pin, 0% duty cycle, ie. 0 signal
Timer1.pwm(AUDIO_CHANNEL_1_lowByte_PIN,
0); // pwm pin, 0% duty cycle, ie. 0 signal
// audio output interrupt on timer 2, sets the pwm levels of timer 1
setupTimer2();
}
/* set up Timer 2 using modified FrequencyTimer2 library */
void dummy() {}
static void backupPreMozziTimer2() {
// backup Timer2 register values
#if defined(TCCR2A)
pre_mozzi_TCCR2A = TCCR2A;
pre_mozzi_TCCR2B = TCCR2B;
pre_mozzi_OCR2A = OCR2A;
pre_mozzi_TIMSK2 = TIMSK2;
#elif defined(TCCR2)
pre_mozzi_TCCR2 = TCCR2;
pre_mozzi_OCR2 = OCR2;
pre_mozzi_TIMSK = TIMSK;
#elif defined(TCCR4A)
pre_mozzi_TCCR4B = TCCR4A;
pre_mozzi_TCCR4B = TCCR4B;
pre_mozzi_TCCR4B = TCCR4C;
pre_mozzi_TCCR4B = TCCR4D;
pre_mozzi_TCCR4B = TCCR4E;
pre_mozzi_OCR4C = OCR4C;
pre_mozzi_TIMSK4 = TIMSK4;
#endif
}
// audio output interrupt on timer 2 (or 4 on ATMEGA32U4 cpu), sets the pwm
// levels of timer 2
static void setupTimer2() {
backupPreMozziTimer2(); // to reset while pausing
unsigned long period = F_CPU / AUDIO_RATE;
FrequencyTimer2::setPeriodCPUCycles(period);
FrequencyTimer2::setOnOverflow(dummy);
FrequencyTimer2::enable();
}
#if defined(TIMER2_COMPA_vect)
ISR(TIMER2_COMPA_vect)
#elif defined(TIMER2_COMP_vect)
ISR(TIMER2_COMP_vect)
#elif defined(TIMER4_COMPA_vect)
ISR(TIMER4_COMPA_vect)
#else
#error \
"This board does not have a hardware timer which is compatible with FrequencyTimer2"
void dummy_function(void)
#endif
{
defaultAudioOutput();
}
// end of HIFI
#endif
//-----------------------------------------------------------------------------------------------------------------
void stopMozzi() {
noInterrupts();
// restore backed up register values
TCCR1A = pre_mozzi_TCCR1A;
TCCR1B = pre_mozzi_TCCR1B;
OCR1A = pre_mozzi_OCR1A;
TIMSK1 = pre_mozzi_TIMSK1;
#if (AUDIO_MODE == HIFI)
#if defined(TCCR2A)
TCCR2A = pre_mozzi_TCCR2A;
TCCR2B = pre_mozzi_TCCR2B;
OCR2A = pre_mozzi_OCR2A;
TIMSK2 = pre_mozzi_TIMSK2;
#elif defined(TCCR2)
TCCR2 = pre_mozzi_TCCR2;
OCR2 = pre_mozzi_OCR2;
TIMSK = pre_mozzi_TIMSK;
#elif defined(TCCR4A)
TCCR4B = pre_mozzi_TCCR4A;
TCCR4B = pre_mozzi_TCCR4B;
TCCR4B = pre_mozzi_TCCR4C;
TCCR4B = pre_mozzi_TCCR4D;
TCCR4B = pre_mozzi_TCCR4E;
OCR4C = pre_mozzi_OCR4C;
TIMSK4 = pre_mozzi_TIMSK4;
#endif
#endif
interrupts();
}
// Unmodified TimerOne.cpp has TIMER3_OVF_vect.
// Watch out if you update the library file.
// The symptom will be no sound.
// ISR(TIMER1_OVF_vect)
// {
// Timer1.isrCallback();
// }
//// END AUDIO OUTPUT code ///////