Pulse Width Modulation Using PIC18F4550

Pulse Width Modulation Using PIC18F4550

Pulse width modulation (PWM) is a widely used modulation technique not only in communication systems but also high current driving applications like motor drivers, LED drivers etc. This tutorial explains how to generate pulse width modulation using PIC18F4550.

What is pulse width modulation

PWM is a digital modulation technique widely used for coding a digital data into a pulsating signal which usually looks like a square wave. The applications such as motor speed control, for encoding messages in telecommunication systems and for controlled switching in switch mode power supplies and for sound synthesis in audio amplifiers etc.
PWM uses a rectangular pulse train whose modulation results in the average value of the pulse sequence.

pwm-duty-cycle-variations

Period and Duty Cycle

The period is the total length of the pulse and it is measured in time. Pulse repeats over this defined time, which is referred to as a period.

Duty cycle is the percentage of ON time over the total period of the pulse. For example, if the period of the pulse is 100 ms and the ON time is 50 ms, then the period is (50 ms/100 ms)% which is 50%. A 50% pulse is a square wave whereas 100% pulse is equal to setting it to 5V.

pwm-duty-cycle-calculation
A Pulse

So we can define,
Duty cycle = (d/t) x 100 percentage
where d = ON time and t = period

PWM in PIC Microcontroller

PIC18F4550 devices all have two CCP (Capture/Compare/PWM) modules. Each module contains a16-bit register, which can operate as a 16-bit capture register, a 16-bit compare register or a PWM master/slave duty cycle register which is primarily controlled by timers modules.

CCP Module

CCP stands for capture/compare and PWM. It is a hardware module inside the PIC microcontroller helps to trigger events based on time.

Capture mode allows us a duration based timing of an event. This circuit gives information regarding the current state of a register which constantly changes its value. In this case, it is the timer TMR1 register.

Compare mode compares values contained in two registers at some point. One of them is the timer TMR1 register. This circuit also allows the user to trigger an external event when a predetermined amount of time has expired.

PWM module generates the rectangular pulses whose duty cycle and frequency can be varied by altering the PWM registers.

Here in PIC18F4550, Port C pins RC1 and RC2 acts as PWM output pins.

PWM operation steps

Following steps are needed to setup the PWM module in PIC18F4550.

  1. Set the PWM Period
  2. Set PWM duty cycle
  3. Output pin configuration
  4. Prescalar value and timer configuration
  5. PWM Module configuration

1. Set the PWM Period

The PWM period is set by writing the PR2 register. So the value can be calculated by using this formula.i.e,

PWM period = [(PR2) + 1] x 4 x Tosc x (TMR2 prescale value)

For example, we use a 20MHz clock and the O/P frequency is 2KHz
where PWM period = 1/ Frequency (that will be 1/2000 = .0005)

.0005 = (PR2 + 1) x (1 / 20000000) x 16
PR2 + 1 = (.0005 x 20000000) / 16
PR2 = 624
PR2 = 0x270 ( 624 in hex)

PR2 = 0x270; // Set the PWM period

2. Set the PWM duty cycle

The PWM duty cycle is specified by writing to the CCPRxL register and to the CCPxCON<5:4> bits. Up to 10-bit resolution is available. The CCPRxL contains the eight MSbs and the CCPxCON<5:4> bits contain the two LSbs. This 10-bit value is represented by CCPRxL: CCPxCON<5:4>. The duty cycle will be in the range of 0 to 1024.

PWM Duty Cycle = (CCPRXL:CCPXCON<5:4>) x Tosc x (TMR2 Prescale Value)

For example, for setting up PWM module 1 of duty cycle value 16, we need to write first 8-bit to CCPR1L and LSB two bits in CCP1CON<5:4>. In the above example, we’ve used a prescaler value of 16 (0b0000010000). So CCPR1L will become 00000100 and CCP1CON<5:4> will be 00.

CCPR1L = 0x05;          // PWM duty cycle - first 8-bits (MSb)
CCP1CON &= (0x00 <<4);  // PWM duty cycle - last 2-bits (LSb)

3. Output pin configuration

In this step, we need to set the pins used by PWM module as output. In our case, it is the RC2 pin.

/*set RC2 as PWM  out pin*/
  TRISCbits.RC2 = 0;        // TRISC &=0xFD

4. Prescalar value and timer configuration

Prescalar value is set in TMR2 register. After setting up the register, enable the timer2 module by writing to T2CON.

TMR2 = 0x0F;                       // Timer2 initial value, if needed
T2CONbits.T2CKPS0 = 1;            // Prescalar values 1:16
T2CONbits.T2CKPS1 = 1;            // Prescalar values 1:16
T2CONbits.TMR2ON = 1;           // Turn ON the timer after setting up everything

5. PWM module configuration

In this step, we are configuring the CCP module for PWM operation. We can do this by CCP1CON mode selection bits (bits 3-0).

    CCP1CON &= 0xCF;        // 5,4 bits zeroed (DC1B1:DC1B0 = 00)
    CCP1CON |= 0x0C;      //PWM mode ( CCP1M3:CCP1M0 = 1100)

By using the above register configurations, we can generate PWM output of 2KHz.

Sample code using register settings

Below is the sample firmware using above mentioned settings. They are arranged as two functions, one for initialization and other for setting duty cycle.

#include 
#include "config.h"         //  Configuration bit settings header file

void PWM1_Init(unsigned char period){
    TRISC &=0xFD;           //  Set RC2 as output
    
    /* CCP PWM mode */
    CCP1CON &= 0xCF;        //  5,4 bits zeroed (DC1B1:DC1B0 = 00)
    CCP1CON |= 0x0C;        //  PWM mode ( CCP1M3:CCP1M0 = 1100)
    
    /* Timer2 configuration */
    PR2 = period;           //  configure timer2 period
    T2CON = 0x02;           //  Set prescalar 16   
    TMR2ON = 1;             //  timer2 on
    
}

void PWM1_setDC(unsigned int dutycycle){
    
    CCPR1L = dutycycle>>2;  //  PWM duty cycle - first 8-bits (MSb)
    CCP1CON &= 0xCF;        //  5,4 bits zeroed (DC1B1:DC1B0 = 00)
    CCP1CON |= ((dutycycle<<4)&0x30);  //  PWM duty cycle - last 2-bits (LSb) in CCP1CON 5,4 bits    
}

void main()
 {
    /*open PWM at 2KHz*/ 
    PWM1_Init(0x270);
    
    /*set duty cycle 0 - 1023 range */
    PWM1_setDC(512);
 
    while(1);               //  Infinite run
}

The above code doesn’t set an initial value at TMR2. If the need arises, set it.

Sample code for Pulse width modulation using PIC18F4550 – XC8 library

Microchip’s XC8 has a PWM peripheral library which is very easy to use. It has a separate library for timers and PWM, includes them separately and calls the functions with necessary arguments.

/*include Header files */

#include <xc.h>
#include "config.h"     //configuration bit settings
#include <plib/timers.h>
#include <plib/pwm.h>

/*The speed of your internal(or)external oscillator*/
#define _XTAL_FREQ 20000000

void main()
 {
 /*set RC2 as PWM  out pin*/
 TRISCbits.RC2 = 0;
 
 /*configuring Timer 2 and set as clock source, at 1:16 prescalar */ 
 Timer2Config = T2_PS_1_16;
 OpenTimer2(Timer2Config);

 /*open PWM at 2KHz*/ 
 OpenPWM1(0x270);
 
 /*set duty cycle 0 - 1023 range */
 SetDCPWM1(512);
 
 while(1);      //always run
}