PWM – Pulse Width Modulation Tutorial | CCP Module

Previous Tutorial Previous Tutorial Tutorial 15 Next Tutorial Next Tutorial
PWM Tutorial | CCP Modules
Intermediate Level ★★☆☆☆

 

In this tutorial, you’ll learn what is Pulse Width Modulation? What are the applications of PWM? And How to generate PWM signals with PIC Microcontrollers. We’ll discuss the last mode of operation for the CCP Modules which is PWM and develop the necessary firmware to drive the CCP Module in PWM mode. Finally, we’ll hook an LED to our PIC Microcontroller chip and create an LED Dimmer application. So let’s get started!


   Required Components   

Qty. Component Name Buy On Amazon.com
1 PIC16F877A Add
1 BreadBoard Add
1 LED Add    Add
1 Resistors Kit Add    Add
1 Capacitors Kit Add    Add
1 Jumper Wires Pack Add    Add
1 LM7805 Voltage Regulator (5v) Add
1 Crystal Oscillator Add
1 PICkit2 or 3 Programmer Add
2 9v Battery or DC Power Supply Add    Add    Add

The Prototyping Board Setup

Prototyping Board - Embedded Systems Tutorials With PIC MCUs


   What Is A PWM Signal?   

 

PWM stands for Pulse Width Modulation. This feature provides microcontrollers by a mean of, seemingly, outputting analog values of voltage between (0-5)v. Instead of outputting digital values that are either Low (0v) or High (5v). The PWM signal plot looks something like as shown in the diagram below.

PWM-signal-with-variable-duty-cycle
image source https://www.elprocus.com

The plot for this signal captures two features: (Frequency & Duty Cycle). And we’ll discuss each of them in more detail hereafter.

 PWM Frequency 

As you know from elementary physics that a frequency of a signal is a measure of how many cycles are completed each second. It’s measured in Hz (Hertz), and we can also refer to it as the inverse of the time period of each cycle (F = 1 / T). Which means by varying the time period of each cycle, we’re controlling the frequency of the output signal. In fact, that’s what we’ll be doing hereafter in this tutorial’s LAB.

 PWM Duty Cycle 

The duty cycle is a measure for how long does the output line stays ON (High) to the cycle’s full-time period as a ratio. Which means the duty cycle can be easily determined using the following formula.

PWM Duty Cycle

PWM Signal’s Effective Voltage. The controllability of the PWM duty cycle enables microcontroller devices to extend the hard-limited ON/OFF (High/Low) digital output pattern. Thus our microcontroller can now, seemingly not exactly, output analog values between (0-5v). 

You can think of it as changing the duty cycle of a PWM signal corresponds to changing the average (effective) voltage of the output line. For example, a PWM signal with 50% DC is seemingly considered a 2.5v voltage level on the output line. Hence, 80% DC is 4v, and 100% DC is 5v, and so on.

pwm fade
image source http://www.pyroelectro.com

This analogy while not being 100% accurate in a theoretical sense. But practically, it’s the way we add sweet & smooth control over light instead of On-OFF behavior with the full lighting power which can be annoying for specific applications. And we can also add a soft-start soft-end motor speed control instead of running an RC robot with 100% speed and breaking to 0% instantaneously which will result in undesirable drifts.

 


   What Are The Applications Of PWM?   

 

There are numerous situations in which it’s ideal to have a PWM output signal. That’s why there are countless applications which are mainly dependent on the pulse width modulation technique. I’m going to introduce some of them down below:

  • Light intensity control.
  • Motor speed control.
  • ِAudio signal generation.
  • Servo control (valves, motors, etc).
  • Voltage regulation. By switching voltage to the load with the appropriate duty cycle, the output will approximate a voltage at the desired level. The switching noise is usually filtered with an LC network.
  • The solar tracking and charging systems.

And much more, you can google “PWM Applications” to find out hundreds of applications related to PWM.

 


   CCP Modules – PWM Mode   

 

In PWM Mode, the CCPx pin (CCP1=RC2, CCP2=RC1) produces up-to a 10-Bit resolution PWM output signal. Since the CCP1 pin is multiplexed with the PORTC data latch, the RC2 pin must be configured as an output pin by clearing the TRISC2 bit. And the same goes for the CCP2 pin (RC1).

 PWM Mode Diagram 

