Hello,
I am writing here in search of fresh help because anything i tried last days it won't work and i doubt of anything. I hope a fresh mind will find my mistakes.
Thanks in advance.
An arduino uno (16MHz), a speaker, and a spi port to connect SD card.
I want to read a SD card to make pwm sound. Files are pcm, raw at this point. I tested some libraries with some advantages but i want make my own program and then change pwm frequence to hear myself if there is a difference of noise.
There is both buttons and serial usb to give order, because i test sometimes with serial and sometimes without it depending of where i am.
My program blocks just after starting the sampling timer. I added Serial displays to see loop and interrupt activity and nothing works after the timer 2 starts.
/***
Reads raw sound file on SD to make sound on digital pin 9 / OC1A.
Sample rate is 62500 while CPU=16MHz and TOP=255.
***/
#include <Arduino.h>
#include <SD.h>
#define ddd "8b/"
//just a directory to help quick maintenance
long int myfilesize;
int TopCounter;
boolean reading=false;
File mypcmsdfile;
int speakerPin=9; // must respect the timer , oc1a=digital9
void setup()
{
TIMSK2 &= ~_BV(TOIE2); // deactivate sample interrupt just in case
sei(); // global enable interrupt just in case
Serial.begin(115200);
Serial.print("Output is pin "); Serial.println(speakerPin); // a tip to remind the pin, in this program it is 9
if (!SD.begin(4))
{
Serial.println("SD fail");
return;
}
else
{
Serial.println("SD ok");
}
DDRB |= 1 << DDB1; //portb OUT pin 1 , arduino pin 9, timer oc1a
ConfigTim3(); // pwm with custom resolution
}
void loop(void)
{
Serial.print("+");
//a command with buttons
if(analogRead(A0)>1000)
{
letsPlay(ddd"recula.afm",1000,255); // 1000ms of delay and toppwm=255
}
if(analogRead(A1)>1000)
{
letsPlay(ddd"clochea.afm",2000,255);
}
if(analogRead(A2)>1000)
{
letsPlay(ddd"clochea.afm",1000,99);
}
if(analogRead(A3)>1000)
{
letsPlay(ddd"clochea.afm",2000,199);
}
// a command with Serial monitor arduino
switch(Serial.read())
{
case 'q': letsPlay(ddd"recula.afm",1000,255);
break;
case 'w': letsPlay(ddd"recula.afm",1000,99);
break;
case 'e': letsPlay(ddd"clochea.afm",2000,255);
break;
case 'r': letsPlay(ddd"clochea.afm",2000,99);
break;
case 't': letsPlay(ddd"clochea.afm",2000,199);
break;
default:break;
}
}
void ConfigTim3(void)
{
//****** PWM t1
//wgm=1110 pwm top=icr1
TCCR1B &= ~_BV(CS12) & ~_BV(CS11) & ~_BV(CS10); // deactivate pwm
TCCR1A |= 1 << WGM11;
TCCR1A &= ~(1 << WGM10);
TCCR1B |= (1 << WGM13)|(1<<WGM12);//PWM en mode 14, fast pwm, TOP=ICR1, (voir doc page 133)
TCCR1A |= (1 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0); // lazy of reset, setting this bit is enough
//pin 9 , OC1A non inverting mode)
// f=F/(1+ICR)
ICR1=99;//default top pwm
OCR1A = 0; //rapport cyclique fixé à 0% par défaut
//****** sample t2
//normal top=0xff f=62500Hz : wgm=000
//nothing com=0000
//prescale=1 : cs=001 // but is set at start of play
TCCR2B &= ~_BV(CS22) & ~_BV(CS21) & ~_BV(CS20); // deactivate sampling for begin
TCCR2B &= ~_BV(WGM22) & ~_BV(WGM21) & ~_BV(WGM20);
TCCR2A &= ~_BV(COM2A1) & ~_BV(COM2A0) & ~_BV(COM2B1) & ~_BV(COM2B0) ;
TIMSK2 &= ~_BV(TOIE2);
}
ISR(TIMER2_OVF_vect) // overflow interrupt
{
//static int i;
//Serial.print(i++);
Serial.print("*");
if (mypcmsdfile.position() >= myfilesize)
{
stopPlayback();
}
else
{
OCR1A = mypcmsdfile.read();
//OCR1A = mypcmsdfile.read()*(1+TopCounter)/256;
//with thiswe could correct the cyclic rate even if top (so period) is changed
}
}
void letsPlay(char str[], int time, int toppwm)
{
stopPlayback(); // needed because SD.exists cannot find opened files
Serial.print(str);
if(SD.exists(str))
{
Serial.println("...ok.");
startPlayback(str,toppwm);
//if(time) {delay(time); stopPlayback();}
}
else
{
Serial.println("...not found.");
}
}
void startPlayback(char filename[], int toppwm)
{
if(reading)
{
return;
}
reading=true;
mypcmsdfile=SD.open(filename);
//if(mypcmsdfile){Serial.println("open");}else{Serial.println("error");}
myfilesize=mypcmsdfile.size();
mypcmsdfile.seek(0); // indice of start of data
Serial.println(myfilesize);
Serial.println(toppwm);
ICR1=toppwm;
TopCounter=toppwm;
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10); // prescale=1 : cs=001
Serial.println("...");
// the last display which works fine !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20); // prescale=1 : cs=001
Serial.println("..");
TIMSK2 |= _BV(TOIE2); // enable interrupt to sample (overflow)
Serial.println(".");
}
void stopPlayback()
{
TIMSK2 &= ~_BV(TOIE2); //disable interrupt sample
TCCR2B &= ~( _BV(CS22) | _BV(CS21) | _BV(CS20) ); //stop sampling
OCR1A=0;
TCCR1B &= ~( _BV(CS12) | _BV(CS11) | _BV(CS10) ); //stop pwm
digitalWrite(speakerPin, LOW);
mypcmsdfile.close();
reading=false;
}
EDIT: removed the useless call to a missing function which ramped the compare value between zero and the actual value, to begin or stop.