PIC18F4550 Audio Output

The 18F4550 output has a maximum drive capability of 25 mA so it’s unable to drive an 8ohm speaker directly (5V max / 8ohm = 625mA peak). The circuit below uses an LM386 to buffer the output. The RC network has a high enough impedance to meet the needed load requirements while providing a small amount of low pass filtering to take a little of the “buzz” sound off of the output.
Audio Amplifier Schematic

The code below makes use of timer 0 and timer 3 to play reveille the wake up bugle call. Timer 3 is programmed to provide half the period of the the note to be played. The output port is toggled on for a half cycle and then off for a half cycle providing a square-wave output at the frequency of the desired note. Timer 0 is programmed for the duration of the note.

void play_note(unsigned int note, unsigned int duration)
{
   int i;
   
   INTCONbits.TMR0IF = 0; TMR0H =  duration / 256;  TMR0L =  duration % 256;  // T0 for duration
   while(!INTCONbits.TMR0IF)
   {
     while(!PIR2bits.TMR3IF &  i) CCP1CON = 0x1C;    //  PWM out is 96 kHz with max DC for a tone half cycle
     while(!PIR2bits.TMR3IF & !i) CCP1CON = 0x00;    //  PWM out is off for a tone half cycle
     PIR2bits.TMR3IF = 0; TMR3H =  note / 256; TMR3L =  note % 256; i = !i;  // T3 for tone half cycle
   }
   wait_ms(10); // brief delay to break the tone before the next one. uses timer 0
}

The play_join subroutine has no break at the end of the note. It can be called repeatedly to provide a note of much longer duration. It’s used in the reveille song to play 1/2 notes.

void play_join(unsigned int note, unsigned int duration) // no break in the tone 
{
   int i;
   
   INTCONbits.TMR0IF = 0; TMR0H =  duration / 256;  TMR0L =  duration % 256;  // T0 for duration
   while(!INTCONbits.TMR0IF)
   {
     while(!PIR2bits.TMR3IF &  i) CCP1CON = 0x1C;    //  PWM out is 96 kHz with max DC for a tone half cycle
     while(!PIR2bits.TMR3IF & !i) CCP1CON = 0x00;    //  PWM out is off for a tone half cycle
     PIR2bits.TMR3IF = 0; TMR3H =  note / 256; TMR3L =  note % 256; i = !i;  // T3 for tone half cycle
   }
}

