Need help with PWM DAC audio output using Mega2560 R3

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

Mega does not have 16KB SRAM - only the 1284P processor has that much.

CrossRoads:
Mega does not have 16KB SRAM - only the 1284P processor has that much.

Yeah, I don't know why I had 16kB in my head, so I bumped the ringbuffer down to 4kB and the sinewave is working again. Now I'm struggling to get input working. I have a preamped signal as input, but this code produces no output:

/* 
 * Arduino Audio Loopback Test
 *
 * Arduino Realtime Audio Processing
 * 2 ADC 8-Bit Mode
 * ana�og 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))

// pin variables
int ledPin = 13;
int pwmPin = 10;
//int testPin = 7;

boolean div32;
boolean div16;

// interrupt variables accessed globally
volatile boolean f_sample;
volatile byte badc0;
volatile byte badc1;
volatile byte ibb;

// global counters
int cnta;
int icnt1;
int icnt2;
int cnt2;
int iw1;
int iw2;
int iw;
byte bb;

// 8-bit audio memory
const int ddsz = 4096; // 2^12B , or 4kB
byte dd[ddsz];

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(pwmPin, OUTPUT);
  //pinMode(testPin, OUTPUT);
  Serial.begin(57600);
  Serial.println("Arduino Audio Phasor");

  fill_sinewave();

  // set adc prescaler  to 32 for 38kHz sampling frequency
  ADCSRA = _BV(ADPS2) | _BV(ADPS0);

  // set 8-bit adc resolution
  // multiplex input on channel 0
  ADMUX = _BV(ADLAR) | _BV(REFS0);

  // Timer2 PWM Mode set to fast PWM 
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);

  // Timer2 Clock Prescaler to : 1 
  TCCR2B = _BV(WGM22) | _BV(CS22) | _BV(CS20);

  // Timer2 PWM Port Enable on pin 10
  sbi(DDRB, DDB4);
  //DDRB = _BV(DDB4);

  // disable interrupts to avoid distortion
  //cli();
  
  // disable timer0, enable timer2. Timer0 controls delay(), so it will not be available
  cbi(TIMSK0, TOIE0);
  sbi(TIMSK2, TOIE2);
}

void loop() {
  // wait for Sample Value from ADC
  while (!f_sample) { }  // Cycle 31250 KHz = 32uSec 

  f_sample = false;

  bb = badc1;
  dd[icnt1] = bb;              // write to buffer
  iw1 = dd[icnt2];             // read the delay buffer

  badc0 = badc0 / 20;        // limit poti value to 512
  icnt1++;

  icnt2 = icnt1 - badc0;

  icnt2 = icnt2 & (ddsz-1);       // limit index 0..ddsz-1
  icnt1 = icnt1 & (ddsz-1);       // limit index 0..ddsz-1

  iw2 = iw1 + bb;
  iw2 = iw2 / 2;

  bb = iw2;

  OCR2A = bb;                // Sample Value to PWM Output
}

/*
 * Fill the audio ringbuffer with a fundamental sine wave
 */
void fill_sinewave() {
  float fd;
  float fcnt;
  float dx = 2 * PI / ddsz;                  // fill the byte ringbuffer
  for (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/512 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) {
  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
  }
}

You are using interrupt handlers yet you've disabled all interrupts.

Why don't you just turn off timer0 interrupts?