Here is the logic (Block) diagram for the PWM mode as found in the datasheet (8.3 page-67)

Pulse Width Modulation - PWM Block Diagram

Or equivalently this hand-drawn version for the same diagram

Pulse Width Modulation - PWM Block Diagram

 CCP Operation In PWM Mode 

As you might have noticed in the above diagram, the operation of the ccp module in PWM mode goes as follows. First, the CCP module is set to operate in PWM mode by writing to the CCP1CON register. Then, the required PWM frequency will tell us the PWM time period which we’ll be loading to the PR2 register. Timer2 should be turned ON with the pre-specified prescaler value. And the duty cycle is set by writing to the [ CCPR1L register & CCP1CON<5:4> Bits ].

From the PWM Block Diagram, the PR2 value is constantly compared against the TMR2 register until a match occurs which indicates the end of the PWM cycle and the starting of a new cycle. Hence, the value of PR2 determines the time period of each PWM cycle and it consequently provides a mean of controlling the PWM output Frequency!

Pulse Width Modulation - PWM Block Diagram

The CCP1 pin (RC2) is set (High = 1) at the beginning of each cycle. And the DC (duty cycle) 10-Bit value is latched to the [ CCPR1H + 2-Bit internal latches ]. This DC value is constantly compared against the [ TMR2 + 2-Bit Q Clock ]. When a match occurs, the output latch for the CCP1 pin (RC2) is reset (Low = 0). And it stays low till the end of the current PWM cycle. At the beginning of a new cycle, the output latch is set again to High and so on. Hence, the DC value controls for how long the PWM output pin will be set (High). Hence, the DC 10-Bit value provides a mean for controlling the PWM output Duty Cycle!

PWM Output Signal Diagram

 Notes For PWM Mode 

I- Clearing the CCP1CON register:

Clearing the CCP1CON register will force the CCP1 PWM output latch to the default low level. This is not the PORTC I/O data latch.

II- Timer2 postscaler:

The Timer2 postscaler is not used in the determination of the PWM frequency. The postscaler could be used to have a servo update rate at a different frequency than the PWM output.

III- Duty Cycle value:

If the PWM duty cycle value is longer than the PWM period, the CCP1 pin will not be cleared.

IV- Double-Buffering & Glitches:

CCPR1L and CCP1CON<5:4> can be written to at any time, but the duty cycle value is not latched into CCPR1H until after a match between PR2 and TMR2 occurs (i.e., the period is complete). In PWM mode, CCPR1H is a read-only register. The CCPR1H register and a 2-bit internal latch are used to double-buffer the PWM duty cycle. This double-buffering is essential for glitch-free PWM operation.

V- PWM Resolution:

The PWM duty cycle is specified by writing to the CCPR1L register and to the CCP1CON<5:4> bits. Up to 10-bit resolution is available. The maximum PWM resolution (bits) for a given PWM frequency is given by the following formula.
Pulse Width Modulation - PWM Resolution Eqn

PCBgogo Ad

The typical resolution (in bits) for a specific PWM frequency, with a specific Prescaler ratio PS, for a system running at Fosc clock rate is given by the equation down below

Pulse Width Modulation - PWM Resolution Eqation

 


   Configuring CCP Modules For PWM Mode   

 

Here is the step-by-step procedure that you should follow in order to operate the CCP module in the PWM mode to generate the PWM output signal with your desired duty cycle and frequency.

 Configuring The CCP1 Module For PWM Operation 

Step1 –  Configure the CCP module for PWM operation

  • As mentioned in the datasheet in the CCP1CON register’s description. Writing 11xx to the CCP1M0:CCP1M3 4-Bits selects the PWM mode of operation. Note that xx means that Bit0,1 are don’t cares! that’s why we’ll neglect them while writing the code.

Step2 –  Configure the CCP1 pin (RC2) to be an output pin by clearing the corresponding TRISC2 bit

  • The CCP1/RC2 pin should be configured as an output pin.

Step3 –  Determine the PWM frequency and get the PWM period value

  • Let’s say we need to have a PWM signal with a frequency of 5kHz. This means that the time period should be = 1/F = 1/5000 = 200µs

Step4 –  Using The PWM period, Calculate the value which we’ll load to the PR2 Register

  • This step involves choosing a value for Timer2 prescaler prior to determining the PR2 register’s value. For this step, we’ll be using the following equation

