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:
    //
    // CreatingSound2.C Song "God Save The Queen"
    //
     #include <p18f1320.h>
     #include <delays.h>
     #include <stdlib.h>
     #pragma config OSC=INTIO2, WDT=OFF, LVP=OFF, DEBUG=ON
     #define PAUSE Delay1KTCYx(50);
     #define WHOLE 1000
     #define HALF 500
     #define LONG 2000
     
    void playNote(unsigned int note, unsigned int timer)   // Note & Timer driver; determines which note will be played and for how long
            {    
        unsigned int times, freq, notes[] = {64021, 64105, 64185, 64260, 64331, 64398, 64462, 64522, 64578, 64632, 64682, 64730, 64774, 64817, 64856, 64895};   
                    freq = notes[note];   // Determine frequency based on array value
                    INTCONbits.TMR0IF = 0;   // Clear OVF Flag
                    LATA = 0x01;   // Initialize LATA 
                    TMR0L = freq & 0xFF;     
                    TMR0H = (freq >> 8) & 0xFF;            
                    T0CON|=0x80;
     
                    for(times=timer;times>0;times--)   // Duration of playing
                    {
                                    TMR0H = (freq >> 8) & 0xFF;   //Load TMR0H byte first
                                    TMR0L = freq & 0xFF;   // Load TMR0L byte next
                                    while(!INTCONbits.TMR0IF);   // Wait for timer
    `        INTCONbits.TMR0IF = 0;   // Clear OVF Flag
            LATA = ~LATA;   // Invert output   
                    }
       PAUSE;
            }
     
     void main()
     {
     
     OSCCONbits.IRCF0=1; 
     OSCCONbits.IRCF1=1;
     OSCCONbits.IRCF2=1;
     T0CONbits.TMR0ON = 0;   // Don't turn timer on yet
     T0CONbits.T08BIT = 0;  // Timer0 is configured as 16-bit timer
     T0CONbits.T0CS = 0;   // Use internal clock
     T0CONbits.PSA = 1;   // Prescaler is not assigned
     T0CONbits.T0PS2 = 0;
     T0CONbits.T0PS1 = 0;
     T0CONbits.T0PS0 = 0;
     while(!OSCCONbits.IOFS);
     TRISA = 0b11111110;   // Use RA0 as output
     
     while(1)
     {
      playNote(3, WHOLE);  // Section 1
      playNote(3, WHOLE);
      playNote(3, WHOLE);
     
      playNote(1, WHOLE);  // Section 2
      playNote(3, HALF);
      playNote(5, WHOLE);
     
      playNote(7, WHOLE);  // Section 3
      playNote(7, WHOLE);
      playNote(8, WHOLE);
     
      playNote(5, WHOLE);  // Section 4
      playNote(7, HALF);
      playNote(8, WHOLE);
     
      playNote(12, WHOLE); // Section 5
      playNote(0, WHOLE);
      playNote(12, WHOLE);
     
      playNote(7, WHOLE);  // Section 6
      playNote(8, HALF);
      playNote(10, WHOLE);
     
      playNote(12, WHOLE); // Section 7
      playNote(12, WHOLE);
      playNote(13, WHOLE);
     
      playNote(10, WHOLE); // Section 8
      playNote(10, HALF);
      playNote(12, WHOLE);
     
      playNote(5, WHOLE);  // Section 9
      playNote(1, WHOLE);
      playNote(3, HALF);
     
      playNote(3, LONG);  // Section 10
     
      playNote(10, WHOLE); // Section 11
      playNote(10, WHOLE);
      playNote(10, WHOLE);
     
      playNote(10, WHOLE); // Section 12
      playNote(8, HALF);
      playNote(7, WHOLE);
     
      playNote(8, WHOLE);  // Section 13
      playNote(8, WHOLE);
      playNote(8, WHOLE);
     
      playNote(8, WHOLE);  // Section 14
      playNote(7, HALF);
      playNote(5, WHOLE);
     
      playNote(5, WHOLE);  // Section 15
      playNote(7, WHOLE);
      playNote(7, HALF);
     
      playNote(0, LONG);  // Section 16
     
      playNote(15, WHOLE); // Section 17
      playNote(15, WHOLE);
      playNote(15, WHOLE);
     
      playNote(15, WHOLE); // Section 18
      playNote(13, HALF);
      playNote(12, WHOLE);
     
      playNote(13, WHOLE); // Section 19
      playNote(13, WHOLE);
      playNote(13, WHOLE);
     
      playNote(13, WHOLE); // Section 20
      playNote(12, HALF);
      playNote(10, WHOLE);
     
      playNote(7, WHOLE);  // Section 21
      playNote(8, HALF);
      playNote(7, HALF);
      playNote(5, HALF);
      playNote(3, HALF);
     
      playNote(7, WHOLE);  // Section 22
      playNote(8, HALF);
      playNote(10, WHOLE);
     
      playNote(12, WHOLE); // Section 23
      playNote(5, WHOLE);
      playNote(3, HALF);
     
      playNote(3, LONG);  // Section 24
     
      playNote(12, WHOLE); // Section 25
      playNote(13, HALF);
      playNote(12, HALF);
      playNote(10, HALF);
      playNote(8, HALF);
     
      playNote(7, WHOLE);  // Section 26
      playNote(5, HALF);
      playNote(3, WHOLE);
     
      playNote(5, WHOLE);  // Section 27
      playNote(7, WHOLE);
      playNote(7, HALF);
     
      playNote(0, LONG);  // Section 28
     
      Delay100TCYx(255);
     }
    }

    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
     

    Attached Files:

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

    Attached Files:

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