Hello all,
I'm currently trying to get realtime audio processing working with the guide here. I have the circuit set up exactly as pictured, but the problem I'm running into is the code. I've been able to get a basic sinewave tone as output, but I can't remember what I changed, and it isn't working anymore.
The original author was using a Diecimila, but I'm using a Mega2560 R3.
Here are the changes I know I've made:
- Changed ringbuffer to 8KB since Mega2560 has 16KB of SRAM
- Change input sampling rate to ~39kHz (adc prescale set to 32)
- Change output timer rate to ~32.5kHz (timer2 prescale set to 1)
- Tried to change the output to pin 10, since the mega's timer2 controls pins 10 and 9 (vs 11 and 3)
Here is what I'm working with right now:
/* Arduino Audio Loopback Test
*
* Arduino Realtime Audio Processing
* 2 ADC 8-Bit Mode
* analog input 1 is used to sample the audio signal
* analog input 0 is used to control an audio effect
* PWM DAC with Timer2 as analog output
*
* KHM 2008 / Lab3/ Martin Nawrath nawrath@khm.de
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne
*/
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define tbi(sfr, bit) (_SFR_BYTE(sfr) ^= _BV(bit))
//int testPin = 7;
int pwmPin = 10;
int ledPin = 13;
boolean div32;
boolean div16;
// interrupt variables accessed globally
volatile boolean f_sample;
volatile byte badc0;
volatile byte badc1;
volatile byte ibb;
// global counters
int icnt;
//int cnt2;
//int ii;
// 8-bit audio ringbuffer
const int ddsz = 8192;
byte dd[ddsz];
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(pwmPin, OUTPUT);
//pinMode(testPin, OUTPUT);
Serial.begin(57600);
Serial.println("Arduino Audio Sinewave");
fill_sinewave();
// set adc prescaler to 32 for ~38.5kHz sampling frequency
//sbi(ADCSRA, ADPS2);
//cbi(ADCSRA, ADPS1);
//sbi(ADCSRA, ADPS0);
ADCSRA = _BV(ADPS2) | _BV(ADPS0);
// 8-Bit ADC in ADCH Register
//sbi(ADMUX, ADLAR);
// VCC Reference
//sbi(ADMUX, REFS0);
//cbi(ADMUX, REFS1);
// Set Input Multiplexer to Channel 0
//cbi(ADMUX, MUX0);
//cbi(ADMUX, MUX1);
//cbi(ADMUX, MUX2);
//cbi(ADMUX, MUX3);
ADMUX = _BV(ADLAR) | _BV(REFS0);
// Timer1 PWM Mode set to fast PWM
//sbi(TCCR2A, COM2A1);
//cbi(TCCR2A, COM2A0);
//sbi(TCCR2B, COM2B1);
//cbi(TCCR2B, COM2B0);
//sbi(TCCR2A, WGM20);
//sbi(TCCR2A, WGM21);
//cbi(TCCR2A, WGM22);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
// Timer1 Clock Prescaler to : 1
//sbi(TCCR2B, CS22);
//cbi(TCCR2B, CS21);
//sbi(TCCR2B, CS20);
TCCR2B = _BV(CS22) | _BV(CS20);
// Timer1 PWM Port Enable on Pin 10
//sbi(DDRB, DDB4);
DDRB = _BV(DDB4);
// disable interrupts to avoid distortion
// cli();
// Disable timer0, and enable timer2. delay() will no longer work.
cbi(TIMSK0, TOIE0);
sbi(TIMSK2, TOIE2);
}
void loop() {
// wait for Sample Value from ADC
while (!f_sample) { } // Cycle 31250 KHz = 32uSec
f_sample = false;
badc1 = dd[icnt]; // get the buffervalue on indexposition
icnt++;
icnt &= (ddsz-1); // limit index 0..511
//cnt2++;
//cnt2 &= 32767; // limit cnt2 to 0..32767
OCR2A = badc1; // Sample Value to PWM Output
Serial.println(badc1);
}
//******************************************************************
void fill_sinewave() {
float dx;
float fd;
float fcnt;
byte bb;
dx = 2 * PI / ddsz; // fill the audio ringbuffer
for (int iw = 0; iw < ddsz; iw++) { // with 50 periods sinewawe
fd = 127 * sin(fcnt); // fundamental tone
fcnt = fcnt + dx; // in the range of 0 to 2xpi and 1/ddsz increments
bb = 127 + fd; // add dc offset to sinewawe
dd[iw] = bb; // write value into array
}
}
//******************************************************************
// Timer2 Interrupt Service at 62.5 KHz
// here the audio and pot signal is sampled in a rate of: 16Mhz / 256 / 2 / 2 = 15625 Hz
// runtime : xxxx microseconds
ISR(TIMER2_OVF_vect) {
PORTB |= 1;
div32 = !div32; // divide timer2 frequency / 2 to 31.25kHz
if (div32) {
div16 = !div16;
if (div16) { // sample channel 0 and 1 alternately so each channel is sampled with 15.6kHz
badc0 = ADCH; // get ADC channel 0
cbi(ADMUX, MUX0);
sbi(ADMUX, MUX1); // set multiplexer to channel 1
}
else {
badc1 = ADCH; // get ADC channel 1
sbi(ADMUX, MUX0); // set multiplexer to channel 0
cbi(ADMUX, MUX1);
f_sample = true;
}
ibb++;
ibb--;
ibb++;
ibb--; // short delay before start conversion
sbi(ADCSRA, ADSC); // start next conversion
}
}
Any help would be appreciated.
Possibly handy links:
Timer PWM Cheatsheet (With Mega2560 exceptions.)
Mega2560 Pin Mappings