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);
}