Resource icon

Sound / Frequency on a micro 2017-07-30

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..
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..
Author
Ian Rogers
Views
12,532
First release
Last update
Rating
5.00 star(s) 1 ratings

Latest reviews

Well explained! Example is a good reference to remember.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…