PIC Sound Generator

Discussion in 'Embedded Systems and Microcontrollers' started by ELECTRONERD, Aug 10, 2010.

  1. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    Hey Everyone,

    I haven't been on AAC for a little while, my job has kept me busy. I still browse the other posts when I can, and I try to remain assiduous at my projects after I finish work each day, if time permits of course. :)

    Project:

    I just began programming with PICs and have run through a series of projects to exercise my skill level in programming; this particular project is entitled a "PIC Sound Generator" in which it will generate notes at my command.

    Theory & Operation:

    I begin with all my header files, definings, etc. until I reach my first function. This function allows me to enter a note that I previously stored into an array, and then define how long of a duration I wish it to play for.

    My frequency is set by Timer0 on the PIC, in which I rapidly turn RA0 on and off depending on my TMR0L and TMR0H values, and furthermore, my array value. TMR0H and TMR0L enable me to use 16-bit mode since my lowest note is 1515μS. Remember that I wish to know my lowest frequency note since the delay is longer. If I were to use 8-bit, my maximum delay would be 128μS. Thus, equation for 16-Bit timer:

    Frequency Delay = [(65536 - Delay)*0.5uS]2

    Which simplifies to:

    Frequency Delay (uS) = (65536 - Delay)

    It has already been noted to me that Timer2 would be a better choice, but I wish to become more familliar with all the PICs abilities and attributes, so I will eventually learn about Timer2 as well.

    In the main() function, you see I have a long list of the playNote function commands. These, as I mentioned, give me what note I want and for how long I wish to play it. The latter statement is based on whole notes (actually quarter notes) and half notes (actually eighth notes). The music is depicted here:

    http://upload.wikimedia.org/wikipedia/commons/4/49/Gstk.png

    The time signature is 3/4; meaning there are 3 quarter (1/4) notes per measure; or section in my comments. Therefore, to make the music even, a section must eventually add up to 3 quarter notes. Otherwise, the music becomes convoluted and not very pleasant to listen to. I can, at my disposal, change how fast I want my music to play if I change my WHOLE or HALF values previously defined at the beginning. Note that they correlate with one another; if I have 800 as the WHOLE, I must have half of that for the HALF value. I was fortunate to have experience in this regard (I play classical guitar), so that made things much easier.


    Code:
    • Using PIC18F1320, with C18 compiler
    Code ( (Unknown Language)):
    1. //
    2. // CreatingSound2.C Song "God Save The Queen"
    3. //
    4.  #include <p18f1320.h>
    5.  #include <delays.h>
    6.  #include <stdlib.h>
    7.  #pragma config OSC=INTIO2, WDT=OFF, LVP=OFF, DEBUG=ON
    8.  #define PAUSE Delay1KTCYx(50);
    9.  #define WHOLE 1000
    10.  #define HALF 500
    11.  #define LONG 2000
    12.  
    13. void playNote(unsigned int note, unsigned int timer)   // Note & Timer driver; determines which note will be played and for how long
    14.         {    
    15.     unsigned int times, freq, notes[] = {64021, 64105, 64185, 64260, 64331, 64398, 64462, 64522, 64578, 64632, 64682, 64730, 64774, 64817, 64856, 64895};  
    16.                 freq = notes[note];   // Determine frequency based on array value
    17.                 INTCONbits.TMR0IF = 0;   // Clear OVF Flag
    18.                 LATA = 0x01;   // Initialize LATA
    19.                 TMR0L = freq & 0xFF;    
    20.                 TMR0H = (freq >> 8) & 0xFF;            
    21.                 T0CON|=0x80;
    22.  
    23.                 for(times=timer;times>0;times--)   // Duration of playing
    24.                 {
    25.                                 TMR0H = (freq >> 8) & 0xFF;   //Load TMR0H byte first
    26.                                 TMR0L = freq & 0xFF;   // Load TMR0L byte next
    27.                                 while(!INTCONbits.TMR0IF);   // Wait for timer
    28. `        INTCONbits.TMR0IF = 0;   // Clear OVF Flag
    29.         LATA = ~LATA;   // Invert output  
    30.                 }
    31.    PAUSE;
    32.         }
    33.  
    34.  void main()
    35.  {
    36.  
    37.  OSCCONbits.IRCF0=1;
    38.  OSCCONbits.IRCF1=1;
    39.  OSCCONbits.IRCF2=1;
    40.  T0CONbits.TMR0ON = 0;   // Don't turn timer on yet
    41.  T0CONbits.T08BIT = 0;  // Timer0 is configured as 16-bit timer
    42.  T0CONbits.T0CS = 0;   // Use internal clock
    43.  T0CONbits.PSA = 1;   // Prescaler is not assigned
    44.  T0CONbits.T0PS2 = 0;
    45.  T0CONbits.T0PS1 = 0;
    46.  T0CONbits.T0PS0 = 0;
    47.  while(!OSCCONbits.IOFS);
    48.  TRISA = 0b11111110;   // Use RA0 as output
    49.  
    50.  while(1)
    51.  {
    52.   playNote(3, WHOLE);  // Section 1
    53.   playNote(3, WHOLE);
    54.   playNote(3, WHOLE);
    55.  
    56.   playNote(1, WHOLE);  // Section 2
    57.   playNote(3, HALF);
    58.   playNote(5, WHOLE);
    59.  
    60.   playNote(7, WHOLE);  // Section 3
    61.   playNote(7, WHOLE);
    62.   playNote(8, WHOLE);
    63.  
    64.   playNote(5, WHOLE);  // Section 4
    65.   playNote(7, HALF);
    66.   playNote(8, WHOLE);
    67.  
    68.   playNote(12, WHOLE); // Section 5
    69.   playNote(0, WHOLE);
    70.   playNote(12, WHOLE);
    71.  
    72.   playNote(7, WHOLE);  // Section 6
    73.   playNote(8, HALF);
    74.   playNote(10, WHOLE);
    75.  
    76.   playNote(12, WHOLE); // Section 7
    77.   playNote(12, WHOLE);
    78.   playNote(13, WHOLE);
    79.  
    80.   playNote(10, WHOLE); // Section 8
    81.   playNote(10, HALF);
    82.   playNote(12, WHOLE);
    83.  
    84.   playNote(5, WHOLE);  // Section 9
    85.   playNote(1, WHOLE);
    86.   playNote(3, HALF);
    87.  
    88.   playNote(3, LONG);  // Section 10
    89.  
    90.   playNote(10, WHOLE); // Section 11
    91.   playNote(10, WHOLE);
    92.   playNote(10, WHOLE);
    93.  
    94.   playNote(10, WHOLE); // Section 12
    95.   playNote(8, HALF);
    96.   playNote(7, WHOLE);
    97.  
    98.   playNote(8, WHOLE);  // Section 13
    99.   playNote(8, WHOLE);
    100.   playNote(8, WHOLE);
    101.  
    102.   playNote(8, WHOLE);  // Section 14
    103.   playNote(7, HALF);
    104.   playNote(5, WHOLE);
    105.  
    106.   playNote(5, WHOLE);  // Section 15
    107.   playNote(7, WHOLE);
    108.   playNote(7, HALF);
    109.  
    110.   playNote(0, LONG);  // Section 16
    111.  
    112.   playNote(15, WHOLE); // Section 17
    113.   playNote(15, WHOLE);
    114.   playNote(15, WHOLE);
    115.  
    116.   playNote(15, WHOLE); // Section 18
    117.   playNote(13, HALF);
    118.   playNote(12, WHOLE);
    119.  
    120.   playNote(13, WHOLE); // Section 19
    121.   playNote(13, WHOLE);
    122.   playNote(13, WHOLE);
    123.  
    124.   playNote(13, WHOLE); // Section 20
    125.   playNote(12, HALF);
    126.   playNote(10, WHOLE);
    127.  
    128.   playNote(7, WHOLE);  // Section 21
    129.   playNote(8, HALF);
    130.   playNote(7, HALF);
    131.   playNote(5, HALF);
    132.   playNote(3, HALF);
    133.  
    134.   playNote(7, WHOLE);  // Section 22
    135.   playNote(8, HALF);
    136.   playNote(10, WHOLE);
    137.  
    138.   playNote(12, WHOLE); // Section 23
    139.   playNote(5, WHOLE);
    140.   playNote(3, HALF);
    141.  
    142.   playNote(3, LONG);  // Section 24
    143.  
    144.   playNote(12, WHOLE); // Section 25
    145.   playNote(13, HALF);
    146.   playNote(12, HALF);
    147.   playNote(10, HALF);
    148.   playNote(8, HALF);
    149.  
    150.   playNote(7, WHOLE);  // Section 26
    151.   playNote(5, HALF);
    152.   playNote(3, WHOLE);
    153.  
    154.   playNote(5, WHOLE);  // Section 27
    155.   playNote(7, WHOLE);
    156.   playNote(7, HALF);
    157.  
    158.   playNote(0, LONG);  // Section 28
    159.  
    160.   Delay100TCYx(255);
    161.  }
    162. }

    A picture of my setup is depicted in the attachements and to see it working click the following link:

    http://s974.photobucket.com/albums/ae227/ElectroNerdy/uC%20Video/?action=view&current=PICSoudGenerator.mp4&newest=1

    On the o'scope you'll notice that the waveform is somewhat distorted, this is due to the speaker. If I were to take that off, not only would you not have any sound (obviously), but it would then give you an adequate square wave.

    I hope you all enjoyed this! If you have any suggestions or comments for me, please feel free to mention them! I would be grateful for any comments in order to edify myself.

    I would like to thank 3v0 and Hayato for their help!

    Austin
     
    Last edited: Aug 10, 2010
    #1
  2. russ_hensel

    russ_hensel Well-Known Member

    Joined:
    Jan 11, 2009
    818
    47
    and your question is?....
     
    #2
  3. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    I apologize, I suppose this particular thread should have been moved to the Projects forum! It is a completed project I recently did, so I thought I would share it with everyone.

    Sorry for the misunderstanding!

    Thanks,

    Austin
     
    #3
  4. retched

    retched AAC Fanatic!

    Joined:
    Dec 5, 2009
    5,201
    309
    The ability to read an external file, possibly from a memory card, to enable you to compose separate 'songs' without needing separate PICs to play them would be a nice next logical step.

    This is a nice project for folks who wish to get involved in PIC programming and in-circuit programming.

    It gives instant feedback of a job-well-done.

    Good Job.
     
    #4
  5. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    Thanks Retched!

    Now I'm storing additional songs into 2D arrays, containing the notes and time values. I plan on having three switches which will enable me to select between the three different songs. I'll post the results here when I finish!
     
    #5
  6. bertus

    bertus Administrator

    Joined:
    Apr 5, 2008
    13,935
    1,760
    Hello,

    When you made the changes, can you also post the schematic?
    Then it would be a complete project.

    Bertus
     
    #6
  7. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    Hi Bertus,

    It really isn't much of a schematic, simply a 47μF cap connected from the RA0 I/O pin and then to the speaker.

    If you think it is necessary, I can make a schematic.
     
    #7
  8. retched

    retched AAC Fanatic!

    Joined:
    Dec 5, 2009
    5,201
    309
    Might as well post the schematic. Im sure this will be a popular project with first time uC users. At least that way they have a schematic to compare their build against, no matter how simple or small.
     
    #8
  9. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    Alright, then I'll go ahead and post it for everyone. However, this project is a progressional type of one, thus I am currently working on it with several more features. I'll post both the schematics when I finish.

    Till Then,

    Austin
     
    #9
  10. ELECTRONERD

    ELECTRONERD Thread Starter Senior Member

    Joined:
    May 26, 2009
    1,145
    16
    Hey Everyone,

    Here is the schematic, as requested in addition to the new code:

    http://mufnet.net/index.php?show=1261

    Code ( (Unknown Language)):
    1. [FONT=monospace]
    2. [LIST=1]
    3. [*]//
    4. [*]//      CreatingSound3.C
    5. [*]//
    6. [*]        #include <p18f1320.h>
    7. [*]        #include <delays.h>
    8. [*]        #include <adc.h>
    9. [*]        #pragma config OSC=INTIO2, WDT=OFF, LVP=OFF, DEBUG=ON
    10. [*]        #define PAUSE Delay1KTCYx(50);
    11. [*]        rom const unsigned int songs[6][80] = {
    12. [*]                                                                {0, 5, 7, 8, 10, 8, 0, 0, 5, 7, 8, 0, 8, 5, 12, 10, 12, 17, 19, 20, 22, 20, 12, 12, 17, 19, 20, 12, 20, 17, 24, 22, 12, 12, 17, 19, 20, 17, 24, 20, 29, 17, 17, 20, 19, 17, 24, 20, 17, 12, 12, 17, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    13. [*]                                                                {500, 1000, 1500, 250, 250, 1000, 500, 500, 1500, 250, 250, 250, 250, 250, 250, 1500, 500, 1000, 1500, 250, 250, 1000, 500, 500, 1500, 250, 250, 250, 250, 250, 250, 1500, 250, 250, 500, 250, 250, 250, 250, 250, 1000, 250, 250, 250, 250, 250, 1500, 250, 250, 500, 500, 1500, 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    14. [*]                                                                {7, 9, 10, 9, 10, 12, 14, 15, 14, 12, 10, 14, 15, 14, 19, 17, 19, 12, 14, 15, 14, 30, 10, 12, 14, 12, 10, 14, 9, 10, 9, 9, 10, 12, 10, 9, 7, 9, 7, 6, 10, 9, 10, 9, 7, 6, 14, 7, 12, 10, 9, 14, 15, 17, 15, 14, 12, 9, 7, 6, 7, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    15. [*]                                                                {500, 500, 500, 250, 500, 500, 500, 500, 500, 250, 500, 500, 500, 500, 500, 250, 500, 500, 500, 500, 1500, 500, 500, 500, 500, 250, 500, 1500, 500, 250, 500, 500, 500, 500, 500, 250, 500, 500, 500, 500, 1000, 500, 500, 250, 500, 1000, 500, 500, 250, 500, 1500, 500, 250, 500, 500, 250, 500, 1500, 250, 500, 1500, 5000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    16. [*]                                                                {5, 5, 5, 3, 5, 7, 9, 9, 10, 7, 9, 10, 14, 2, 14, 9, 10, 12, 14, 14, 158, 12, 12, 14, 7, 3, 5, 5, 12, 12, 12, 12, 10, 9, 10, 10, 10, 10, 9, 7, 7, 9, 9, 2, 17, 17, 17, 17, 15, 14, 15, 15, 15, 15, 14, 12, 9, 10, 9, 7, 5, 9, 10, 12, 14, 7, 5, 5, 14, 15, 14, 12, 10, 9, 7, 5, 7, 9, 9, 20},
    17. [*]                                                                {500, 500, 500, 1500, 250, 500, 500, 500, 500, 1500, 250, 500, 500, 500, 500, 1500, 250, 500, 500, 500, 500, 1500, 250, 500, 500, 1000, 250, 1500, 500, 500, 500, 1500, 250, 500, 500, 500, 500, 1500, 250, 500, 500, 500, 250, 1500, 500, 500, 500, 1500, 250, 500, 500, 500, 500, 1500, 250, 500, 500, 250, 250, 250, 250, 1500, 250, 500, 500, 1500, 250, 1500, 500, 250, 250, 250, 250, 1500, 250, 500, 500, 1500, 250, 1500}
    18. [*]                                                          };
    19. [*]void playNote(unsigned int note, unsigned int timer)   // Note & Timer driver; determines which note will be played and for how long
    20. [*]        {
    21. [*]                        unsigned int times, freq, notes[] = {58725, 59107, 59468, 59809, 60131, 60434, 60720, 60990, 61245, 61486, 61714, 61928, 62131, 62322, 62502, 62672, 62833, 62985, 63128, 63263, 63391, 63511, 63625, 63732, 63833, 63929, 64019, 64104, 64104, 64184, 64260, 65536};
    22. [*]         freq = notes[note];   // Determine frequency based on array value
    23. [*]            INTCONbits.TMR0IF = 0;   // Clear OVF Flag
    24. [*]            LATA = 0x01;   // Initialize LATA
    25. [*]            TMR0L = freq & 0xFF;
    26. [*]            TMR0H = (freq >> 8) & 0xFF;
    27. [*]            T0CON|=0x80;
    28. [*]            for(times=timer;times>0;times--)   // Duration of playing
    29. [*]            {
    30. [*]                TMR0H = (freq >> 8) & 0xFF;   //Load TMR0H byte first
    31. [*]                TMR0L = freq & 0xFF;   // Load TMR0L byte next
    32. [*]                while(!INTCONbits.TMR0IF);   // Wait for timer
    33. [*]`                            INTCONbits.TMR0IF = 0;   // Clear OVF Flag
    34. [*]                                LATA = ~LATA;   // Invert output
    35. [*]            }
    36. [*]                PAUSE;
    37. [*]        }
    38. [*]void playSong(unsigned int song)
    39. [*]{
    40. [*]        int time, note, counter;
    41. [*]while(1)
    42. [*]{
    43. [*]        if(song==0)     // Song 1
    44. [*]        {
    45. [*]                for(counter=0;counter<53;counter++)
    46. [*]                {
    47. [*]                        note = songs[0][counter];       // Song 1 notes
    48. [*]                        time = songs[1][counter];       // Song 1 time values
    49. [*]                        playNote(note, time);   // Play through notes and times
    50. [*]                }
    51. [*]        break;  // When song 1 finishes, break
    52. [*]        }
    53. [*]        else if(song==1)        // Song 2
    54. [*]        {
    55. [*]                for(counter=0;counter<63;counter++)
    56. [*]                {
    57. [*]                        note = songs[2][counter];       // Song 2 notes
    58. [*]                        time = songs[3][counter];       // Song 2 time values
    59. [*]                        playNote(note, time);   // Play through notes and times
    60. [*]                }
    61. [*]        break;  // When song 2 finishes, break
    62. [*]        }
    63. [*]        else if(song==2)        // Song 3
    64. [*]        {
    65. [*]                for(counter=0;counter<80;counter++)
    66. [*]                {
    67. [*]                        note = songs[4][counter];       // Song 3 notes
    68. [*]                        time = songs[5][counter];       // Song 3 time values
    69. [*]                        playNote(note, time);      // Play through notes and times
    70. [*]                }
    71. [*]        break;  // When song 3 finishes, break
    72. [*]        }
    73. [*]        else
    74. [*]        {
    75. [*]                LATB = 0x08;    // Otherwise, LED 4
    76. [*]        }
    77. [*] }
    78. [*]}
    79. [*]        void main()
    80. [*]        {
    81. [*]        ADCON1 = 0x0E;  // ADC Settings as digital inputs from RA1-RA3
    82. [*]        OSCCONbits.IRCF0=1;
    83. [*]        OSCCONbits.IRCF1=1;
    84. [*]        OSCCONbits.IRCF2=1;
    85. [*]        T0CONbits.TMR0ON = 0;   // Don't turn timer on yet
    86. [*]        T0CONbits.T08BIT = 0;   // Timer0 is configured as 16-bit timer
    87. [*]        T0CONbits.T0CS = 0;       // Use internal clock
    88. [*]        T0CONbits.PSA = 1;   // Prescaler is not assigned
    89. [*]        T0CONbits.T0PS2 = 0;
    90. [*]        T0CONbits.T0PS1 = 0;
    91. [*]        T0CONbits.T0PS0 = 0;
    92. [*]        while(!OSCCONbits.IOFS);
    93. [*]        TRISA = 0xFE;
    94. [*]        TRISB = 0xF0;
    95. [*]        while(1)
    96. [*]        {
    97. [*]               switch(PORTA & 0x0E)
    98. [*]               {
    99. [*]                case 0x0C:
    100. [*]                        LATB = 0x01;
    101. [*]                        playSong(0);
    102. [*]                        break;
    103. [*]                case 0x0A:
    104. [*]                        LATB = 0x02;
    105. [*]                        playSong(1);
    106. [*]                        break;
    107. [*]                case 0x06:
    108. [*]                        LATB = 0x04;
    109. [*]                        playSong(2);
    110. [*]                        break;
    111. [*]                default:
    112. [*]                        LATB = 0x08;
    113. [*]                }
    114. [*]         }
    115. [*]}
    116. [/LIST]
    117. [/FONT]
    118.  
    119.  
    I also have a video of it in action:

    http://s974.photobucket.com/albums/ae227/ElectroNerdy/uC%20Project/?action=view&current=PICSoundGenerator3C.mp4&newest=1

    Thanks!
     
    Last edited: Sep 14, 2010
    #10
Loading...