Mozzi  version v2.0
sound synthesis library for Arduino
MozziGuts_impl_AVR.hpp
1 /*
2  * MozziGuts_impl_AVR.hpp
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2012-2024 Tim Barrass and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10 */
11 
12 #include "utility/FrequencyTimer2.h"
13 #include "utility/TimerOne.h"
14 
15 #if (F_CPU != 16000000)
16 #warning
17  "Mozzi has been tested with a cpu clock speed of 16MHz on Arduino! Results may vary with other speeds."
18 #endif
19 
20 ////// BEGIN analog input code ////////
21 #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD)
22 extern uint8_t analog_reference;
23 
24 ISR(ADC_vect, ISR_BLOCK)
25 {
26  MozziPrivate::advanceADCStep();
27 }
28 
29 namespace MozziPrivate {
30 #define getADCReading() ADC /* officially (ADCL | (ADCH << 8)) but the compiler works it out */
31 #define channelNumToIndex(channel) channel
32 uint8_t adcPinToChannelNum(uint8_t pin) {
33 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
34  if (pin >= 54) pin -= 54; // allow for channel or pin numbers
35 #elif defined(__AVR_ATmega32U4__)
36  if (pin >= 18) pin -= 18; // allow for channel or pin numbers
37 # if defined(CORE_TEENSY) // special handling for Teensy2, which does not (did not?) have an analogPinToChannel() define (see https://github.com/sensorium/Mozzi/issues/10)
38  static const uint8_t PROGMEM adc_mapping[] = {
39  // 0, 1, 4, 5, 6, 7, 13, 12, 11, 10, 9, 8
40  0, 1, 4, 5, 6, 7, 13, 12, 11, 10, 9, 8, 10, 11, 12, 13, 7, 6, 5, 4, 1, 0, 8
41  };
42  pin = pgm_read_byte(adc_mapping + (P));
43 # else
44  pin = analogPinToChannel(pin);
45 # endif
46 #elif defined(__AVR_ATmega1284__)
47  if (pin >= 24) pin -= 24; // allow for channel or pin numbers
48 #else
49  if (pin >= 14) pin -= 14; // allow for channel or pin numbers
50 #endif
51  return pin;
52 }
53 
54 void adcStartConversion(uint8_t channel) {
55 #if defined(__AVR_ATmega32U4__)
56  ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((channel >> 3) & 0x01) << MUX5);
57 #elif defined(ADCSRB) && defined(MUX5)
58  // the MUX5 bit of ADCSRB selects whether we're reading from channels
59  // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
60  ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((channel >> 3) & 0x01) << MUX5);
61 #endif
62 
63 // from wiring_analog.c:
64 // set the analog reference (high two bits of ADMUX) and select the
65 // channel (low 4 bits). this also sets ADLAR (left-adjust result)
66 // to 0 (the default).
67 #if defined(ADMUX)
68 # if defined(TEENSYDUINO) // analog_reference is not part TEENSY 2.0 codebase
69  ADMUX = (1 << REFS0) | (channel & 0x07); // TB2017 this overwrote analog_reference
70 # else
71  ADMUX = (analog_reference << 6) | (channel & 0x07);
72 # endif
73 #endif
74 #if defined(ADCSRA) && defined(ADCL)
75  // start the conversion
76  ADCSRA |= (1 << ADSC);
77 #endif
78 }
79 
80 static void startSecondADCReadOnCurrentChannel() {
81  ADCSRA |= (1 << ADSC); // start a second conversion on the current channel
82 }
83 
84 /*
85 void adcEnableInterrupt(){
86  ADCSRA |= (1 << ADIE);
87 }
88 */
89 
90 void setupMozziADC(int8_t speed) {
91  ADCSRA |= (1 << ADIE); // adc Enable Interrupt
92  adcDisconnectAllDigitalIns();
93  setupFastAnalogRead(speed);
94 }
95 
96 void setupFastAnalogRead(int8_t speed) {
97  if (speed == FAST_ADC){ // divide by 16
98  ADCSRA |= (1 << ADPS2);
99  ADCSRA &= ~(1 << ADPS1);
100  ADCSRA &= ~(1 << ADPS0);
101  } else if(speed == FASTER_ADC){ // divide by 8
102  ADCSRA &= ~(1 << ADPS2);
103  ADCSRA |= (1 << ADPS1);
104  ADCSRA |= (1 << ADPS0);
105  } else if(speed == FASTEST_ADC){ // divide by 4
106  ADCSRA &= ~(1 << ADPS2);
107  ADCSRA |= (1 << ADPS1);
108  ADCSRA &= ~(1 << ADPS0);
109  }
110 }
111 }
112 
113 #endif
114 
115 ////// END analog input code ////////
116 
117 
118 namespace MozziPrivate {
119 //// BEGIN AUDIO OUTPUT code ///////
120 /*
121 ATmega328 technical manual, Section 12.7.4:
122 The dual-slope operation [of phase correct pwm] has lower maximum operation
123 frequency than single slope operation. However, due to the symmetric feature
124 of the dual-slope PWM modes, these modes are preferred for motor control
125 applications.
126 Due to the single-slope operation, the operating frequency of the
127 fast PWM mode can be twice as high as the phase correct PWM mode that use
128 dual-slope operation. This high frequency makes the fast PWM mode well suited
129 for power regulation, rectification, and DAC applications. High frequency allows
130 physically small sized external components (coils, capacitors)..
131 
132 DAC, that's us! Fast PWM.
133 
134 PWM frequency tests
135 62500Hz, single 8 or dual 16 bits, bad aliasing
136 125000Hz dual 14 bits, sweet
137 250000Hz dual 12 bits, gritty, if you're gonna have 2 pins, have 14 bits
138 500000Hz dual 10 bits, grittier
139 16384Hz single nearly 9 bits (original mode) not bad for a single pin, but
140 carrier freq noise can be an issue
141 */
142 
143 // to store backups of timer registers so Mozzi can be stopped and pre_mozzi
144 // timer values can be restored
145 static uint8_t pre_mozzi_TCCR1A, pre_mozzi_TCCR1B, pre_mozzi_OCR1A,
147 
148 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
149 #if defined(TCCR2A)
152 #elif defined(TCCR2)
154 #elif defined(TCCR4A)
157 #endif
158 #endif
159 
160 static void backupPreMozziTimer1() {
161  // backup pre-mozzi register values for pausing later
162  pre_mozzi_TCCR1A = TCCR1A;
163  pre_mozzi_TCCR1B = TCCR1B;
164  pre_mozzi_OCR1A = OCR1A;
165  pre_mozzi_TIMSK1 = TIMSK1;
166 }
167 
168 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
169 #if defined(TCCR2A)
171 #elif defined(TCCR2)
173 #elif defined(TCCR4A)
176 #endif
177 #endif
178 
179 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED)
180 static void startAudio() {
183  (F_CPU/MOZZI_AUDIO_RATE)-1, // the -1 here is a result of empirical tests
184  // that showed that it brings the resulting frequency
185  // closer to what is expected.
186  // see: https://github.com/sensorium/Mozzi/pull/202
187 
188  PHASE_FREQ_CORRECT); // set period, phase and frequency correct
189  TIMSK1 = _BV(TOIE1); // Overflow Interrupt Enable (when not using
190  // Timer1.attachInterrupt())
191 }
192 
193 } // namespace MozziPrivate
194 
197 }
198 
199 namespace MozziPrivate {
200 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM)
201 static void startAudio() {}
202 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM)
203 inline void audioOutput(const AudioOutput f)
204 {
206 # if (MOZZI_AUDIO_CHANNELS > 1)
208 # endif
209 }
210 
211 static void startAudio() {
213 
214  pinMode(MOZZI_AUDIO_PIN_1, OUTPUT); // set pin to output for audio
215 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) && (MOZZI_PWM_RATE < 32768) // Formerly known as the - long since deprecated - "STANDARD" mode
217  (F_CPU/MOZZI_AUDIO_RATE)-1,// the -1 here is a result of empirical tests
218  // that showed that it brings the resulting frequency
219  // closer to what is expected.
220  // see: https://github.com/sensorium/Mozzi/pull/202
221  PHASE_FREQ_CORRECT); // set period, phase and frequency correct
222 # else // Formerly known as "STANDARD_PLUS" mode
223  Timer1.initializeCPUCycles((F_CPU/MOZZI_PWM_RATE)-1, // the -1 here is a result of empirical tests
224  // that showed that it brings the resulting frequency
225  // closer to what is expected.
226  // see: https://github.com/sensorium/Mozzi/pull/202
227  FAST); // fast mode enables higher PWM rate
228 # endif
230  MOZZI_AUDIO_BIAS); // pwm pin, 50% of Mozzi's duty cycle, ie. 0 signal
231 # if (MOZZI_AUDIO_CHANNELS > 1)
232  pinMode(MOZZI_AUDIO_PIN_2, OUTPUT); // set pin to output for audio
234 # endif
235  TIMSK1 = _BV(TOIE1); // Overflow Interrupt Enable (when not using
236  // Timer1.attachInterrupt())
237 }
238 
239 } // namespace MozziPrivate
240 
241 /* Interrupt service routine moves sound data from the output buffer to the
242 Arduino output register, running at MOZZI_AUDIO_RATE. */
244 # if (MOZZI_AUDIO_RATE < MOZZI_PWM_RATE) // only update every second ISR, if lower audio rate
245  static_assert(2l*MOZZI_AUDIO_RATE == MOZZI_PWM_RATE, "audio rate must the same, or exactly half of the pwm rate!");
246  static boolean alternate;
247  alternate = !alternate;
248  if (alternate) return;
249 # endif
250 
252 }
253 
254 namespace MozziPrivate {
255 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
256 inline void audioOutput(const AudioOutput f) {
257  // read about dual pwm at
258  // http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/
259  // sketches at http://wiki.openmusiclabs.com/wiki/PWMDAC,
260  // http://wiki.openmusiclabs.com/wiki/MiniArDSP
261  // if (!output_buffer.isEmpty()){
262  //unsigned int out = output_buffer.read();
263  // 14 bit, 7 bits on each pin
264  // MOZZI_AUDIO_PIN_1_REGISTER = out >> 7; // B00111111 10000000 becomes
265  // B1111111
266  // try to avoid looping over 7 shifts - need to check timing or disassemble to
267  // see what really happens unsigned int out_high = out<<1; // B00111111
268  // 10000000 becomes B01111111 00000000
269  // MOZZI_AUDIO_PIN_1_REGISTER = out_high >> 8; // B01111111 00000000
270  // produces B01111111 MOZZI_AUDIO_PIN_1_LOW_REGISTER = out & 127;
271  /* Atmega manual, p123
272  The high byte (OCR1xH) has to be written first.
273  When the high byte I/O location is written by the CPU,
274  the TEMP Register will be updated by the value written.
275  Then when the low byte (OCR1xL) is written to the lower eight bits,
276  the high byte will be copied into the upper 8-bits of
277  either the OCR1x buffer or OCR1x Compare Register in
278  the same system clock cycle.
279  */
282 }
283 
284 static void setupTimer2();
285 static void startAudio() {
287  // pwm on timer 1
288  pinMode(MOZZI_AUDIO_PIN_1, OUTPUT); // set pin to output for audio, use 3.9k resistor
289  pinMode(MOZZI_AUDIO_PIN_1_LOW, OUTPUT); // set pin to output for audio, use 499k resistor
291  F_CPU/125000,
292  FAST); // set period for 125000 Hz fast pwm carrier frequency = 14 bits
293  Timer1.pwm(MOZZI_AUDIO_PIN_1, 0); // pwm pin, 0% duty cycle, ie. 0 signal
295  // audio output interrupt on timer 2, sets the pwm levels of timer 1
296  setupTimer2();
297 }
298 
299 /* set up Timer 2 using modified FrequencyTimer2 library */
300 void dummy() {}
301 
302 static void backupPreMozziTimer2() {
303  // backup Timer2 register values
304 #if defined(TCCR2A)
309 #elif defined(TCCR2)
313 #elif defined(TCCR4A)
321 #endif
322 }
323 
324 // audio output interrupt on timer 2 (or 4 on ATMEGA32U4 cpu), sets the pwm
325 // levels of timer 2
326 static void setupTimer2() {
327  backupPreMozziTimer2(); // to reset while pausing
328  unsigned long period = F_CPU / MOZZI_AUDIO_RATE;
332 }
333 
334 } // namespace MozziPrivate
335 
336 #if defined(TIMER2_COMPA_vect)
338 #elif defined(TIMER2_COMP_vect)
340 #elif defined(TIMER4_COMPA_vect)
342 #else
343 #error
344  "This board does not have a hardware timer which is compatible with FrequencyTimer2"
345 void dummy_function(void)
346 #endif
347 {
349 }
350 
351 // end of HIFI
352 
353 namespace MozziPrivate {
354 
355 #endif
356 
357 //-----------------------------------------------------------------------------------------------------------------
358 
359 
360 void stopMozzi() {
361  noInterrupts();
362 
363  // restore backed up register values
364  TCCR1A = pre_mozzi_TCCR1A;
365  TCCR1B = pre_mozzi_TCCR1B;
366  OCR1A = pre_mozzi_OCR1A;
367 
368  TIMSK1 = pre_mozzi_TIMSK1;
369 
370 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
371 #if defined(TCCR2A)
372  TCCR2A = pre_mozzi_TCCR2A;
373  TCCR2B = pre_mozzi_TCCR2B;
374  OCR2A = pre_mozzi_OCR2A;
375  TIMSK2 = pre_mozzi_TIMSK2;
376 #elif defined(TCCR2)
377  TCCR2 = pre_mozzi_TCCR2;
378  OCR2 = pre_mozzi_OCR2;
379  TIMSK = pre_mozzi_TIMSK;
380 #elif defined(TCCR4A)
381  TCCR4B = pre_mozzi_TCCR4A;
382  TCCR4B = pre_mozzi_TCCR4B;
383  TCCR4B = pre_mozzi_TCCR4C;
384  TCCR4B = pre_mozzi_TCCR4D;
385  TCCR4B = pre_mozzi_TCCR4E;
386  OCR4C = pre_mozzi_OCR4C;
387  TIMSK4 = pre_mozzi_TIMSK4;
388 #endif
389 #endif
390  interrupts();
391 }
392 
393 // Unmodified TimerOne.cpp has TIMER3_OVF_vect.
394 // Watch out if you update the library file.
395 // The symptom will be no sound.
396 // ISR(TIMER1_OVF_vect)
397 // {
398 // Timer1.isrCallback();
399 // }
400 //// END AUDIO OUTPUT code ///////
401 
402 //// BEGIN Random seeding ////////
403 #if defined (__AVR_ATmega644P__)
404 
405 // a less fancy version for gizduino (__AVR_ATmega644P__) which doesn't know INTERNAL
406 static long longRandom()
407 {
408  return ((long)analogRead(0)+63)*(analogRead(1)+97); // added offsets in case analogRead is 0
409 }
410 
411 #else
412 
413 /*
414 longRandom(), used as a seed generator, comes from:
415 http://arduino.cc/forum/index.php/topic,38091.0.html
416 // AUTHOR: Rob Tillaart
417 // PURPOSE: Simple Random functions based upon unreliable internal temp sensor
418 // VERSION: 0.1
419 // DATE: 2011-05-01
420 //
421 // Released to the public domain, use at own risk
422 //
423 */
424 static long longRandom()
425 {
426  //analogReference(INTERNAL);
427  unsigned long rv = 0;
428  for (uint8_t i=0; i< 32; i++) rv |= ((analogRead(8)+1171) & 1L) << i; // added 1171 in case analogRead is 0
429  return rv;
430 }
431 #endif
432 
433 void MozziRandPrivate::autoSeed() {
434  ADCSRA &= ~ (1 << ADIE); // adc Disable Interrupt, re-enable at end
435  // this attempt at remembering analog_reference stops it working
436  // maybe needs a delay after changing analog reference in longRandom (Arduino reference suggests this)
437  // because the analog reads return 0
438  //uint8_t analog_reference_orig = ADMUX&192; // analog_reference is high 2 bits of ADMUX, store this because longRandom sets it to internal
439  x = longRandom();
440  y = longRandom();
441  z = longRandom();
442  //analogReference(analog_reference_orig); // change back to original
443  ADCSRA |= (1 << ADIE); // adc re-Enable Interrupt
444 }
445 
446 //// END Random seeding ////////
447 }