• 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..

Latest threads

EE World Online Articles

Loading

 
Top