void reveille(void)
{
// Requires play_note() and play_join() subroutines
// Tone Pitch:
   unsigned int G4 = 50230; // 392.0 Hz, 65,536 - ((1/2*392.0) * 12e6/1) = 50230
   unsigned int C5 = 54070; // 523.3 Hz, 65,536 - ((1/2*523.3) * 12e6/1) = 54070
   unsigned int E5 = 56438; // 659.5 Hz, 65,536 - ((1/2*659.5) * 12e6/1) = 56438
   unsigned int F5 = 56946; // 698.5 Hz, 65,536 - ((1/2*698.5) * 12e6/1) = 56946
   unsigned int G5 = 57883; // 784.0 Hz, 65,536 - ((1/2*784.0) * 12e6/1) = 57883
// Tone Duration:
   unsigned int d_1_4  = 28036; // 400 ms, 65,536 - ( .4 * 12e6/128) = 28036
   unsigned int d_1_8  = 46786; // 200 ms, 65,536 - ( .2 * 12e6/128) = 46786
   unsigned int d_1_16 = 56161; // 100 ms, 65,536 - ( .1 * 12e6/128) = 56161
   // Configure the PWM, and Timer2 for 96 kHz operation
   // Configure TIMER2  U  PS3 PS2 PS1 PS0 ON  PS1 PS0; PreScale = 1
   T2CON = 0x04;    //  0   0   0   0   0   1   0   0
   // Set the PWM Period for 96 kHz by setting PRS(PR2) and TMR2PS(T2CKPS)
   // Given: Tcy = 4 * Tosc = 1/(12 MHz) = 83.333 ns,
   // let T2CKPS = 1, P = 1/96 kHz = 10.4167 us, P = (PR2 + 1) * Tcy * T2CKPS
   // PR2 = (P / (Tcy * TSCKPS)) - 1 =  124
   PR2 = 124;      // Check: Period = 125 * 83.333 n * 1 = 10.417 us, (96.000 kHz)
   // Duty Cycle is a portion of the period in time units
   // DC = (1/4)(CCPR1L:CCP1CON<5:4>) * Tcy * T2CKPS
   // PWM Duty Cycle to ramp from 0 to 1 and then back at the desired tone
   // for DC = 0 * P, 10bit = 4 * (.104167 us * 12 MHz) = 0
   // for DC = 1 * P, 10bit = 4 * (10.417us * 12 MHz) = 500
   CCPR1L = 0x7D;   // 7D = 125, CCP1CON<5:4> = 0b00, configured below
   // Configure CCP1 module for PWM.  Duty Cycle bits set to 0
                     //  U   U  DC1 DC0 MD3 MD2 MD1 MD0
   CCP1CON = 0x0C;   //  0   0   0   0   1   1   0   0, Turn PWM on
   
// Setup Timer 0 to provide the duration of a 1/4 note at 152 bpm
// Timer 0 is the slowest timer with a PSmax = 256
// 152 bpm = 2.5333 bps for a 1/4 note => .39474 s, @ 400 ms, 1/8 = 200, 1/16 = 100
                   //     ON  8B  CS  SE PSA PS2 PS1 PS0
   T0CON = 0x85;   //     1   0   0   0   0   1   0   1  , TMR0 ON, PS = 128
   INTCONbits.TMR0IF = 0; // reset output bit
   // round(2^16 -  * Fcy/128), instruction cycle = Fcy = Fosc/4
   TMR0H =  28036 / 256;  // 65,536 - .4 * 12e6/128 = 28,036 (400 ms)
   TMR0L =  28036 % 256;
   
// Setup Timer 3 to provide 1/2 the period of 392 Hz tone (G4) => 1.2755 ms
// Timer 3 PSmax = 8, Timer 2 PSmax = 16, Timer 1 PSmax = 8
                   //    R16 CP2 PS1 PS0 CP1 SYC  CS  ON
   T3CON = 0x01;   //     0   0   0   0   0   0   0   1  , TMR3 ON, PS = 1
   PIE2bits.TMR3IE = 1; // enable PIR2bits.TMR3IF
   PIR2bits.TMR3IF = 0; // reset output bit
   // round(2^16 -  * Fcy/1), instruction cycle = Fcy = Fosc/4
   TMR3H =  50230 / 256;  // 65,536 - 1.2755e-3 * 12e6/1 = 50230, 50230/256 = 196
   TMR3L =  50230 % 256;  // 50230 % 256 = 54
   play_note(G4, d_1_4); // Note 1 G4 1/4
   play_note(C5, d_1_4); // Note 2 C5 1/4
   play_note(E5, d_1_8); // Note 3 E5 1/8
   play_note(C5, d_1_8); // Note 4 C5 1/8
   play_note(G4, d_1_4); // Note 5 G4 1/4
   play_note(E5, d_1_4); // Note 6 E5 1/4
   play_note(C5, d_1_4); // Note 7 C5 1/4
   play_note(E5, d_1_8); // Note 8 E5 1/8
   play_note(C5, d_1_8); // Note 9 C5 1/8
   play_note(G4, d_1_4); // Note 10 G4 1/4
   play_note(E5, d_1_4); // Note 11 E5 1/4
   play_note(C5, d_1_4); // Note 12 C5 1/4
   play_note(E5, d_1_8); // Note 13 E5 1/8
   play_note(C5, d_1_8); // Note 14 C5 1/8
   play_note(G4, d_1_4); // Note 15 G4 1/4
   play_note(C5, d_1_4); // Note 16 C5 1/4
   play_join(E5, d_1_4); // Note 17 E5 1/2
   play_note(E5, d_1_4);
   play_note(C5, d_1_4); // Note 18 C5 1/4
   play_note(G4, d_1_4); // Note 19 G4 1/4
   play_note(C5, d_1_4); // Note 20 C5 1/4
   play_note(E5, d_1_8); // Note 21 E5 1/8
   play_note(C5, d_1_8); // Note 22 C5 1/8
   play_note(G4, d_1_4); // Note 23 G4 1/4
   play_note(E5, d_1_4); // Note 24 E5 1/4
   play_note(C5, d_1_4); // Note 25 C5 1/4
   play_note(E5, d_1_8); // Note 26 E5 1/8
   play_note(C5, d_1_8); // Note 27 C5 1/8
   play_note(G4, d_1_4); // Note 28 G4 1/4
   play_note(E5, d_1_4); // Note 29 E5 1/4
   play_note(C5, d_1_4); // Note 30 C5 1/4
   play_note(E5, d_1_8); // Note 31 E5 1/8
   play_note(C5, d_1_8); // Note 32 C5 1/8
   play_note(G4, d_1_4); // Note 33 G4 1/4
   play_note(G4, d_1_4); // Note 34 G4 1/4
   play_join(C5, d_1_4); // Note 35 C5 3/4
   play_join(C5, d_1_4);
   play_note(C5, d_1_4);
   play_note(E5, d_1_4); // Note 36 E5 1/4
   play_note(E5, d_1_4); // Note 37 E5 1/4
   play_note(E5, d_1_4); // Note 38 E5 1/4
   play_note(E5, d_1_4); // Note 39 E5 1/4
   play_note(E5, d_1_4); // Note 40 E5 1/4
   play_join(G5, d_1_4); // Note 41 G5 1/2
   play_note(G5, d_1_4);
   play_note(E5, d_1_4); // Note 42 E5 1/4
   play_note(C5, d_1_4); // Note 43 C5 1/4
   play_note(E5, d_1_4); // Note 44 E5 1/4
   play_note(C5, d_1_4); // Note 45 C5 1/4
   play_note(E5, d_1_4); // Note 46 E5 1/4
   play_note(C5, d_1_4); // Note 47 C5 1/4
   play_join(E5, d_1_4); // Note 48 E5 1/2
   play_note(E5, d_1_4);
   play_note(C5, d_1_4); // Note 49 C5 1/4
   play_note(E5, d_1_4); // Note 50 E5 1/4
   play_note(E5, d_1_4); // Note 51 E5 1/4
   play_note(E5, d_1_4); // Note 52 E5 1/4
   play_note(E5, d_1_4); // Note 53 E5 1/4
   play_note(E5, d_1_4); // Note 54 E5 1/4
   play_join(G5, d_1_4); // Note 55 G5 1/2
   play_note(G5, d_1_4);
   play_note(E5, d_1_4); // Note 56 E5 1/4
   play_note(C5, d_1_4); // Note 57 C5 1/4
   play_note(E5, d_1_4); // Note 58 E5 1/4
   play_note(C5, d_1_4); // Note 59 C5 1/4
   play_note(G4, d_1_4); // Note 60 G4 1/4
   play_note(G4, d_1_4); // Note 61 G4 1/4
   play_join(C5, d_1_4); // Note 62 C5 3/4
   play_join(C5, d_1_4);
   play_join(C5, d_1_4);
   play_note(G4, d_1_4); // Note 1 G4 1/4
   play_note(C5, d_1_4); // Note 2 C5 1/4
   play_note(E5, d_1_8); // Note 3 E5 1/8
   play_note(C5, d_1_8); // Note 4 C5 1/8
   play_note(G4, d_1_4); // Note 5 G4 1/4
   play_note(E5, d_1_4); // Note 6 E5 1/4
   play_note(C5, d_1_4); // Note 7 C5 1/4
   play_note(E5, d_1_8); // Note 8 E5 1/8
   play_note(C5, d_1_8); // Note 9 C5 1/8
   play_note(G4, d_1_4); // Note 10 G4 1/4
   play_note(E5, d_1_4); // Note 11 E5 1/4
   play_note(C5, d_1_4); // Note 12 C5 1/4
   play_note(E5, d_1_8); // Note 13 E5 1/8
   play_note(C5, d_1_8); // Note 14 C5 1/8
   play_note(G4, d_1_4); // Note 15 G4 1/4
   play_note(C5, d_1_4); // Note 16 C5 1/4
   play_join(E5, d_1_4); // Note 17 E5 1/2
   play_note(E5, d_1_4);
   play_note(C5, d_1_4); // Note 18 C5 1/4
   play_note(G4, d_1_4); // Note 19 G4 1/4
   play_note(C5, d_1_4); // Note 20 C5 1/4
   play_note(E5, d_1_8); // Note 21 E5 1/8
   play_note(C5, d_1_8); // Note 22 C5 1/8
   play_note(G4, d_1_4); // Note 23 G4 1/4
   play_note(E5, d_1_4); // Note 24 E5 1/4
   play_note(C5, d_1_4); // Note 25 C5 1/4
   play_note(E5, d_1_8); // Note 26 E5 1/8
   play_note(C5, d_1_8); // Note 27 C5 1/8
   play_note(G4, d_1_4); // Note 28 G4 1/4
   play_note(E5, d_1_4); // Note 29 E5 1/4
   play_note(C5, d_1_4); // Note 30 C5 1/4
   play_note(E5, d_1_8); // Note 31 E5 1/8
   play_note(C5, d_1_8); // Note 32 C5 1/8
   play_note(G4, d_1_4); // Note 33 G4 1/4
   play_note(G4, d_1_4); // Note 34 G4 1/4
   play_join(C5, d_1_4); // Note 35 C5 3/4
   play_join(C5, d_1_4);
   play_join(C5, d_1_4);
   play_note(C5, d_1_4);
}