1. 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.
    Dismiss Notice
Ian Rogers

Sound / Frequency on a micro

Sound

  1. Ian Rogers
    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..
    Code (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..