Arduino Audio Recorder with Playback

Updated 18 July 2019

This project started with an idea, as most projects do. I wondered if I could use the Arduino UNO to record audio and play it back. I wanted something to amuse the grandkids. They would press a button, say something and the device would then repeat what they had said.

Parts used in this project:

arduino-audio-recorder-with-playback-complete

In my search, I found many people had used Arduino devices to digitise or playback audio; so I had confidence it might be worth a try.

The starting point for me was:-

APC magazine - Arduino Masterclass

* Project #18 - Digital Audio Recorder v2.0

* Darren Yates - 11 April 2014.

 

Modifying the code to suit my purpose I was able to record audio that played back on a PC with reasonable clarity. Previously, I had successfully read and written files to an SD Card using the SdFat Library by Bill Greiman. This is an excellent library for accessing SD Card files.

Outputting the audio from the ATMega328P proved much more difficult. After trying a number of options which only produced distorted audio, I was ready to give up.

I posted on the Core Electronics forum the audio recording part.

Stephen replied with a link to

 Maks Surguy's blog on Innovation, IoT and Laravel – 1 Feb 13

How to play WAV audio files with Arduino Uno and MicroSD card - Maks Surguy's...

I checked it out and examined the TMRpcm.h library in detail, which was being used. The library was not suitable for what I was doing as the audio recording routines were not fully developed. But it did give me a good insight into what I needed to get my device working.

Overall this project has given me a better understanding of the ATMega328P registers and audio wave files.

Useful Software

Highly recommend Audacity. https://www.audacityteam.org/

This software allowed me to create audio files and test files the device had recorded.

The image shows one of the files the device recorded.

 arduino-audio-recorder-with-playback-audacity

Highly recommend HxD Hex Editor. https://mh-nexus.de/en/hxd/

This allowed me to look at the construction of the .wav files to ensure the device was creating them correctly. The image shows the hex dump of the file the device recorded.

arduino-audio-recorder-with-playback-hex-editor

Design

The device uses an ATMega328P running at 16MHz, using a crystal oscillator. It is powered by a 3.7V LiPo 2000mAH battery. A switch disconnects the battery from the circuit board. Input is a single push button and a microphone. The output is a single LED light and a speaker.

            When powered on the LED flashes RED 4 times then turns GREEN indicating the device is ready to use. If the SD Card is not working correctly or is missing, the LED flashes RED at a 1-second interval for as long as the device has power. The push button is ignored.

            Pressing the button starts recording and the LED turns RED, pressing the button again stops recording and the LED turns GREEN. After 2 seconds the recorded audio will play through the speaker. The LED turns RED whenever playback is operating and during playback, the push button is ignored. The recorded audio is saved to one of 10 files on the SD Card. The oldest file is overwritten with the new recording. A small file on the SD card records the current file name so when powered up the device will start recording where it left off last time it was powered on.

If the button has not been pressed for 10 seconds one of the recorded files will be played.

If the button has not been pressed for 60 seconds one of 10 separate audio files will be played. I chose music for these files. These files are not altered by the program, they are saved to the SD card before it is placed in the device.

I felt this design made the operation of the device more interesting for the grandkids.

Microphone, ADC Reference, Resolution

Electret Microphone Amplifier - MAX9814 with Auto Gain Control

The gain is set to 40dB by tying the Gain pin to VCC. This proved better than 50dB or 60dB.

The output varies 2Vpp around 1.25V. This means the audio signal varies from 0.25V to 2.25V.  Connecting a 2.5V reference to the Aref pin of the micro covers this range of input to the ADC. Software tells the ADC to use the Aref pin as its reference voltage.

The 10-bit digitised values will range from 102 to 921. But, only the upper 8 bits are used so the range reduces to 5 to 204. This allows better use of memory and less time to write each digitised value to the SD card but results in reduced audio quality.

ADC set to run at 2MHz. 13 clocks = 6.5uS each digitised value.

SD Card

MicroSD card breakout board+

Standard SPI interface. Software set to run at an 8MHz clock. Full SPI speed.

