Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
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,290
First release
Last update
Rating
5.00 star(s) 1 ratings

Latest reviews

Well explained! Example is a good reference to remember.

Latest threads

New Articles From Microcontroller Tips

Back
Top