Pulse Width Modulation - PWM Time Period

  • We now have the PWMperiod, the Tosc = 1/Fosc, and we’ll pick an initial value for Timer2Prescaler (say 1 for 1:1 ratio). It should be easy to solve for PR2 to get its value. The rule of thumb is that PR2 register is an 8-Bit register which means its value ranges from (0 to 255). Your result should never exceed this 255 cap. Otherwise, you should try another value for the Timer2Prescaler. Finally, after getting the PR2 value, we should move this number to the PR2 register.

Step5 –  Set the prescaler of Timer2

  • Adjust the Timer2 prescaler value to match the value which you’ve already used in the previous step. This is done by writing to the T2CON register T2CKPSx bits.

Step6 –  Step The PWM Duty Cycle, by writing to the CCPR1L register & CCP1CON<5:4> Bits

  • To set or change the Duty Cycle of the PWM output signal, you should calculate and write the DC value to the 10-Bit buffer register which consists of CCPR1L:CCP1CON<5:4>. The CCPR1L contains the eight MSBs and the CCP1CON<5:4> contains the two LSbs. This 10-bit value is represented by CCPR1L:CCP1CON<5:4>.
  • The following equation is used to calculate the PWM duty cycle in time:

Pulse Width Modulation - PWM Duty Cycle

  • Let’s say you’re willing to get 40% DC for your PWM output. Then you should multiply 0.40 by PWMperiod To get the PWMDutyCycle in time. Now, substitute for PWMDutyCycle , Tosc , TMR2prescaler in the previous equation and solve for the 10-Bit value between the ( )
  • After getting the 10-Bit duty cycle from your calculations, just write this value to the buffer CCPR1L:CCP1CON<5:4>

Step7 –  Turn ON Timer2

  • Don’t forget to turn ON the Timer2 module! it should be obvious that it’s the working-horse of our system. However, it happens to find out that you’ve missed doing so and that’s why nothing is working at all.

   Implementing PWM Firmware Driver   

 

 Implementing CCP1 PWM Output Driver 

Step1 – Configure the CCP module for PWM operation

//--[ Configure The CCP1 Module For PWM Mode ]--
CCP1M2 = 1;
CCP1M3 = 1;

Step2 – Configure the CCP1 pin (RC2) to be an output pin
TRISC2 = 0;  // RC2/CCP1(PWM) Output Pin

Step3 – Determine the PWM frequency

Let’s say our objective is to generate a 2kHz PWM signal, and that is our desired PWMFrequency. And let’s also assume our microcontroller is running at 4MHz. Well, it’s time to choose the TMR2prescaler value to be capable of using the following equation in order to get the PR2 register’s value. let’s pick 1:1 PS and substitute in the frequency equation

PWM Time Period

After substitution, our equation will be as follows 

Pulse Width Modulation - PWM freqency step

Solving for PR2 will yield the following result. (PR2 = 499), which exceeds the 8-Bit range (0-255). Then it’s not OK to use this prescaler value and we’ll pick another value!

Let’s check a prescaler of 1:4 and solve for PR2 once again.

Pulse Width Modulation - PWM Freq Step3

Solving for PR2 will now yield the following result. (PR2 = 124). which falls in the 8-Bit range (0-255). So, it’s OK to use this prescaler value and let’s proceed to the next step.

Step4 – Write to the PR2 Register

PR2 = 124;

Step5 – Set the prescaler of Timer2
// Set Timer2 Prescaler To Match The (1:4) Ratio
T2CKPS0 = 1;
T2CKPS1 = 0;

Step6 – Set The PWM Duty Cycle

This step involves both calculating and writing the 10-Bit DC value as discussed earlier. Let’s say you’re willing to get a 70% Duty Cycle for your PWM output. Then the PWMDutyCycle Time is calculated by multiplying the PWMperiod by 0.70 as shown down below

PWM DC step

Now, we’re able to use the duty cycle equation which we’ve seen earlier. And solve for the 10-Bit value between the ( )

PWM Duty Cycle

This will result in (CCPR1L:CCP1CON<5:4>) = 350

This actually means that 70% DC maps to 350

Therefore, 100% DC maps to (100*350)/70 = 500, and 50% DC maps to 250, and so on!