The files written to the SD card are the standard .wav format and play correctly on all PC audio programs I have tested.

File Names

wav_name.txt

            Stores the name of the current recorded audio file.

rec00000.wav to rec00009.wav

            Recorded audio files.

            15K625Hz, 8-bit sample rate.

play0000.wav to play0009.wav                     

            Pre-recorded audio files, to be played if idle for 60 seconds.

            8KHz, 8-bit sample rate.

Power Supply

Pololu 5V Step-Up/Step-Down Voltage Regulator S7V7F5

These devices from Pololu are fantastic for these types of circuits. Less than 500mA total. The LiPo will vary from 4.2V to 3.2V over its charge life, typical. The regulator keeps a constant 5V supply to the circuit.

Switch

Single push button switch.

Once the switch has been pressed software will ignore its state until at least 500mS have elapsed. The software also waits for the switch to be released before it can be actioned again. This avoids the state where the switch is held down and the device switches between record and playback too quickly. This is a kind of debounce.

LED

This is a dual colour LED. It has 3 leads, common cathode, anode red, anode green.

On the circuit diagram, it is shown as 2 LEDs, which was used at the breadboard testing stage.

Programming Header

I always use a programming header on the circuit board. This allows for in-circuit programming.

Arduino Code

The only Library used SdFat.h (can't say enough how good this library is)

Uses two buffers when recording. When one buffer full, switch to other to record digitized values, full buffer then saved to SD card. Buffer size 256 bytes. SD card has 16mS to save buffer before next is ready. 15625Hz sample rate, 64uS per sample, 256 times 64uS = 16.384mS.

Timer 1 used for PWM output.

Timer 2 used to generate an interrupt at sample rate.

(See program code for more detail on how timers are set up and used)

 

The interrupt handler :

            record mode - starts the ADC, puts the value into a buffer.

            playback mode - value from the buffer placed in the output register of Timer1.

 Both routines increment counters and change buffers on counter overflow.

 

Sketch uses 13492 bytes (41%) of program storage space. The maximum is 32256 bytes.

Global variables use 1283 bytes (62%) of dynamic memory, leaving 765 bytes for local variables. The maximum is 2048 bytes.

Conclusion

I am surprised with the results I achieved from the ATMega328P. The audio quality is not high fidelity but it is understandable and suits the purpose I was after. This project was also excellent in helping me understand the micro operation, register usage and wav files.

I hope the information provided here can assist someone with their own project. This is not a "how to" but covers what I think are the most important points. Cheers

Arduino Code

//==================================================================================================
//    UNO 020 Audio Recorder Playback 007
//    UNO
//==================================================================================================
/* 
 * Digitise audio from microphone and save to SD card.
 * Up to 10 .wav files can be saved.
 * Replay latest recorded file 2 seconds after save.
 * Update file name after replay.
 * 
 * Play one of the recorded files if button not pressed for 10 seconds
 * Play one of the music files if nothing recorded for 1 minute
 * If file not found dont do anything.
 * 
 * Modified by James Grigg 27 November 2018
 * from 
 *    APC magazine  - Arduino Masterclass
 *    Project #18 - Digital Audio Recorder v2.0
 *    Darren Yates - 11 April 2014
 *    Includes the SdFat library - https://code.google.com/p/sdfatlib/downloads/list
 *    
 *    Installed SdFat Library by Bill Greiman Version 1.0.7
 *    ***** excellent library ******
 *    
 * 29 November 2018   
 * Add file to record current filename, ( filename.txt )
 *    check for valid file
 *    read at start to get name of file to save
 *    when record save to this filename,
 *    when record end, increment name, 
 *    save in filename.txt for next record.
 *    
 *    Standard audio sampling rates 44100, 48000, 32000, 22050, 11025, 8000
 *    Use these in wave file definitions
 *    
 * 02 December 2018   
 *    Record and playback working ok.
 *    Audio playback quality very poor.
 *    
 * 06 Jamuary 2019
 *    Seemed to have fixed audio distortion
 *    Carrier and rate discrepancy, last carrier cycle ended too early
 *    Introducing frequencies not in the original.
 *    But at higher volume level audio distortion still present.
 *    Not really fixed.
 *    
 *    Changed sample rate from 8KHz to 15K625Hz, seems to have fixed problem.
 *    Audio hiss present, reduces if mic amp gain set lower. ie 60dB to 40dB.
 *    LM386 board used to amplify audio, PAM8302A did not want to work. 
 *    680ohm from pin to 100uF capacitor to LM386 input. 
 * 
 * 09 Jan 2019
 *    Single switch rather than two.
 *    Changed to ADC5, better layout for PCB.
 *    MIC closer to ADC5 than ADC0 pin on ATMega328P
 *    
 * 16 Jan 2019   
 *    Single switch now working ok, circuit built.
 *    Added LM385 power switch, reduces current drain on battery. 
*/
//==================================================================================================
#include <SdFat.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// digital pin definitions
#define SD_CS           10  // chip select line for SD card
#define RedLED           2  // Red LED
#define GreenLED         4  // Green LED
#define btnStart         5  // push button start/stop record
#define LM386pwr         8  // power up LM386 board only when replay.

// audio output pin = 9. Timer 1A. Direct register access.

#define BUF_SIZE         256  // 
#define BUF_SIZEx2       512  // 
#define WAIT_REPLAY     2000  // Delay in milliseconds between file saved and file replay
#define WAIT_RANDOM    10000  // Delay in milliseconds to wait after replay, to play random file
#define WAIT_MUSIC     60000  // Delay in milliseconds to wait after any file output, before playing random music file
#define WAIT_DEBOUNCE    500  // Delay in milliseconds to wait after button pressed

unsigned long fileSize = 0L;
unsigned long waveChunk = 16;           // Size of 'chunk', waveType + numChannels + sampleRate + bytesPerSec + blockAlign + bitsPerSample
unsigned int waveType = 1;              // 1 = PCM
unsigned int numChannels = 1;           // 1 = mono, 2 = stereo
unsigned long sampleRate = 15625;
unsigned long bytesPerSec = 15625;     // sampleRate * numChannels * bitsPerSample / 8
unsigned int blockAlign = 1;           // numChannels * bitsPerSample / 8
unsigned int bitsPerSample = 8;
unsigned long dataSize = 0L;

unsigned long millisCount = 0L;
unsigned long millisWaitReplay = 0L;
unsigned long millisWaitRandom = 0L;
unsigned long millisWaitMusic = 0L;
unsigned long millisDebounce = 0L;

bool RecordActive = false;              // audio recording in progress
bool FileSaved = false;                 // indicates a .wav file has been saved
bool Replay = false;                    // indicates a .wav file has been saved and has been replayed
bool SendByte = false ;                 // indicates an interrupt has occured and time to output another byte
bool Buffer = false;                    // select buffer 0 at start, buffer 0 = false, buffer 1 = true
bool ButtonRelease = false;             // button debounce, must release button before it can be used again

byte buf00[BUF_SIZE]; 
byte buf01[BUF_SIZE]; 
byte byte1, byte2, byte3, byte4;

unsigned long ByteCount = 0L;
unsigned long ByteSaved = 0L;
unsigned int bufByteCount;

char Wav_File[13] = "rec00000.wav";  // starting filename
char Wav_Name[13] = "wav_name.txt";  // has the next Wav_File.wav name to use

SdFat sd;
SdFile SD_file;

//==================================================================================================
void setup() {
//Serial.begin(115200);
//Serial.println("Audio Recorder Playback PWM Output 003 ");
//Serial.println(' ');

  pinMode(RedLED, OUTPUT);
  pinMode(GreenLED, OUTPUT);
  pinMode(btnStart, INPUT_PULLUP);
  pinMode(LM386pwr, OUTPUT);
  digitalWrite(LM386pwr,HIGH);  // power off

// initialise SD card
//Serial.println(F("Initializing SD card ..."));
   if (sd.begin(SD_CS, SPI_FULL_SPEED)) { // 8MHz SPI bus speed
    for (int i = 0; i < 8; i++) {
      digitalWrite(RedLED, !digitalRead(RedLED));
      delay(100);
    }
// indicate recording stopped, this is the start up condition
    digitalWrite(RedLED,LOW);  
    digitalWrite(GreenLED,HIGH);
  } else { 
// no SD card or card error, flash LED twice per second, until reset
//Serial.println(F("Card failed, or not present"));
    SD_Error();             // never returns from here
  }
  GetWavFileName(false);          // get current name, dont change it
  millisDelay(1000);

// Timer 1, 2 and ADC setup, direct register access
  Setup_Timer_1_2_and_ADC();
}
//==================================================================================================
void loop() {

// Button must be released for at least 500 milliseconds before it can be actioned again.
// Also check the button has been released before actioning to avoid the situation where
// holding down the button switches from start record to stop record to start record etc.
  if (millis() > millisDebounce) {
    if (digitalRead(btnStart) == LOW) {
      if (ButtonRelease) {
        if (!RecordActive) { StartRec(); } else { StopRec(); }
        ButtonRelease = false;
      }    
      millisDebounce = millis() + WAIT_DEBOUNCE;
    } else { ButtonRelease = true; }
  }
// buffer 0 full save to card
  if (ByteCount % BUF_SIZEx2 == BUF_SIZE && RecordActive) { 
    SD_file.write(buf00,BUF_SIZE); 
    ByteSaved+= BUF_SIZE; 
  }
// buffer 1 full save to card
  if (ByteCount % BUF_SIZEx2 == 0 && RecordActive) { 
    SD_file.write(buf01,BUF_SIZE); 
    ByteSaved+= BUF_SIZE; 
  }
// check for file recorded and saved, if so wait replay time then output  
  if (FileSaved && !RecordActive) {
    if (millisWaitReplay > 0) {
      if (millis() > millisWaitReplay) {
        OutputWav_File(Wav_File);
        GetWavFileName(true);               // update file name after file has been output.
      }
    }
  }
// check for inactive time expired then output a random file
  if (Replay && !FileSaved && !RecordActive) {
    if (millisWaitRandom > 0) {
      if (millis() > millisWaitRandom) {
        OutputRandom_File(false);
      }
    }
    if (millisWaitMusic > 0) {
      if (millis() > millisWaitMusic) {
        OutputRandom_File(true);
        millisWaitMusic = millis() + WAIT_MUSIC;
      }
    }
  }
}
//==================================================================================================
//==================================================================================================
//==================================================================================================
void OutputRandom_File(bool music) {

  char f[13] = "rec00000.wav";    
  char m[13] = "play0000.wav";    
  int num = 0;

  num = random(10);
  if (music) {
    OCR2A = 255;             // music files 8000Hz sample rate
    m[7] = char(num + 48);   // convert to ASCII 
    OutputWav_File(m);
    OCR2A = 127;              // set rate back to 15625Hz rate
  } else {
    f[7] = char(num + 48);  // convert to ASCII 
    OutputWav_File(f);
  }
  return;
}
//==================================================================================================
void OutputWav_File(char* _File) {

//  unsigned long i = 0L;
  
  digitalWrite(RedLED,HIGH);
  digitalWrite(GreenLED,LOW);

  ByteCount = 0;
  ByteSaved = 0;
  Buffer = false;     // point to buffer 0

  if (SD_file.open(_File, O_READ)) {      // if file does not exist do nothing
    SD_file.seekSet(40);

// get data size of file
    byte1 = (byte)(SD_file.read());
    byte2 = (byte)(SD_file.read());
    byte3 = (byte)(SD_file.read());
    byte4 = (byte)(SD_file.read());
    dataSize = byte4; 
    dataSize = dataSize << 8;
    dataSize = dataSize + byte3; 
    dataSize = dataSize << 8;
    dataSize = dataSize + byte2; 
    dataSize = dataSize << 8;
    dataSize = dataSize + byte1;

    SD_file.read(buf00,BUF_SIZE);         // fill buffers before output start
    SD_file.read(buf01,BUF_SIZE);

    digitalWrite(LM386pwr,LOW);  // power on
// enable interrupts    
    sbi (TIMSK2, OCIE2A); 
// output the file data, stay in this loop till all of the file sent
// interrupt routine sends byte, this gets new buffer data when necessary
    while (ByteSaved < dataSize) {
      if (ByteCount % BUF_SIZEx2 == BUF_SIZE) { 
        SD_file.read(buf00,BUF_SIZE); 
        ByteSaved+= BUF_SIZE; 
      }
      if (ByteCount % BUF_SIZEx2 == 0) { 
        SD_file.read(buf01,BUF_SIZE); 
        ByteSaved+= BUF_SIZE; 
      }
    }
// disable interrupts    
    cbi (TIMSK2, OCIE2A); 
    OCR1A = 1;
    digitalWrite(LM386pwr,HIGH);  // power off
  }
  SD_file.close();
  millisWaitReplay = 0L;
  millisWaitRandom = millis() + WAIT_RANDOM;
  FileSaved = false;
  Replay = true;
  digitalWrite(RedLED,LOW);
  digitalWrite(GreenLED,HIGH);
  return;
}
//==================================================================================================
//==================================================================================================
void GetWavFileName(bool update_name) {

  bool fileok = true;
  
// check SD card files
// check if Wav_Name exists on SD card
// check if it has a valid file size
// check if the name stored in the file is valid "rec00000.txt"

  if (sd.exists(Wav_Name)) {                    
    if (SD_file.open(Wav_Name, O_RDWR)) {
      SD_file.rewind();
      if (SD_file.fileSize() == 12) {            
        SD_file.read(Wav_File, 12);
        if ((Wav_File[0] == 'r') && (Wav_File[1] == 'e') && (Wav_File[2] == 'c')) {
          for (int i = 3; i < 8; i++) {
            if ((Wav_File[i] < 48) || (Wav_File[i] > 57)) { fileok = false; }
          }
          if (fileok && update_name) {
            int n0 = (int)(Wav_File[7]);
            if (n0++ == 57) { n0 = 48; }                // note: n0 is incremented after the compare
            Wav_File[7] = char(n0);
            SD_file.rewind();
            SD_file.write(Wav_File);
          }
        } else { fileok = false; }
      } else { fileok = false; }
      SD_file.close();
    } else {
//Serial.println(F("Cannot open wav_name.txt"));
       SD_Error();                              // never returns from here
    }
  } else { fileok = false; }
// no record of valid files or the Wav_Name file is corrupt, then start at beginning 
  if (! fileok) {
//Serial.println(F("Creating new wav_name.txt ....."));
    SD_file.open(Wav_Name, O_CREAT | O_TRUNC | O_RDWR);
    SD_file.write(Wav_File);                              
  }
// close Wav_Name after we have next Wav_File name 
  SD_file.close();
  millisDelay(500);
  return;
}
//==================================================================================================
void StartRec() {

// try and get a random seed, ie it is not known when record started.
  randomSeed(millis());

// indicate recording started
  digitalWrite(RedLED,HIGH);    
  digitalWrite(GreenLED,LOW);

  ByteCount = 0;
  ByteSaved = 0;
  Buffer = false;     // point to buffer 0
  Replay = false;
  FileSaved = false;
  RecordActive = true;
  millisWaitReplay = 0L;
  millisWaitRandom = 0L;
  millisWaitMusic = 0L;
  FileSaved = false;

  SD_file.open(Wav_File, O_CREAT | O_TRUNC | O_RDWR);

// write the initial WAV header to file
  SD_file.write("RIFF");

  byte1 = fileSize & 0xff;
  byte2 = (fileSize >> 8) & 0xff;
  byte3 = (fileSize >> 16) & 0xff;
  byte4 = (fileSize >> 24) & 0xff;  
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

  SD_file.write("WAVE");
  SD_file.write("fmt ");

  byte1 = waveChunk & 0xff;
  byte2 = (waveChunk >> 8) & 0xff;
  byte3 = (waveChunk >> 16) & 0xff;
  byte4 = (waveChunk >> 24) & 0xff;  
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

  byte1 = waveType & 0xff;
  byte2 = (waveType >> 8) & 0xff;
  SD_file.write(byte1);  SD_file.write(byte2); 

  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  SD_file.write(byte1);  SD_file.write(byte2); 

  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;  
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

  byte1 = bytesPerSec & 0xff;
  byte2 = (bytesPerSec >> 8) & 0xff;
  byte3 = (bytesPerSec >> 16) & 0xff;
  byte4 = (bytesPerSec >> 24) & 0xff;  
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);
  
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  SD_file.write(byte1);  SD_file.write(byte2); 
  
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  SD_file.write(byte1);  SD_file.write(byte2); 
  
  SD_file.write("data");
  
  byte1 = dataSize & 0xff;
  byte2 = (dataSize >> 8) & 0xff;
  byte3 = (dataSize >> 16) & 0xff;
  byte4 = (dataSize >> 24) & 0xff;  
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

// enable timer interrupt, start recording audio
// interrupt routine gets the data
  sbi (TIMSK2, OCIE2A); 

  return;  
}
//==================================================================================================
void StopRec() {
    
// disable timer interrupt
  cbi (TIMSK2, OCIE2A); 

// Update the WAV file with its file size and data size in bytes  
// values saved in file LS byte first. ie 129,024 bytes = 0001f800h. 00 f8 01 00 stored in file 

// FileSize is 36 byte longer than DataSize due to header information but doesnt include first 8 bytes.
//      "RIFF" - 4 bytes, FileSize - 4 bytes.
// So actual file size will be Datasize + 36 + 4 + 4 bytes
  byte1 = (ByteSaved + 36) & 0xff;
  byte2 = ((ByteSaved + 36) >> 8) & 0xff;
  byte3 = ((ByteSaved + 36) >> 16) & 0xff;
  byte4 = ((ByteSaved + 36) >> 24) & 0xff;  
  SD_file.seekSet(4);
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

// Update DataSize, number of bytes in audio wave form
  byte1 = ByteSaved & 0xff;
  byte2 = (ByteSaved >> 8) & 0xff;
  byte3 = (ByteSaved >> 16) & 0xff;
  byte4 = (ByteSaved >> 24) & 0xff;  
  SD_file.seekSet(40);
  SD_file.write(byte1);  SD_file.write(byte2);  SD_file.write(byte3);  SD_file.write(byte4);

  SD_file.close();

  millisWaitReplay = millis() + WAIT_REPLAY;
  millisWaitMusic = millis() + WAIT_MUSIC;
  RecordActive = false;
  Replay = false;
  FileSaved = true;

// indicate recording stopped
  digitalWrite(RedLED,LOW);
  digitalWrite(GreenLED,HIGH);
  return;  
}
//==================================================================================================
ISR(TIMER2_COMPA_vect) {

  if (RecordActive) {
    sbi(ADCSRA, ADSC);                  // start ADC sample
    while(bit_is_set(ADCSRA, ADSC));    // wait until ADSC bit goes low = new sample ready, 
                                        //           about 6.5uSecs at 2MHz ADC clock
    ByteCount++;                        
    bufByteCount++;
    if (bufByteCount == BUF_SIZE) {
      if (! Buffer) { Buffer = true; } else { Buffer = false; }
      bufByteCount = 0;
    }
    if (! Buffer) { buf00[bufByteCount] = ADCH; } else { buf01[bufByteCount] = ADCH; }
  }
  if (FileSaved || Replay) {
    ByteCount++;                        
    bufByteCount++;
    if (bufByteCount == BUF_SIZE) {
      if (! Buffer) { Buffer = true; } else { Buffer = false; }
      bufByteCount = 0;
    }
    if (! Buffer) { OCR1A = buf00[bufByteCount]; } else { OCR1A = buf01[bufByteCount]; }
  }
}
//==================================================================================================
void Setup_Timer_1_2_and_ADC() {

// Timer 2 setup, Use compare match to interrupt to read ADC value, also to output bytes on Timer 1 OC1A pin.
//    (note: SD card module has 256 times interrupt rate to write buffer before next buffer ready.)
// WGM22,21,20 = 010. CTC mode - clear timer on compare match. OCR2A sets top of counter.
// CS22,21,20 = 010. Prescaler 8.
// Interupt frequency  = CPU clock (16MHz) / (Prescale * (OCR2A + 1))
//  16MHz/(8*(250+1)) = 7968.127 
//  For a 7812.5Hz rate OCR2A = 16MHz/(7812.5*8) - 1 =  255. (62,500 / 8 = 7812.5)
//  For a 8KHz rate OCR2A = 16MHz/(8KHz*8) - 1 =  249. 
//  For a 15,625Hz rate OCR2A = 16MHz/(15625*8) - 1 =  127. (62,500 / 4 = 15625)
  
  TCCR2A &= ~_BV(WGM22);  // 
  TCCR2A |= _BV(WGM21);   // 
  TCCR2A &= ~_BV(WGM20);  // 
                          
  TCCR2B &= ~_BV(CS22);   // Timer2 Clock Prescaler to : 8
  TCCR2B |= _BV(CS21);   
  TCCR2B &= ~_BV(CS20);  

  OCR2A = 127;            // Compare Match register

// other interrupt frequencies
/*  Hard to get exact rate correctly. 16MHz should be 16M384Hz for exact rates.
 *  OCR2A = 89;           // 16MHz/(8*(89+1)) = 22,222.22222Hz 
 *  OCR2A = 90;           // 16MHz/(8*(90+1)) = 21,978.02197Hz
 *  OCR2A = 91;           // 16MHz/(8*(91+1)) = 21,730.13043Hz 
 *   
 *  The following evenly divide into 62,500 carrier rate. 
 *  OCR2A = 255;           // 16MHz/(8*(X+1)) = 7,812.5   128uSecs
 *                            16MHz = 7,812.5 * (8*(X+1)) 
 *                            8*(X+1)  = 2048
 *                            X+1 = 256
 *                            X = 255
 *                            Time to write buffer 256 * 128uS = 32.768mS
 *                            
 *  OCR2A = 127;           // 16MHz/(8*(X+1)) = 15,625    64usecs
 *                            16MHz = 15,625 * (8*(X+1)) 
 *                            8*(X+1)  = 1024
 *                            X+1 = 128
 *                            X = 127
 *                            Time to write buffer 256 * 64uS = 16.384mS
 *                            
 *  OCR2A = 63;           // 16MHz/(8*(X+1)) = 31,250     32uSecs
 *                            16MHz = 31,250 * (8*(X+1)) 
 *                            8*(X+1)  = 512
 *                            X+1 = 64
 *                            Time to write buffer 256 * 128uS = 8.192mS
 *                            X = 63
*/
//........................
// Timer 1 setup. Outputs audio on pin 9 OCR1A. 
// PWM clock = 62,500Hz. Pulse width = size of the byte.  

// Counts from BOTTOM (0x00) to TOP (0xff) then restarts from the BOTTOM, 256 cycles.
// With no prescaler, highest frequency is F_CPU (16MHz) / 256 = 62,500Hz. 16uSecs.
// OC1A connected to the output, pin 9.
// On a compare match OC1A is cleared. Timer count value = OCR1A value.
// At BOTTOM OC1A is set. This generates the variable pulse width representing the audio signal.
// Higher values will result in a longer pulse width.
// When timer 2 interrupts, OCR1A changed to the new value. 
// If rate is 8000Hz, OCR1A will be the same for 62,500/8000 = 7.8125 cycles. 
// Is this a problem, not integer boundary ??? YES YES YES
//          8th pulse will be distorted.
// 62,500/2 = 31,250; 31,250/2 = 15,625; 15,625/2 = 7812.5; Exactly 8 times.
// Set Timer 2 to 7812.5Hz interrupt.

// Fast PWM 8-bit  WGM13,12,11,10 = 0101
  TCCR1B &= ~_BV(WGM13);
  TCCR1B |= _BV(WGM12);
  TCCR1A &= ~_BV(WGM11);
  TCCR1A |= _BV(WGM10);

// Clear OC1A on Compare Match (Set output to low level) COM1A1,0 = 10
  TCCR1A |= _BV(COM1A1);
  TCCR1A &= ~_BV(COM1A0); 

// I/O Clock / 1 (No prescaling)
  TCCR1B &= ~_BV(CS12);
  TCCR1B &= ~_BV(CS11); 
  TCCR1B |= _BV(CS10);

// values placed here will alter the duty cycle
  OCR1A = 1;
// set data direction on PB1, D9, pin15, OCR1A as output.  
  DDRB |= _BV(0x01);      

//........................
// ADC setup, select analog pin, left adjust, voltage reference, set prescaler for ADC clock
  ADMUX = 0x25;         // REFS1,0 = 00. External AREF, internal Vref turned off
                        //               2.5V reference connected to ARef pin 21.
                        // Input signal from MIC amplifier is 2Vpp 1.25V DC level. 
                        // Signal range is 0.25V to 2.25V.
                        // ADC will produce (1024/2.5)*2  = 819 samples.
                        //    2.5V/1024 = 0.00244140625V per sample.  
                        //    At 0.25V sample = 102.4,  at 2.25V sample = 921.6
                        //    921.6 - 102.4 = 819.2
                        // 
                        // ADLAR = 1. Result is left adjusted, ADCH has upper 8 bits of conversion.
                        //            8 bit resolution. 102 to 921 becomes 5 to 204. 
                        //            Note: never 0 or 255. Avoids timing issues.
                        // MUX3,2,1,0 = 0101. ADC5 pin assigned. Connect audio input to A5.
                        // PCB board design placed MIC output closer to pin 28 (A5) than pin 23 (A0)

  cbi(ADCSRA,ADPS2);    // ADPS2,1,0 bits = 011. Prescaler set to 8 
  sbi(ADCSRA,ADPS1);    //    ADC clock = CPU Clock / 8 = 2MHz,  0.5uSecs per clock cycle.
  sbi(ADCSRA,ADPS0);    //    13 ADC clocks for each conversion, 0.5 * 13  =  6.5uSecs to convert. 

  return;
}
// other settings for ADMUX
/*  
  ADMUX = 0x20;         // REFS1,0 = 00.      External AREF, internal Vref turned off
                        //                    2.5V reference connected to ARef pin 21.
                        // ADLAR = 1.         Result is left adjusted, ADCH has upper 8 bits of conversion.
                        // MUX3,2,1,0 = 0000. ADC0 pin assigned. Connect audio input to A0.

  ADMUX = 0x65;         // REFS1,0 = 01.      AVcc with external capacitor at AREF pin
                        // ADLAR = 1.         Result is left adjusted, ADCH has upper 8 bits of conversion.
                        // MUX3,2,1,0 = 0101. ADC5 pin assigned. Connect audio input to A5.

  ADMUX = 0x60;         // REFS1,0 = 01.      AVcc with external capacitor at AREF pin
                        // ADLAR = 1.         Result is left adjusted, ADCH has upper 8 bits of conversion.
                        // MUX3,2,1,0 = 0101. ADC0 pin assigned. Connect audio input to A0.
*/
//==================================================================================================
void SD_Error (void) {
// flash LED twice per second indicating an error, most likely in SD card
  while(1) {
    digitalWrite(RedLED,!digitalRead(RedLED));
    millisDelay(500);
  }
}
//==================================================================================================
void millisDelay (int d) {
  millisCount = millis() + d;
  while (millis() < millisCount) {}
  return;
}
//==================================================================================================
/*void printSDfileList(void) {

  File root = sd.open("/");
  if (root) {
    root.rewind();
    root.ls(LS_SIZE);
//    root.ls(LS_DATE | LS_SIZE);
  }
  return;
}*/
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.