Wednesday, September 15, 2010

PIC16F887/877 programming in C Tutorial 8 (PWM)

The PWM mode generates a Pulse-Width Modulated signal on the CCPx pin. The duty cycle, period and resolution are determined by the following registers:
• PR2
In Pulse-Width Modulation (PWM) mode, the CCP module produces up to a 10-bit resolution PWM output on the CCPx pin. Since the CCPx pin is multiplexed with the PORT data latch, the TRIS for that pin must be cleared to enable the CCPx pin output driver.

PWM Period:
The PWM period is specified by the PR2 register of Timer2. The PWM period can be calculated using the formula
                             PWM Period = [(PR2) + 1] • 4 • TOSC • (TMR2 Prescale Value)
                             Note: TOSC = 1/FOSC

When TMR2 is equal to PR2, the following three events occur on the next increment cycle:
• TMR2 is cleared
• The CCPx pin is set. (Exception: If the PWM duty cycle = 0%, the pin will not be set.)
• The PWM duty cycle is latched from CCPRxL into CCPRxH.

PWM Duty Cycle:
The PWM duty cycle is specified by writing a 10-bit value to multiple registers: CCPRxL register and DCxB<1:0> bits of the CCPxCON register. The CCPRxL contains the eight MSbs and the DCxB<1:0> bits of the CCPxCON register contain the two LSbs. CCPRxL and DCxB<1:0> bits of the CCPxCON register can be written to at any time. The duty cycle
value is not latched into CCPRxH until after the period completes (i.e., a match between PR2 and TMR2 registers occurs). While using the PWM, the CCPRxH register is read-only.

         Pulse Width = (CCPRxL:CCPxCON<5:4>) • TOSC • (TMR2 Prescale Value)

                  Duty Cycle Ratio (CCPRxL:CCPxCON<5:4>) / 4(PR2 + 1)

PWM Resolution:
The resolution determines the number of available duty cycles for a given period. For example, a 10-bit resolution will result in 1024 discrete duty cycles, whereas an 8-bit
resolution will result in 256 discrete duty cycles. The maximum PWM resolution is 10 bits when PR2 is 255.
                                     Resolution = log[4(PR2 + 1)] / log(2)  bits

Setup for PWM Operation:
The following steps should be taken when configuring the CCP module for PWM operation:
1. Disable the PWM pin (CCPx) output drivers as an input by setting the associated TRIS bit.
2. Set the PWM period by loading the PR2 register.
3. Configure the CCP module for the PWM mode by loading the CCPxCON register with the appropriate values.
4. Set the PWM duty cycle by loading the CCPRxL register and DCxB<1:0> bits of the CCPxCON register.
5. Configure and start Timer2:
• Clear the TMR2IF interrupt flag bit of the PIR1 register.
• Set the Timer2 prescale value by loading the T2CKPS bits of the T2CON register.
• Enable Timer2 by setting the TMR2ON bit of the T2CON register.
6. Enable PWM output after a new PWM cycle has started:
• Wait until Timer2 overflows (TMR2IF bit of the PIR1 register is set).
• Enable the CCPx pin output driver by clearing the associated TRIS bit.

Note: This is only for 887. For Enhanced PWM Mode please refer to pic16F887 datasheet.

Lets do all this things in easy way by using in-build mikroc library for pwm.
Lets write the code to generate PWM on pins RC1 & RC2. And switches RA0-RA3 to increase/decrease PWM duty cycle.

unsigned short current_duty, current_duty1;

