PIC Sound Generator

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

  1. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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
  2. russ_hensel

    Well-Known Member

    Jan 11, 2009
    818
    47
    and your question is?....
     
  3. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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
     
  4. retched

    AAC Fanatic!

    Dec 5, 2009
    5,201
    312
    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.
     
  5. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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!
     
  6. bertus

    Administrator

    Apr 5, 2008
    15,446
    2,284
    Hello,

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

    Bertus
     
  7. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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.
     
  8. retched

    AAC Fanatic!

    Dec 5, 2009
    5,201
    312
    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.
     
  9. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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
     
  10. ELECTRONERD

    Thread Starter Senior Member

    May 26, 2009
    1,146
    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
Loading...