Go Down

Topic: Arduino playing audio .wav file with PWM and external eprom IC (Read 3895 times) previous topic - next topic


The idea is to incorporate voice in MCU project. I've found a good example http://playground.arduino.cc/Code/PCMAudio, modified little to use with my ATmega8 and it's working with data sample stored in MCU. But as I need more space (upto 5 sec. of msg.), I want to use external eprom (24c512 is handy to me & sufficient for my sample).

I've burn eprom with voice sample (8 Mhz, 8bit) & trying to run with the below code. Though I can't get any result (No voice, No led blink in place of speaker, No serial output when using serial monitor)  ! I only assume that the eprom can't response in such a fast ISR/interrupt ?? Pl. tell me if any solution to this problem.

Code: [Select]

// ATMega8, 16Mhz., EPROM 24C512

* based on speaker_pcm
* EPROM Library from : https://github.com/mikeneiderhauser/I2C-EEPROM-Arduino

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <Wire.h>
#include <EEPROM24LC256_512.h>

#define SAMPLE_RATE 8000
unsigned int sounddata_length=65000;
int ledPin = 13;
int speakerPin = 11;
volatile uint16_t sample;
byte lastSample;

//define eeprom chip (1 per chip)
EEPROM256_512 mem_2;

// This is called at 8000 Hz to load the next sample.
    if (sample >= sounddata_length) {
        if (sample == sounddata_length + lastSample) {
            stopPlayback(); }
        else {
            // Ramp down to zero to reduce the click at the end of playback.
            OCR2 = sounddata_length + lastSample - sample; }
    else {
        OCR2 = mem_2.readByte(sample);

void startPlayback()
pinMode(speakerPin, OUTPUT);

    // Set up Timer 2 to do pulse width modulation on the speaker pin.
    ASSR &= ~(_BV(AS2));

    // Set fast PWM mode
    TCCR2 = _BV(WGM21) | _BV(WGM20);
    TCCR2 = (TCCR2 & ~(_BV(COM20))) | _BV(COM21) ;
    TCCR2 = (TCCR2 & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20);
    //Serial.println("Starting, Please wait...");

    // Set initial pulse width to the first sample.
    OCR2 = mem_2.readByte(0);

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

    // Set CTC mode (Clear Timer on Compare Match)
    // 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
    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)
    TIMSK |= _BV(OCIE1A);

    lastSample = mem_2.readByte(sounddata_length-1);
    sample = 0;

void stopPlayback()
    // Disable playback per-sample interrupt.
    TIMSK &= ~_BV(OCIE1A);
    // Disable the per-sample timer completely.
    TCCR1B &= ~_BV(CS10);
    // Disable the PWM timer.
    TCCR2 &= ~_BV(CS20);
    digitalWrite(speakerPin, LOW);

void setup()
    pinMode(ledPin, OUTPUT);

void loop()
{ while (true); }

Used external EPROM lib. https://github.com/mikeneiderhauser/I2C-EEPROM-Arduino, it's tested & working fine.


I originally looked at the same solution, before realising how restrictive it was. I'm now using the SimpleSDAudio library, which allows you to play audio from an SD card on PWM pins.

It might be worth a look for you.

Go Up