void InitMain() {
//using 887 and 8MHz xtal//////////////
  ANSEL  = 0;                         // Configure AN pins as digital
  ANSELH = 0;
  C1ON_bit = 0;                       // Disable comparators
  C2ON_bit = 0;

  PORTA = 255;
  TRISA = 255;                        // configure PORTA pins as input
  PORTC = 0;                          // set PORTC to 0
  TRISC = 0;                          // designate PORTC pins as output
  PWM1_Init(1000);                    // Initialize PWM1 module at 1KHz
  PWM2_Init(2000);                    // Initialize PWM2 module at 2KHz

void main() {
  current_duty  = 16;                 // initial value for current_duty
  current_duty1 = 16;                 // initial value for current_duty1

  PWM1_Start();                       // start PWM1
  PWM2_Start();                       // start PWM2
  PWM1_Set_Duty(current_duty);        // Set current duty for PWM1
  PWM2_Set_Duty(current_duty1);       // Set current duty for PWM2

  while (1) {                         // endless loop
    if (RA0_bit) {                    // button on RA0 pressed
      current_duty++;                 // increment current_duty

    if (RA1_bit) {                    // button on RA1 pressed
      current_duty--;                 // decrement current_duty

    if (RA2_bit) {                    // button on RA2 pressed
      current_duty1++;                // increment current_duty1

    if (RA3_bit) {                    // button on RA3 pressed
      current_duty1--;                // decrement current_duty1

    Delay_ms(5);                      // slow down change pace a little

PWM1_Init();Initializes the PWM module with duty ratio 0. Parameter freq is a desired PWM frequency in Hz.
PWM1_Set_Duty(); Sets PWM duty ratio. Parameter duty takes values from 0 to 255, where 0 is 0%, 127 is 50%, and 255 is 100% duty ratio. Other specific values for duty ratio can be calculated as (Percent*255)/100.
PWM1_Start(); Starts PWM.



  1. hi, thanx for all the examples.

    Im working on MikroC Pro For PIC v4.6 and I copy/paste your code and build it.
    After that I exactly made the same schematic in ISIS and loaded the *.hex file in the PIC16f887.
    Finally, I try to simulate all, but it doesn't do anything... :S

    Have no idea what could be wrong, btw, this is my first attempt with PWM

    Plz help.

  2. Code is ok. I think problem is in proteus isis.

  3. For MikroC "RA2_bit" must be replaced with PORTA.F2 and "PWM1_Set_Duty" to "PWM1_Change_Duty" and with all of that isn't working in Proteus

  4. Yes; but the above code is written in MikroC Pro for PIC. During the session of this tutorial i tested the code in proteus, at that time it was running fine. I don't know why it is not working now. I think i have to write the new code in MPLAB.

  5. The code works fine now for me.. the problem was PROTEUS, must have a last version.


  7. if you wanna build the code use ctrl+F9. or you should read this first

  8. The codes looks fine to me. So I guess it's the problem with proteus.

  9. Hello i was just wondering if you have written the code for MPlab yet? I am currently working on a similar project however i must have a set frequency of 10KHz, also must be hooked up to a potentiometer. When the pot is at 0V i will get a duty cycle of 10%, and when the pot is set to 5V i will get a duty cycle of 95%. Everything in between having a linear relationship. I would greatly appreciate any help / tips! Thank you so much.

  10. here is the sample code
    change according to ur requirements.

  11. Hey thank you for the code, it was a little help but the problem is i am using the PIC16F887 and that was written using another code & everything is using the pic18.h header file in the code you supplied

  12. yup i know. you have to change it; i.e header file, some SFRs to make it compatible for ur controller.

  13. In MikroC Pro must enable the PWM library, in Proteus ISIS edit PIC Properties, change to 8Mhz the Processor Clock Frequency

    1. is the PWM Library in mikroPro enabled by just calling the function PWM_init() first?

  14. Hi Dear, I need urgent help please . if i want to control the speed by one buttons how i can keep the last state of the button ?

    1. Save the value of the button; i.e; say you have 5 steps for full speed means for each press of button the speed will increment about 20%. save value the of number of times button is pressed, compare it with stored value n update the speed.
      For decreasing; you can add 5 more stages means when the button is pressed for six times speed decreases 20% of the previous speed. But its not the good method.
      The best way is to use a potentiometer connected to adc port, then code the speed level according to the adc values. in this you can easily increase or decrease speed with precision.

  15. Salam Bhai i Want to ask that we are generating PWM PULSES Through Micro Controller but can we attach externel circuitary with it what will b effect and it is necessary for extarnel circutary ..... Kindly reply fasst as soon as posssible

  16. What do you mean by "external circuitry"?
    of course for driving motors we need inverter or h-bridge circuit; by controlling the pwms we can change the speed n direction(3-phase or dc motors) as well.

  17. you are connected 17th and 16 th pin for PWM1 and PWM2 it assigned some where in code or is thatthe pin for PWM timers

  18. ccpx pins are used to generate pwms, read datasheet for more info.

  19. All PWM modules use Timer2 for its operation, so you can not set different frequencies for different PWM modules....

    Doesn't work:
    PWM1_Init(1000); // Initialize PWM1 module at 1KHz
    PWM2_Init(2000); // Initialize PWM2 module at 2KHz

    PWM1_Init(1000); // Initialize PWM1 module at 1KHz
    PWM2_Init(1000); // Initialize PWM2 module at 1KHz

  20. Is there anyone can help me soon as possible by explaining how the duty cycle of PWM pulses affects the speed of the motor when used as gate signal to inverters or h-bridge circuits?

  21. in 1 wave cycle you ca convert it avrage dc voltage, longer high pulces and shorter low pulce in 1 wave cycle gives higher dc voltage hence moter will run faster as avrage dc voltage is higher and if high pulse is shorter then low pulse in 1 cycle then revimoter will run slower as avrage dc voltage will be lower
    i hope this answers good

    1. Thank you, can you also help me the code to read an analog voltage and store it on a defined data type?