Now, we’ve got to write the 350 value to the 10-Bit buffer register. This is done as shown below

CCP1CONbits.CCP1Y = (350) & 1; // Get bit-0 (The LSB)
CCP1CONbits.CCP1X = (350) & 2; // Get bit-1
CCPR1L = (350) >> 2;           // Move The 8 MSBs To CCPR1L register

Step7 – Turn ON Timer2
TMR2ON = 1;

 


   LED Dimmer – LAB   

 

Lab Name PWM – LED Dimmer
Lab Number 13
Lab Level Beginner
Lab Objectives Learn how to use CCP modules to generate PWM signals with a desired frequency of 2kHz and variable duty cycle. We’ll use the output PWM signal to control the brightness of a small LED in this LAB. The duty cycle should be gradually increasing from 0 up-to 100% then it should start to gradually drop back to 0% and so on.

 

       1. Coding       

 

Open the MPLAB IDE and create a new project name it “PWM_LED_Dimmer”. If you have some issues doing so, you can always refer to the previous tutorial using the link below.

Create New Project With MPLAB IDE

Set the configuration bits to match the generic setting which we’ve stated earlier. And if you also find troubles creating this file, you can always refer to the previous tutorial using the link below.

Now, open the main.c file and let’s start developing the firmware for our project.

Our first task is to configure the CCP module to operate in PWM mode. And also configure the PWM output frequency to be 2kHz. Then in the main routine loop, we’ll be gradually increasing the Duty Cycle of the PWM output from 0 to 100% and gradually drop it back to 0% once again and so on.

It’s been always a better practice to create a dedicated function for each task of these. However, for this lab, I’ll be only creating a function for manipulating the DC value in order to fit it in the PWM DC buffer register. The rest of the code will reside normally in the main routine.

The full code listing for this lab is as follows

/*
* LAB Number: 13
* LAB Name: PWM - LED Dimmer
* Author: Khaled Magdy
* For More Information Visit My Website @ DeepBlueMbedded.com
*
*/
#include <xc.h>
#include <stdint.h>
#include "config.h"
#define _XTAL_FREQ 4000000
//================================
void PWM1_Set_Duty(uint16_t);

void main(void) 
{
  //--[ Configure The CCP Module For PWM Mode ]--
  CCP1M3 = 1;
  CCP1M2 = 1;
  TRISC2 = 0; // The CCP1 Output Pin (PWM)
  // Set The PWM Frequency
  PR2 = 124;
  // Set The PS For Timer2 (1:4 Ratio)
  T2CKPS0 = 1;
  T2CKPS1 = 0;
  // Start CCP1 PWM !
  TMR2ON = 1;

  uint16_t DC=0; 
  // Write The System's Main Routine
  while(1)
  {
    DC = 0; // Start With 0% DutyCycle

    // Gradually increase LED brightness
    while (DC<500)
    {
      PWM1_Set_Duty(DC);
      DC++;
      __delay_ms(2);
    }
    __delay_ms(100);

    // Gradually decrease LED brightness
    while(DC>0)
    {
      PWM1_Set_Duty(DC);
      DC--;
      __delay_ms(2);
    }

    __delay_ms(100);
  }
  return;
}

void PWM1_Set_Duty(uint16_t DC)
{
  // Check The DC Value To Make Sure it's Within 10-Bit Range
  if(DC<1024)
  {
    CCP1Y = DC & 1;
    CCP1X = DC & 2;
    CCPR1L = DC >> 2;
  }
}

 

       2. Simulation       

 

To simulate the project, just hook an LED to the RC2/CCP1 pin with a 300Ω resistor. Add the hex file and run the simulator. Here is the schematic for this lab

Pulse Width Modulation - PWM LAB Schematic

It’s highly recommended to hook an oscilloscope to the PWM output line in order to have a clear vision of what is exactly going on. Due to many factors, LED dimming may not be running in real-time on simulators. And here is the typical simulation results!

Pulse Width Modulation - PWM Dimmer Simulation

 

       3. Prototyping       

 

Prototyping this project should also be an easy task. The tough part has been already done before writing the code. And here is the output in case you’re curious.

Play Video On YouTube

 


   Tips + Concluding Remarks   

 

  1  

PWM Calculations

From my own experience, I’ve seen many embedded practitioners struggling with the calculations part of operating the PWM module. That’s why I’m going to summarize it all up once again for you.

