This post presents two example programs for the PIC16F1829 microcontroller that generate 50Hz square waves. The first example uses simple bit banging to generate the waveform. The second example uses the dedicated PWM hardware module.
Example 1: bit banging
In this example program, I set a single pin as a digital output (RC5), then generate a 50Hz square wave using straightforward bit banging.
The clock oscillator is left at its default frequency of Fosc = 500kHz. Therefore, the oscillator period Tosc = 2us. Since the PIC16F1829 performs one machine instruction every four oscillator cycles, the instruction cycle, Tcy = 8us.
// // PIC16F1829 50Hz 50% duty cycle example program // Written by Ted Burke (http://batchloaf.com) // Last updated 5-6-2013 // // To compile with XC8: // xc8 --chip=16F1829 main.c // #include <xc.h> // Select the internal oscillator (default Fosc = 500kHz, which gives // an instruction cycle Tcy = 8us), disable low voltage programming, // disable reset pin, disable watchdog timer #pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF int main(void) { // Make RC5 a digital output TRISCbits.TRISC5 = 0; // Now just switch RC5 high and low repeatedly while(1) { LATCbits.LATC5 = 1; // Set RC5 high _delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us LATCbits.LATC5 = 0; // Set RC5 low _delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us } return 0; }
I don’t have a PIC16F1829 to try this out on, so the best I could do was test it using MPLAB SIM (the simulator in MPLAB v8.50). Here’s a screenshot I captured of the Simulator Logic Analyzer tool (under the View menu in MPLAB) displaying the generated waveform.
The x-axis units in the figure above are instruction cycles, so the period of the generated signal is approximately 2500 instruction cycles.
Careful examination of the waveform in the figure above reveals that the period is actually not precisely 2500 instruction cycles. This may be due to the overhead associated with the additional instructions caused by the while loop and bit-banging. If precision is required, the delay values could be carefully tweaked to compensate. However, it is worth bearing in mind that the frequency of the internal RC oscillator can vary, so if precision is required an external crystal oscillator might be a better choice.
Example 2: using the PWM module
Here’s another example of a 50Hz square wave. This time, the PWM module is used:
// // PIC16F1829 50Hz 50% duty cycle example program // Written by Ted Burke (http://batchloaf.com) // Last updated 7-6-2013 // // To compile with XC8: // xc8 --chip=16F1829 main.c // #include <xc.h> // Select the internal oscillator (default Fosc = 500kHz, which gives // an instruction cycle Tcy = 8us), disable low voltage programming, // disable reset pin, disable watchdog timer #pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF int main(void) { // Set up PWM T2CON = 0b00000111; // Enable TMR2 with prescaler = 64 PR2 = 155; // PWM period = (PR2+1) * 64 * Tcy = 19.968ms CCPR1L = 78; // pulse width = CCPR1L * 64 * Tcy = 9.984ms CCP1CON = 0b00001100; // Enable PWM on CCP1 TRISCbits.TRISC5 = 0; // Make CCP1 pin an output // Now just do nothing while PWM module does the work while(1); return 0; }
Configuring the PWM module is a little more complicated than just bit banging to generate the signal. However, it has (at least) two very important benefits:
- The program can get on with other tasks while the square wave is generated continuously in the background by the dedicated PWM hardware module.
- Since there are no extra instructions associated with a while loop or anything else, it’s much easier to determine the exact length of the waveform period and pulse width (measured in instruction cycles).
