I had need for sound on a pic...
Most of my products use a beeper for "Hey! You pressed a button" or "Excuse me! Something is in alarm!!"
This is fine for most applications, but we all know that the micro goes off to play a note / sound and takes the whole duration waiting...
I found some interrupt driven sound routines on the net... One by a guy called Craig Peacock... I like this one because of the note parser he developed... Basically a string with several notes in several octaves in several durations... It was written as a ringtone player..
Now my latest product needs notifications, so a simple beeper will not suffice.. Using Craig's idea I can implement complex note sequences to make identifiable notifications..
I have changed it slightly as Craig used A ~ A as an octave, where I found most tunes are C ~ C.. I have also included an "End Of Tune" character '~'.. All I need to do is call PlayNotification(sound);
The only other thing is the note frequencies and durations are FOSC dependant.. I haven't written an algorithm to solve that but have included the calculation to help it along.
Here are the note frequencies:-
C = 261.63 = 3.8222mS
D = 277.18 = 3.6077mS
D# = 311.13 = 3.2141mS
E = 329.63 = 3.0337mS
F = 349.23 = 2.8364mS
F# = 369.99 = 2.7027mS
G = 392.00 = 2.5510mS
G#= 415.30 = 2.4079mS
A = 440.00 = 2.2727mS
A# =466.16 = 2.1452mS
B = 493.88 = 2.0248mS
The octaves are >> 1 or << 1 either side.. In effect doubled or halved..
The operation uses CCP module in compare.. Load the CCP1RL and CCP1RH with half the frequency ( pin toggled automatically.. Two needed for a cycle )
The only NON automatic thing is the timer isn't reset!! so we need to do that in an interrupt.. Also the duration is on Timer 0..
CCPR1 = mS / 2 / TOSC... I'm using 32Mhz..
Example:-
B = 2.0248mS.. 2.0248mS/2 = 1.0124mS / .2uS = 8099 or 0x1FA3
This will give 493.88 hz to the speaker.
Here is a small code part... Plays Axel F..
I hope it is useful to others..
I am mostly online as you all know for questions..
Most of my products use a beeper for "Hey! You pressed a button" or "Excuse me! Something is in alarm!!"
This is fine for most applications, but we all know that the micro goes off to play a note / sound and takes the whole duration waiting...
I found some interrupt driven sound routines on the net... One by a guy called Craig Peacock... I like this one because of the note parser he developed... Basically a string with several notes in several octaves in several durations... It was written as a ringtone player..
Now my latest product needs notifications, so a simple beeper will not suffice.. Using Craig's idea I can implement complex note sequences to make identifiable notifications..
I have changed it slightly as Craig used A ~ A as an octave, where I found most tunes are C ~ C.. I have also included an "End Of Tune" character '~'.. All I need to do is call PlayNotification(sound);
The only other thing is the note frequencies and durations are FOSC dependant.. I haven't written an algorithm to solve that but have included the calculation to help it along.
Here are the note frequencies:-
C = 261.63 = 3.8222mS
D = 277.18 = 3.6077mS
D# = 311.13 = 3.2141mS
E = 329.63 = 3.0337mS
F = 349.23 = 2.8364mS
F# = 369.99 = 2.7027mS
G = 392.00 = 2.5510mS
G#= 415.30 = 2.4079mS
A = 440.00 = 2.2727mS
A# =466.16 = 2.1452mS
B = 493.88 = 2.0248mS
The octaves are >> 1 or << 1 either side.. In effect doubled or halved..
The operation uses CCP module in compare.. Load the CCP1RL and CCP1RH with half the frequency ( pin toggled automatically.. Two needed for a cycle )
The only NON automatic thing is the timer isn't reset!! so we need to do that in an interrupt.. Also the duration is on Timer 0..
CCPR1 = mS / 2 / TOSC... I'm using 32Mhz..
Example:-
B = 2.0248mS.. 2.0248mS/2 = 1.0124mS / .2uS = 8099 or 0x1FA3
This will give 493.88 hz to the speaker.
Here is a small code part... Plays Axel F..
C:
#include<xc.h>
#pragma config PLLEN = 1
#pragma config WDTE = 0
#pragma config FOSC = INTOSC
const unsigned char Melody[] = {"32p,8g,8p,16a#.,8p,16g,16p,16g,8c6,8g,8f,8g,8p,16d.6,8p,16g,16p,16g,8d#6,8d6,8a#,8g,8d6,8g6,16g,16f,16p,16f,8d,8a#,2g,4p,16f6,8d6,8c6,8a#,4g,8a#.,16g,16p,16g,8c6,8g,8f,4g,8d.6,16g,16p,16g,8d#6,8d6,8a#,8g,8d6,8g6,16g,16f,16p,16f,8d,8a#,2g,~"};
#define _XTAL_FREQ 32000000
volatile int ticks=0;
volatile unsigned char tune;
void getnote(void)
{
unsigned int note;
if(Melody[tune] == '~'){CCPR1L = CCPR1H = 0; GIE = 0; return;}
// First duration
if(Melody[tune] == '3' && Melody[tune+1] == '2')
{
ticks = 8;
tune+=2;
}
else if(Melody[tune] == '1' && Melody[tune+1] == '6')
{
ticks = 16;
tune+=2;
}
else if(Melody[tune] == '8' )
{
ticks = 32;
tune++;
}
else if(Melody[tune] == '4' )
{
ticks = 64;
tune++;
}
else if(Melody[tune] == '2' )
{
ticks = 128;
tune++;
}
else if(Melody[tune] == '1' )
{
ticks = 256;
tune++;
}
// Second part natural or Sharpe
if(Melody[tune+1] == '#')
{
switch (Melody[tune])
{
case 'c' : note = 0x385F; break;
case 'd' : note = 0x3238; break;
case 'f' : note = 0x2A3B; break;
case 'g' : note = 0x25A0; break;
case 'a' : note = 0x2185; break;
}
tune+=2;
}
else
{
switch (Melody[tune])
{
case 'c' : note = 0x3BB9; break;
case 'd' : note = 0x3535; break;
case 'e' : note = 0x2F67; break;
case 'f' : note = 0x2CBE; break;
case 'g' : note = 0x27DC; break;
case 'a' : note = 0x2383; break;
case 'b' : note = 0x1FA3; break;
case 'p' : note = 0; break;
}
tune++;
}
// A dotted note is 50% bigger
if(Melody[tune] == '.')
{
ticks*=15;
ticks/=10;
tune++;
}
// Three octaves... Could be five easy..
if(Melody[tune] == '4')
{
note<<=1; // Slow down..
tune++;
}
else if(Melody[tune] == '6')
{
note>>=1; // Speed up..
tune++;
}
// Load CCPR1 pair..
CCPR1L = note & 0xff;
CCPR1H = note>>8 & 0xff;
tune++;
}
// CCP fires and Timer 0 determins duration
void interrupt ISR()
{
if(CCP1IF)
{
TMR1H = TMR1L = 0;
CCP1IF = 0;
}
if(T0IF)
{
ticks--;
if(!ticks)
{
getnote();
}
T0IF = 0;
}
}
// This function outputs LED to 8 LEDS on a 74hc595
void leds(char LED)
{
char x,y;
unsigned char MSK, dummy ;
for(x=0;x<4;x++)
{
MSK = 0x80;
dummy = LED;
for(y=0;y<8;y++)
{
RA4 = 0;
if(dummy & MSK) RA4 = 1;
RA1 = 1;
NOP();
RA1 = 0;
MSK>>=1;
}
}
RA5 = 1;
NOP();
RA5 = 0;
}
void main(void)
{
char x=0;
OSCCON = 0xF0; // As fast as possible..
CCP1CON = 0x02; // compare and toggle CCP pin
TRISA = 1;
ANSELA = 0;
OPTION_REG = 0xD7; // duration of Note here
TMR1H = TMR1L = 0;
CCP1IE = PEIE = GIE =1; // turn on interrupts
T0IE = 1;
T1CON = 1;
tune =0;
getnote();
while(1)
{
x++;
leds(x);
__delay_ms(250);
if(!RA0) {tune = 0; GIE = 1; getnote(); } // RESTART..
}
}
I hope it is useful to others..
I am mostly online as you all know for questions..