Where are actually calculating a couple of values:

  • The period to be loaded in the PR2 register
  • The duty cycle 10-Bit value to be loaded in the [ CCPR1L register & CCP1CON<5:4> Bits ]

If you’re still remembering the first section of this tutorial when I’ve told you that the PWM signal plot captures 2 features: (The PWM frequency & The PWM Duty Cycle). And we (the programmers) specify each of those features to meet the design requirements.

Obviously, setting the output PWM frequency is done by writing to the PR2 register. And the output PWM Duty Cycle is set by writing to the [ CCPR1L register & CCP1CON<5:4> Bits ].

That’s why we’ve to calculate these two values. We’ve to do so in order to control both the Frequency and Duty Cycle of the output PWM signal.

1- Frequency Control

To control the frequency of the PWM output, we write to the PR2 register and its value is given by the following equation

PWM Time Period

The only missing parameter other than PR2 is the TMR2PS which you should try guessing & testing. This should be done in such a way that gives you an 8-Bit value for the PR2 register. If the result has exceeded the (0-255) range for 8-Bit values, you should try another value for the prescaler until this condition is met.

2- Duty Cycle Control

To control the DutyCycle of the PWM output, we write to the DC buffer 10-Bit register which consists of (CCPR1L:CCP1CON<5:4>). Calculating the value which we should write to this register in order to get a specific DC percentage % is done using the formula down below.

PWM Duty Cycle

  2  

PWM Timer2 Utilization

The CCPx modules (CCP1 & CCP2) in PWM Mode both are utilizing the Timer2 module as a hardware resource for controlling the timing features of the PWM output signal. This fact necessarily means that both CCP1 & CCP2 will be restricted to generate PWM signals with the exact same frequency. And also you can not use the Timer2 module for any other function within your system. However, you can disable the respective CCPx module for a specific time period during which you can use the Timer2 as a general purpose 8-Bit Timer. Then you can switch back to enable the PWM output, only if it’s feasible for your system, and it should be carefully implemented.

  3  

PWM Resolution

You can always double-check the output typical resolution for your PWM signal using the formula shown down below as I’ve stated earlier in this tutorial.

PWM Resolution Eqation

But for now, let’s use this knowledge in a practical sense. In the previous Lab, our system was being clocked at a 4Mhz rate, with a 1:4 PS ratio, and the desired output PWM frequency was 2kHz. Well, the previous equation is now telling us the following results

PWM Resolution Example

Well, this results means that we typically need 9-Bits to represent the Duty Cycle for the PWM output ranging from (0 to 512). What I’ve done here is a ceiling process. Which is approximating the result to the upper next integer.

PWM Resolution Ceiling

However, it’s not an accurate estimation for the maximum duty cycle range. Yes, we need only 9-Bits in resolution in order to get a 0-to-100% DutyCycle variation capability. But it’s not exactly what we should be doing.

Interestingly, raising 2 to the power of this exact resolution 8.965 yields the following result

28.965 = 500

Which is exactly the maximum value for the duty cycle! Which will give you a 100% DC if you’ve loaded it to the PWM buffer register (CCPR1L:CCP1CON<5:4>).

 

 

Previous Tutorial Previous Tutorial Tutorial 15 Next Tutorial Next Tutorial

 

Share This Page With Your Network!

Khaled Magdy

I'm an embedded systems engineer doing both Software & Hardware. I'm an EE guy who studied Computer Engineering, But I'm also passionate about Computer Science. I love reading, writing, creating projects and Technical training. A reader by day a writer by night, it's my lifestyle. You can view my profile or follow me via contacts.

You may also like...

