Microchip PIC16 / PIC18 PWM Mode

Pic Pwm Banner
This article explains how to set up and use the PWM module on almost all PIC 16 and PIC 18 devices.

Depending on the specifications, some PICs may not even have a PWM module at all and some may have 1 or 2 pins that are PWM capable.

For this example, I’m using a PIC16F876A which has two PWM modules on PORTC 1 and 2.


To identify which pins are PWM capable, look for “CCP1” or “CCP2”. CCP stands for Capture, Compare and PWM. This means that the pin is also capable of capture and compare but this won’t be discussed here.

Looking at the PWM section in the datasheet, they explain step by step on how to set up the PWM module and the recommended settings. That is why I love Microchip PICs, their datasheet is very complete.


There are only 5 steps to set up the PWM module as given by the datasheet.

Since there is already an example of the settings given, I just followed them. The example is based on 20MHz, but I’m running the PIC at only 10MHz. So I expect the PWM Frequency to be half of the stated values.

I chose the first setting which was 1.22kHz. (I will only be getting 0.61kHz because the PIC is running at 10MHz).

1. Set the PWM period in PR2 to 0xFF as given by the table.

2. Since the settings I chose give a maximum resolution of 10 bits, a single register won’t be able to hold it because all registers on the PIC 16 and 18 family are only 8 bit wide. So the upper 8 bits are stored in CCPR1L register and the remaining lower 2 bits are stored in bit 5 and 4 of CCP1CON.

3. This step is self explanatory. Setting the TRISC bits to 0 for output.

4. Since we intend to use the full length of TMR2, there is no need to set a prescale value. The only thing left to do is to turn on Timer 2 by setting the TMR2ON bit.

5. Configure the CCP1 module for PWM operation by setting bit 3 to 0 of CCP1CON register to 1100. (See CCP1CON register in datasheet)

Below is the PWM code written for the XC8 compiler running on MPLAB X.


To test the output, I’ve used a logic analyzer to capture the PWM signal.

Channel 1 is reading a PWM output set to 100 duty cycle while channel 2 is reading a 500 duty cycle.

The frequency is 610 Hz which is spot on just like stated in the datasheet.


The measurement is now based on channel 0. The recorded duty cycle is 9.76% which is correct when you divide 100 with 1024.


Recorded duty cycle is 48.8% which is exactly 500/1024*100.

  1. SimonG08-17-2016

    Are you sure about last line with value 0x30?

  2. Igor Monteiro Bezerra Ulisses06-11-2015

    Hi WaiHung, I’ve been trying to implement this code but it was not possible, have you identified any problem on it?

    • bina12-07-2015

      Hi Igor im not the author but i did just implement his code on a pic16f628a,
      hwat is the error your getting?

  3. vick09-09-2013

    I think it is a logic probe ? Is that correct Wai Hung ?


  4. zx lee09-06-2013

    Hi WaiHung,

    what is the hardware u used for logic analyzer?

Leave a Reply