Audio recording/playback

Hey,

I need a bit of help for my coursework for A2 Electronics.

I’m building a audio recording/playback system and it doesn’t work!

I’ve built a microphone preamp which is being fed into Analog Input 1. I’m also using (currently) PWM on pin 11 for output to a speaker.

With this I’m trying to use I2C but it uses interrupts as well. Is there anyway to use I2C without interrupts or is there a way around it?

The code doesn’t count towards my coursework mark so it’s fine to post here

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
 
#define SAMPLE_RATE 8000
#include "sounddata.h"
#include <Wire.h>

int ledPin = 13;
int speakerPin = 11;
volatile uint16_t sample;
byte lastSample;

#define DAC_PWM 1
#define DAC_R2R_6bit 2
#define DAC_R2R_8bit 3
void SetDAC(byte outputValue, unsigned int channel);

//Lower level funcitons to actualy set the specific DAC type. Used only by the SetDAC funciton.
void SetDAC_PWM(byte outputValue);
void SetDAC_R2R(byte outputValue);

//Set the PWM D/A converter output
void SetDAC_PWM(byte outputValue){
  //This implementation uses PWM in 8 bit mode.
  //Load the upper 8 bits into the PWM control register
  //OCR2A = (outputValue>>8)&0xFF;
  OCR2A = outputValue;
}

//Set the R2R D/A converter output.
void SetDAC_R2R_6bit(byte outputValue){
  //We are using port D bits 2-7 for a 6-bit D/A. 
  //Bits 0 and 1 are serial which we dont want to disturb
  //PORTD=(PIND&0x03)|((outputValue>>8)&0xFC);
  PORTD=((PORTD&0x03)|(outputValue&0xFC));
}

//Set the R2R D/A converter output.
void SetDAC_R2R_8bit(byte outputValue){
  //We are using port D bits 2-7 for a 6-bit D/A. 
  //Bits 0 and 1 are serial which we dont want to disturb
  //PORTD=(PIND&0x03)|((outputValue>>8)&0xFC);
  PORTD=((PORTD&0x03)|(outputValue<<2&0xFC));
  digitalWrite(8,outputValue>>6 & 0x01);
  digitalWrite(9,outputValue>>7 & 0x01);
}

void SetDAC(byte outputValue, unsigned int channel) {
  switch(channel) {
    case DAC_PWM :
      SetDAC_PWM(outputValue);
      break;
    case DAC_R2R_6bit :
      SetDAC_R2R_6bit(outputValue);
      break;
    case DAC_R2R_8bit :
      SetDAC_R2R_8bit(outputValue);
      break;
  }
}

void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
  int rdata = data;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(rdata);
  Wire.endTransmission();
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,1);
  if (Wire.available()) rdata = Wire.receive();
  return rdata;
}

boolean recording = false;
byte recStore = 0;
const int recLength = 1024;
//byte recTable[recLength];
int recOffset = 0;
int playOffset = 0;
boolean currByteWrite = false;
boolean currByteRead = false;
byte i2cAddr = 0x50;
int byteReady = 0;
int byteDone = -1;

// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
  if(digitalRead(3))
  {
    OCR2A = analogRead(0) >> 2;
    return;
  } else if(digitalRead(12) && !recording) {
    recOffset = 0;
    recording = true;
    currByteWrite = false;
  }
  if(!digitalRead(12) && recording)
  {
    playOffset = 0;
    recording = false;
    currByteRead = false;
  }
  if(recording && recOffset < recLength)
  {
    if(!currByteWrite)
    {
      recStore = (analogRead(0) >> 2) & 0xF0;
    } else {
      //recTable[recOffset++] = recStore + ((analogRead(0) >> 6) & 0x0F);
        //!!!! THIS DOESN'T WORK!!!!
      i2c_eeprom_write_byte(i2cAddr,recOffset+1,recStore + ((analogRead(0) >> 6) & 0x0F));
      //Serial.println(recStore+((analogRead(0) >> 6) & 0x0F));
    }
    currByteWrite = !currByteWrite;
  } else if(recording) {
    if(sample >= sounddata_length+lastSample) sample = 0;
    OCR2A = pgm_read_byte(&sounddata_data[sample++]);
  } else if(!recording) {
    if(playOffset >= recLength) playOffset = 0;
    if(!currByteRead)
    {
      //Serial.println("Before recStore");
      //recStore = i2c_eeprom_read_byte(i2cAddr,1);
      //Serial.println("After recStore");
      OCR2A = (recStore & 0xF0);
    } else {
      //OCR2A = (recTable[playOffset++] & 0x0F) << 4;
      OCR2A = (recStore & 0x0F) << 4;
      playOffset++;
    }
    currByteRead = !currByteRead;
  }
  //OCR2A = analogRead(0);
  return;
    /*if (sample >= sounddata_length) {
        if (sample == sounddata_length + lastSample) {
            //stopPlayback();
            sample = 0;
        }
        else {
            // Ramp down to zero to reduce the click at the end of playback.
            OCR2A = sounddata_length + lastSample - sample;
        }
    }
    else {
        byte readData = pgm_read_byte(&sounddata_data[sample]);
        SetDAC(readData, DAC_PWM);
        SetDAC(readData, DAC_R2R_8bit);
    }

    ++sample;*/
    byte readData = analogRead(0) >> 2;
    SetDAC(readData, DAC_PWM);
}

void startPlayback()
{
    pinMode(speakerPin, OUTPUT);

    // Set up Timer 2 to do pulse width modulation on the speaker
    // pin.

    // Use internal clock (datasheet p.160)
    ASSR &= ~(_BV(EXCLK) | _BV(AS2));

    // Set fast PWM mode  (p.157)
    TCCR2A |= _BV(WGM21) | _BV(WGM20);
    TCCR2B &= ~_BV(WGM22);

    // Do non-inverting PWM on pin OC2A (p.155)
    // On the Arduino this is pin 11.
    TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
    TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));

    // No prescaler (p.158)
    TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

    // Set initial pulse width to the first sample.
    OCR2A = pgm_read_byte(&sounddata_data[0]);


    // Set up Timer 1 to send a sample every interrupt.

    cli();

    // Set CTC mode (Clear Timer on Compare Match) (p.133)
    // Have to set OCR1A *after*, otherwise it gets reset to 0!
    TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
    TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));

    // No prescaler (p.134)
    TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

    // Set the compare register (OCR1A).
    // OCR1A is a 16-bit register, so we have to do this with
    // interrupts disabled to be safe.
    OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000

    // Enable interrupt when TCNT1 == OCR1A (p.136)
    TIMSK1 |= _BV(OCIE1A);
/*
    lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
    sample = 0;*/
    sei();
}

void stopPlayback()
{
    // Disable playback per-sample interrupt.
    TIMSK1 &= ~_BV(OCIE1A);

    // Disable the per-sample timer completely.
    TCCR1B &= ~_BV(CS10);

    // Disable the PWM timer.
    TCCR2B &= ~_BV(CS10);

    digitalWrite(speakerPin, LOW);
}

void setup()
{
    pinMode(ledPin, OUTPUT);
    pinMode(3,INPUT);
    pinMode(12,INPUT);
    Wire.begin();
    Serial.begin(115200);
    Serial.println("Before recStore");
    Serial.print("Read: ");
    Serial.println(i2c_eeprom_read_byte(i2cAddr,1));
    Serial.println("After recStore");
    startPlayback();
}

void loop()
{
    while (true)
    {
      // !!!! IT WORKS HERE !!!!
    recStore = i2c_eeprom_read_byte(i2cAddr,1);
    }
}

No-one know whether it's possible?