12 Responses

  1. Shiji Sebastian says:

    How to create a push pull pwm for transformer driving???

  2. meh endis says:

    Thanks for this very informing tutorials about PIC mictocontrollers.There is very much practical knowledge and experience about PIC

  3. Juan D says:

    Hi Khaled I’m Juan an electronics engineer from Colombia, I want to thank you a lot for these tutorials, this is exactly what I needed to be able to understand how to program, I like the professional recommendations that you explain from your experience, I´ve always had problems to learn how to program microcontrollers but these tutorials have helped me a lot.

    on the other hand, I’m a bit confused with the following function

    void PWM1_Set_Duty(uint16_t DC)
    {
    // Check The DC Value To Make Sure it’s Within 10-Bit Range
    if(DC> 2;
    }
    }

    could you explain me please why you put those & (and) with the “DC” variable, DC&1, DC&2 and why DC>>2?

    thanks !!

    • Juan D says:

      sorry i wrote an incomplete function this is the function complete:

      void PWM1_Set_Duty(uint16_t DC)
      {
      // Check The DC Value To Make Sure it’s Within 10-Bit Range
      if(DC> 2;
      }
      }

      • Khaled Magdy says:

        Greetings Juan, i hope you’re doing well my friend ^^
        First of all, the issue is the DC value is a 10-Bit value that should be written in the Duty Cycle register of the CCP module in PWM mode. The CCPR1L register is an 8-Bit so you’ll need another 2 bits to store the Least significant bits of your DC. And those 2 bits should go to CCP1CON<5:4>. Namely, CCP1Y and CCP1X
        Well, how to do it?
        it’s pretty simple!
        logically AND the DC value with a one 1
        like this (DC&1)
        or equivalently (DC & 0b00000001) “The same effect”
        this will give you the value of the least significant bit of the DC, that should go into CCP1Y. And that’s exactly what the first line does!
        CCP1Y = DC & 1;
        the second line, ANDs the DC with 2
        like this (DC & 2)
        that’s equivalent to (DC & 0b00000010)
        which gives you the value of the 2nd least significant bit, that should go into CCP1X bit. And that’s exactly what the first line does!
        CCP1X = DC & 2;

        Now, we’ve registered the 2 least significant bits of the DutyCycle in CCP1Y and CCP1X. What about the high 8-Bit?
        well, it’s an easier task
        we simply right shift the 10-Bit DC value twice and it’ll become an 8-Bit value. So, you can move it directly to the CCPR1L (8bit register). Like this
        CCPR1L = DC>>2;

        I hope this helps and clears everything for you ^^

        Regards,
        Khaled M.

  4. Jannie says:

    Khaled
    Thanks for above.
    Is there a way that the CCP modules can drive 2 independent PWM signals continuously ?
    Or how can I create 2 PWM signals from similar PIC16F15356 ?

    Thanks
    Jannie

    • Khaled Magdy says:

      Hi Jannie,
      According to microchip’s datasheet for this part. It does have 2 independent ccp/pwm generators. Alongside with a CWG (complementary waveform generator) which can also help you drive multiple pwm signals in case you need complementary pwm for motor control or power electronics applications like inverters and so.
      So, all in all, the straight forward way is to use ccp1 and ccp2 to get 2 PWMs.

  5. Jannie says:

    Khaled
    Thanks, Both CCP modules is link to Timer2 and PR2 : I am happy running both signals at the same frequency.
    however the duty cycle needs to be software controlled independently from analogue 2 different feedback signals.

    ( currently I have both PWM’s running at the same DC but working both fine when operating separately )
    So I need to load CCPRxL and CCPRxH and then make the link to the PWMx_LoadDutyValue(dutyValue)
    Not sure which registers to address to make the link … would that be the correct approach?
    A logic way would be to link each CCP1 to timer 1 and CCP2 to timer 2 , but the PIC does not look to be set up that way from the MCC.

    • Khaled Magdy says:

      You’re totally right about that. It may not be the best way to link two ccp to the same timer. But it turned out to be a much better optimization decesion from microchip XD!
      Hence you’re totally fine with running at the same time base “frequency” for both of the 2 PWMs, so you can setup CCP1 AND CCP2 to work in pwm mode and setup timer2 to give you the required frequency. And you’re good to go!
      The duty cycle control is done by writing to the CCPR1 register for PWM1
      And CCPR2 register for PWM2
      That’s all. The duty cycle is independently controlled for both ccp modules.

  6. Jannie says:

    Khaled
    Ok I see my problem with this PIC.
    The CCP modules are still linked to PWM1 and PWM2 however the IC only address/and uses PWM 3,4,5 and 6
    No mention of the PWM1 and PWM2 in the datasheet.!!
    When running the MCC it does generate the PWM1 and PWM2 configuration files(which I disabled) containing configuration bits for CCPx
    Now I am running 2 independent PWM’s
    Thanks

Leave a Reply

%d bloggers like this: