PIC18F4550 PWM

Introduction

Pulse Width Modulation (PWM) is a technique by which the width of a pulse is varied while keeping the frequency of the wave constant.

PWM Generation
PWM Generation

 

A period of a pulse consists of an ON cycle (5V) and an OFF cycle (0V). The fraction for which the signal is ON over a period is known as a duty cycle.

 

Duty Cycle (percentage) = \frac{Ton}{Total Period} \ast 100

 

E.g. A pulse with a period of 10ms will remain ON (high) for 2ms.Therefore, the duty cycle will be

D = 2ms / 10ms = 20%

Through the PWM technique, we can control the power delivered to the load by using the ON-OFF signal. The PWM signals can be used to control the speed of DC motors and to change the intensity of the LED. Moreover, it can also be used to generate sine signals.

Pulse Width Modulated signals with different duty cycle are shown below

PWM Duty Cycle
PWM Duty Cycle

PIC18F4550 controller has an in-built 10-bit PWM module known as the CCP module. The pin CCP1 (RC2) is used for generating PWM signals. It needs to be configured as an output.

 

PIC18F4550 PWM Pins

PIC18F4550 PWM Pins
PIC18F4550 PWM Pins

In PIC18F4550, only Timer2 can be used for PWM generation. TMR2 is a 16-bit Timer2 register which is used to hold the count.

In PIC18F4550 following registers are used for PWM generation.

Note: Here, we are using a CCP1 module. If we want to use the CCP2 module then we need to modify the register name as CCPR1L to CCPR2L, CCP1CON to CCP2CON.

Also, make the RC1 pin as output for PWM generation.

 

CCPR1H and CCPR1L register

In the CCP module, there is a 16-bit register which is split into two 8-bit registers - CCPR1H and CCPR1L.

CCPR1H and CCPR1L register
CCPR1 Register
  • Only CCPR1L is used to decide the duty cycle of the PWM. CCPR1H is not user-accessible for the PWM mode.
  • As the PIC18F4550 generates a 10-bit PWM pulse, to set the duty cycle it uses a 10-bit register. The higher 8 bits (MSBs) DC1B9: DC1B2 of this register are in CCPR1L register (8-bit) and lower 2 bits(LSBs) DC1B1: DC1B0, which are used for a decimal portion in duty cycle, are in CCP1CON register at bit 5 and 4 respectively.
  • So the 10-bit value for duty cycle is represented by CCPR1L: CCP1CON<5: 4>

PR2 register

  • It is an 8-bit register that is used to load a count for a period of the pulse (TPWM).

 

Working of PWM in CCP module

PIC18F4550 PWM Generation Working Block Diagram
PIC18F4550 PWM Generation Working
  1. Load the period value in a PR2 register and the duty cycle value in CCPR1L: CCP1CON<5: 4> registers and initialize the CCP1 pin as an output.
  2. Configure the T2CON register and set the TMR2 register to 0. Also, start the Timer2.
  3. Now when a match occurs between registers PR2 and TMR2, pin CCP1 is pulled high and TMR2 is cleared.
  4. The value of CCPR1L along with the CCP1CON<5: 4> which is a count for duty cycle is moved to the CCPR1H.
  5. Finally, TMR2 is compared with the CCPR1H along with two lower bits of a duty cycle. When matched, the pin CCP1goes low.
TMR2 and PR2 working for PWM generation

This is how PWM is generated in PIC18F4550.

 

CCP1CON register: CCP1 Control Register for PWM

CCP1CON register
CCP1CON Register

DC1B1: DC1B0

These two bits are LSBs which are used for defining the decimal value of a duty cycle.

CCP1M3: CCP1M0: CCP1 module mode select bits

11xx = PWM mode

Other combinations are used to capture and compare modes.

Calculations

Now how to set value for the PR2 register which defines the period value of a pulse.

 

PR2 = \frac{Fosc}{(Fpwm\ast4\ast Timer Prescale Value )}-1

 

where,

Fpwm – Frequency of PWM signal

 

Now, let us see how to set a value for the CCPR1L which decides the duty cycle of a pulse. We know that a duty cycle is some % of the PR2 (period) register. For example, if PR2 is 199 then a 20% duty cycle of the 199 is given by,

duty cycle calculation

e.g.

(CCPR1L: CCP1CON<5: 4>) = (199 + 1) x (20/100)

(CCPR1L: CCP1CON<5: 4>) = 40 = 0b0010100000

 

So, load MSB 8-bits of the above result to the CCPR1L and 2 LSB bits in CCP1CON <5:4>.

i.e. CCPR1L = 0b00101000 = 0x28

      CCP1CON <5:4> = 0b00

Note: CCPR1L (duty cycle) value should be always less than or equal to the PR2 (period) value. If the CCPR1L value is greater than the PR2 value, the pin CCP1 will not be cleared. This allows a duty cycle of 100% and gives the output.

Note: But, if a PR2 value is exceeding 8-bit value i.e. 255 then we have to increase Timer2 pre-scale value.

Max PWM Resolution

\frac{\log (\frac{Fosc}{Fpwm})}{\log 2}

bits

The PWM duty cycle must be a value between 0 and (2 ^ PWM Resolution) - 1.

 

Steps for Programming

  1. Load the PR2 value which will decide the period of the pulse.
  2. Set the duty cycle by loading value in the CCPR1L: CCP1CON<5: 4>
  3. Configure the CCP1CON register for setting a PWM mode.
  4. Initialize the pin CCP1as an output pin which will give PWM output.
  5. Configure the T2CON register and enable TMR2 using T2CON

 

Application 1

Let us generate 10KHz PWM with a 20% duty cycle.

/*
   Generating 10KHz PWM with 20% duty cycle
   www.electronicwings.com
*/

#include "osc_config.h"
#include <pic18f4550.h>

void main()
{

    OSCCON = 0x72;	/* Set internal clock to 8MHz */
    TRISC2 = 0;		/* Set CCP1 pin as output for PWM out */
    PR2 = 199;		/* Load period value */
    CCPR1L = 40;	/* load duty cycle value */
    T2CON = 0;		/* No pre-scalar, timer2 is off */
    CCP1CON = 0x0C;	/* Set PWM mode and no decimal for PWM */
    TMR2 = 0;		/* Clear Timer2 initially */
    TMR2ON = 1;		/* Timer ON for start counting*/

    while(1);

}

 

Application 2

Now, let us control the intensity of LED by generating PWM with different duty cycles using PIC18F4550.

/*
* Control intensity of LED using PIC18F4550
* www.electronicwings.com
*/

#include "osc_config.h"
#include <p18f4550.h>

void MSdelay(unsigned int);

void main()
{
    unsigned int duty_cycle;  
    OSCCON=0x72;         /* set internal clock to 8MHz */
    TRISCbits.TRISC2=0;  /* Set CCP1 pin as output for PWM out */
    PR2=199;             /* load period value in PR2 register */ 
    CCPR1L=1;            /* load duty cycle */
    T2CON=0;             /* no pre-scalar,timer2 is off */
    CCP1CON=0x0C;        /* set PWM mode and no decimal value for PWM */
    TMR2=0;
    T2CONbits.TMR2ON=1;  /* Turn ON Timer2 */
    while(1)
    {

        for(duty_cycle=1;duty_cycle<199;duty_cycle++)
        {
            CCPR1L = duty_cycle;   /* load duty cycle */
            MSdelay(20);
        }
        MSdelay(500);
        
        for(duty_cycle=199;duty_cycle>1;duty_cycle--)
        {
            CCPR1L = duty_cycle;   /* load duty cycle */
            MSdelay(20);
        }
        MSdelay(500);
    }
    
}

void MSdelay(unsigned int val)
{
     unsigned int i,j;
        for(i=0;i<=val;i++)
            for(j=0;j<165;j++);      /*This count Provide delay of 1 ms for 8MHz Frequency */
 }

Video

 

Generate Two different PWM simultaneously

  • We can generate two different PWM on PIC18F4550 on two different channels i.e. CCP1 and CCP2.
  • But these generated PWMs will be of the same frequency. This is because to generate PWM, only Timer 2 is used which is common for both the CCP1 and CCP2.
  • So, the period count which is loaded in the PR2 register to compare it with the Timer 2 (TMR2) register is shared/common. Thus, we can generate two PWM with different duty cycle but with the same frequency.

Now, we will generate Two different PWM on CCP1(RC2) & CCP2(RC1) having the same frequency.

Here, CCP1 will generate PWM of 25% Duty Cycle whereas CCP2 will generate PWM of 50% Duty Cycle.

/*
* Generate Two PWM with different duty cycle on PIC18F4550
* www.electronicwings.com
*/
#include <xc.h>
#include "configuration_bit_header.h"

void main()
{
    OSCCON = 0x76;	/* Set internal clock to 8MHz */
    TRISC1 = 0;		/* Set CCP2 pin as output for PWM out */
    TRISC2 = 0;		/* Set CCP1 pin as output for PWM out */
    PR2 = 199;		/* Load period value */
    
    /**** generate PWM on CCP1 ****/
    CCP1CON = 0x0C;	/* Set PWM mode and no decimal for PWM */
    CCPR1L = 50;    /* load 25% duty cycle value */
    
    /**** generate PWM on CCP2 ****/
    CCP2CON = 0x0C;	/* Set PWM mode and no decimal for PWM */    
    CCPR2L = 100;	/* load 50% duty cycle value */
    
    /*configure Timer 2 for PWM*/
    T2CON = 0;		/* No pre-scalar, timer2 is off */
    TMR2 = 0;		/* Clear Timer2 initially */
    TMR2ON = 1;		/* Timer ON for start counting*/
    while(1);
}

Components Used

PICKit 4 MPLAB
PICKit 4 MPLAB
1
PIC18f4550
PIC18f4550
1
LED 5mm
LED 5mm
1
Breadboard
Breadboard
1

Downloads

PIC18F4550 PWM Proteus Simulation Download
Microchip Application Notes on CCP Module Download
PIC18F4550 PWM Project File Download
Ad