# Implementing PWM On PIC18F Microcontroller

PWM output with 20% duty cycle.

# What is PWM?

As you guys know, microcontrollers, FPGAs, and other embedded processors can only output a high or low signal based on the power supplied to it. In other words, embedded processors cannot output a variety of voltages. Despite this, we have the processor “pretend” to output different voltages using a series of pulses and varying the width of the pulses. The technique of varying the width of a series of pulses is called Pulse Width Modulation (PWM).

The picture above shows an example of a simple pulse train.  The time it takes for each pulse to start again is referred as its period (Ts).  In each pulse train, the pulse is on for a certain amount of time before turning off. The percentage which the pulse’s on time compared to its time off is called the duty cycle.  Duty Cycle is expressed as…

$D=\frac{T_{on}}{T_{on}+T_{off}}$

# PIC18F And PWM

As you’re already aware of, all microcontrollers feature a set number of PWM pins. But what pins are specifically designed for PWM? When reading your PIC18F datasheet, look for pins that have CCP (compare capture pwm) labeled. So let’s look at a picture featuring the PIC18F4553

As you see in the picture, RC1, RC2 and RB3 can be configured as PWM pins.

# PWM Calculations

So there are three crucial registers you must take in account for PWM: PR2, and CCPRXL:CCPXCON[5:4]. Configuring the PR2 register, will allow you to configure the PWM period while selecting a proper the value for CCPRXL:CCPXCON[5:4] will allow you to choose the right duty cycle. For choosing the right value for the PR2 register, use the following equation…

$PR2=\frac{PWM Period}{4*T_{osc}*TMR Prescale Value}-1$

Keep in mind that Tosc is the the inverse of the frequency that your microcontroller is running at. Also, we will use a prescale of 16 as we will use this in the upcoming code example. For CCPRXL:CCPXCON[5:4], we will use the following equation…

$CCPRXL:CCPXCON[5:4]=\frac{PWM Duty Cycle}{T_{osc}*TMR Prescale}$

CCPRXL:CCPXCON[5:4] is a 10 bit number. To make things easier on you guys, make sure you convert your number to a 10 bit binary number. After you convert the number to a binary number, set your CCPRXL register to the first 8 MSB numbers. Finally, take the last 2 LSB and you use it for your CCPXCON[5:4].

# PWM Coding Procedure

Now that I talked about the calculations, let’s talk about how to code your PIC18F microcontroller to use PWM. For this tutorial, we will use the PIC18F4553’s  CCP1 pin, set the PWM period at 1KHZ, a time prescale of 16, a clock frequency of 8*10^6 and a 50% duty cycle.Parts of the final code can be found in my Beginner’s Guide To PIC18F Microcontroller.

First we need to configure the CCP1 pin as an output pin,which is RC2.To do this we set the TRISCbits.RC2 to 0 and PORTCbits.RC2 to 0.

TRISCbits.RC2=0;
PORTCbits.RC2=0;

Now, we need to choose a value for PR2 register. Here is my calculation for PR2.
$PR2=\frac{10^{-3}}{4*16*\frac{1}{8*10^{6}}}-1=124$
Now that we found the number, let’s convert it to a binary number and use it.

PR2 = 0b1111011;

PWM uses timer2 to work. So we need to not only turn on timer2, but we must set the time scale to 16. This can be done by properly configuring T2CON by setting T2CON’s TMR2ON to 1, and T2CKPS1 to 1.

T2CON = 0b00000111 ;

CCPR1L:CCP1CON[5:4] is the last thing to compute.
$CCPR1L:CCP1CON[5:4]=\frac{\frac{1}{10^{3}}*.5}{\frac{1}{8*10^{6}}*16}=250$
When we convert 250 to its binary value, we get 0011111010. However, we take the first 8 MSB for CCPR1L and the last 2 LSB for CCP1CON bit 5 and CCP1CON bit 4.

CCPR1L = 0b00111110;
CCP1CON = 0b00101100;

Since you guys been so swell, here’s the final code for this example.

#include <p18f4553.h>
#include <stdio.h>
#include <stdlib.h>
#include <usart.h>
#include <delays.h>
#include <portb.h>

#pragma config FOSC = HS
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config PWRT = ON
#pragma config DEBUG=OFF

int main(void)
{
TRISCbits.RC2=0;
PORTCbits.RC2=0;
PR2 = 0b01111100 ;
T2CON = 0b00000111 ;
CCPR1L = 0b00111110;
CCP1CON = 0b00101100;

while(1);
}

### 8 thoughts on “Implementing PWM On PIC18F Microcontroller”

1. which compiler you have used???

2. can you give me the formula for PR2 and CCPR1L:CCP1CON[5:4], i doing solar tacker which i use pic18f4550. i need to control the servo motor so it can face the sun. so i need those for calculation so it will always face the sun.

3. Awesome tutorial! Thanks! But one question…Is the screen shot of the PWM signal (on the top of this page) the actual signal you got from your code? The reason I ask is because I see that the period of the signal is 1ms (which corresponds to your desired value of 1 kHz). But the duty cycle from the signal looks much less than the 50% you put for your code…It looks closer to 20%….

• I apologize for the slow response. I’ve been swapped with work.

To answer your question: yes and no. The picture was from an old PWM output code, which I failed to o mention in my post. Thank you!

4. Cool Cap’s example is similar to some earlier work I did plugging in numbers. For those of you who wixh not to go through this painful exercise, you can try the calculator method to really speed up your expended time.

http://www.micro-examples.com/public/microex-navig/doc/097-pwm-calculator.html

5. hello,

how can CCPR!L value be more than PR” value. Isnt it supposed to be like half of PR2 value for 50% duty cycle or 3/4th of PR2 value for 75% duty cycle