PIC18F4550 Timer Capture (CCP) Mode
PIC18F4550 Timer Capture (CCP) Mode
PIC18F4550 Timer Capture (CCP) Mode
Introduction
Input Capture is widely used in many applications such as:
The time of an event occurrence can be recorded by latching the count when an edge is detected (rising or falling). This can be done using input
capture mode in the microcontrollers.
PIC18F4550 has an in-built CCP (Capture / Compare / PWM) module which has input capture mode. The input capture of the signal has applications
like finding frequency, calculating duty cycle of the input signal etc.
Let us see the capture mode in PIC microcontroller 18F4550.
PIC18F4550 has two CCP modules -
1. CCP1
2. CCP2
Here, we will be discussing CCP1 module.
In capture mode, input pulse should be connected to CCP1 which is multiplexed with PORTC.2 (RC2) as shown below, and configure it as the input
pin.
CCPR1 Register
2. CCP1CON Register : CCP Control Register
This is an 8-bit register used to select the capture mode. Let’s see the bit configuration of this register.
CCP1CON Register
CCP1M3 : CCP1M0 : CCP1 Mode Select bits
0100 = Capture mode is selected for every falling edge detected.
0101 = Capture mode is selected for every rising edge detected.
0110 = Capture mode is selected for every 4th rising edge detected.
0111 = Capture mode is selected for every 16th rising edge detected.
And the other combinations of these bits are used for Compare and PWM mode.
DCxB1 and DCxB0 are used for PWM mode only.
CCP1IF Interrupt Flag
PIR1 Register
CCP1IF :
1 = TMR1 or TMR3 register capture occurred (at falling or rising edge of pulse)
0 = No TMR1 or TMR3 capture occurred.
We need to enable this (CCP1IF) flag by enabling the CCP1IE bit (CCP Interrupt enable bit) in the PIE1 register.
PIE1 Register : Peripheral Interrupt Enable
PIE1 Register
T3CON Register : Timer3 Control and Status Register
T3CON register is used for selecting a timer for the capture mode. This is shown below
T3CON Register
T3CCP2 : T3CCP1 (bit 6 and bit 3)
These two bits in combination are used for Timer and Capture mode selection.
00 = Timer1 is the capture / compare clock source for both the CCP modules.
01 = Timer3 is the capture / compare clock source for the CCP2,
Timer1 is the capture / compare clock source for the CCP1
1x = Timer3 is the capture / compare clock source for both the CCP modules.
RD16 :
This bit is used to specify that the Timer3 register is 16-bit (TMR3) or two 8-bit (TMR3H and TMR3L).
RD16 = 1, Enable one 16-bit register.
RD16 = 0, Enable two 8-bit registers.
The setting of this bit is required only when we select Timer3 for the capture mode.
Otherwise, RD16 in T1CON register is used for Timer1.
Capture Operation
1. Make timer ON.
2. Monitor until CCP1IF flag==1, which is set when capture occurs.
3. Copy the value of the CCPR1 register to any data variable and get a time of event occurrence.
Application
Let’s design an application using PIC18F4550 in which we will measure Frequency of the input signal and display it on 16x2 LCD. The input signal is
connected to CCP1 (RC2) pin for frequency measurement.
/*
Frequency Measurement using Input Capture Mode in PIC18F4550
http://www.electronicwings.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <p18f4550.h>
#include "osc_config.h"
#include "LCD_8bit_file.h"
#include <string.h>
void main()
{
unsigned long signal_period,data1,data2;
unsigned long frequency_Hz[20];
float Frequency;
TRISCbits.TRISC2=1;
OSCCON=0x72; /* set internal clock to 8MHz */
LCD_Init();
memset(frequency_Hz,0,20);
LCD_String_xy(0,1,"Pulse");
PIE1bits.CCP1IE=1;
PIR1bits.CCP1IF=0;
CCP1CON=0x05; /* Capture mode is selected for detecting Rising edge */
CCPR1=0x00; /*CCPR1 is capture count Register which is cleared initially*/
TMR1IF=0;
T1CON=0x80; /* Enable 16-bit TMR1 Register,No pre-scale,use internal clock,Timer OFF */
TMR1=0;
TMR1ON=1; /* Turn-On Timer1 */
while(1)
{
while(!(PIR1bits.CCP1IF)); /*Wait for Interrupt flag which is generated when edge is detected*/
PIR1bits.CCP1IF=0;
data1 = CCPR1; /*Copy count of 1st edge detected*/
while(!(PIR1bits.CCP1IF)); /*Wait for Interrupt flag which is generated when edge is detected*/
PIR1bits.CCP1IF=0;
data2 = CCPR1; /*Copy count of 2nd edge detected*/
LCD_String_xy(2,0,frequency_Hz);
}
TMR1=0;
memset(frequency_Hz,0,20);
}
}
PIC18F4550 Timer Compare Mode
Introduction
PIC18F4550 has inbuilt CCP module which has Capture, Compare and PWM mode for various applications.
CCP in Compare mode is used to generate waveform of various duty cycle like PWM and also used to trigger an event when pre-determined
time expires.
Also it is used to generate specific time delay.
PIC18F4550 has 2 in-built CCP module i.e. CCP1 and CCP2.
Output (e.g. Waveform generation) of CCP2 and CCP1 in compare mode is generated on two pins i.e. RC1 and RC2 respectively.
CCP1CON Register
DC1B1:DC1B0:
Used for PWM mode.
CCP1M3:CCP1M0: CCP1 module mode select bit
These bit decides which action takes on compare match.
0010 = Toggle output on match
1000 = Initialize CCP1 pin low, on compare match this pin is set
to high.
1001 = Initialize CCP1 pin high, on compare match this pin is set
to low.
1010 = On compare match generates software interrupt.
1011 = On compare match trigger special event, reset timer, start
ADC conversion.
So Configure compare mode as per application using CCP1CON Register.
Other combinations are used for Capture and PWM mode.
Interrupt on Match
Also remember that after every compare match between CCPR1 and Timer1/Timer3 CCP1IF interrupt flag is set.
This flag is located at PIR1<bit2> Register.
Also note that T3CON register is used to select Timer1 or Timer3 for compare mode.
T3CON Register: Timer3 Control Register
T3CON Register
T3CCP2:T3CCP1: Used to select Timer for Compare mode operation.
00 = Timer1 for both CCP module
01 = Timer1 for CCP1 module
Timer3 for CCP2 module
1x = Timer3 for both CCP module
Calculation
Now How to calculate Count for CCPR1?
Following figure illustrates steps to calculate count for desired delay which to be load into CCPR1 Register.
Application 1
Here let’s generate a square wave of 1 KHz having 50% duty cycle.
For 50% Duty Cycle
Assume Oscillator frequency = 8MHz
Period of waveform = 1ms (1 KHz given)
So Instruction Cycle = 1/ (FOSC/4) = 1/ (8MHz/4) = 0.5 us
That means Timer will increment its count continuously after each 0.5us delay.
Here I used Timer3 register.
Now calculate CCPR1 count.
For 50% Duty Cycle – ON time=OFF time=0.5ms
0.5ms/0.5us = 1000 i.e. 0x03E8
CCPR1 = 0x03E8
/*
* Generate waveform of 1KHz having 50% duty cycle
* https://www.electronicwings.com
*/
#include "osc.h"
#include <p18f4550.h>
void main()
{
OSCCON=0x72; /* Configure internal clock at 8MHz */
TRISCbits.TRISC2=0; /* Configure RC2 pin as output */
CCP1CON=0x02; /* Module is configured for compare mode and is set up so that upon a compare match of CCPR1 and
TMR3, RC2 is driven low*/
PIR1bits.CCP1IF=0; /* Clear interrupt flag*/
TMR3=0; /* Clear Timer3 Register*/
T3CON=0xC0; /* Timer3 used for compare mode with TMR3 register in 16-bit format*/
CCPR1=1000; /* Load a count for generating 1khz*/
T3CONbits.TMR3ON=1; /* Turn On Timer3*/
while (1)
{
/* Wait for CCP Interrupt flag to set, it is set on compare match between CCPR1 and TMR3*/
while(PIR1bits.CCP1IF==0);
PIR1bits.CCP1IF=0;/* Clear interrupt flag */
TMR3=0;
}
}
Output
Application 2
Now generate another 1KHz waveform with 40% duty Cycle.
For 40% Duty Cycle,
ON time=0.4ms, OFF time=0.6ms
0.4ms/0.5us = 800 i.e. 0x0320
0.6ms/0.5us = 1200 i.e. 0x04B0
Load these values into CCPR1 Register
/*
* Generate waveform of 1KHz having 40% duty cycle
* https://www.electronicwings.com
*/
#include "osc.h"
#include <p18f4550.h>
void main()
{
OSCCON=0x72; /* Configure internal clock at 8MHz */
TRISCbits.TRISC2=0; /* Configure RC2 pin as output. */
CCP1CON=9; /* Module is configured for compare mode and is set up so that upon a compare match of CCPR1 and
TMR3, RC2 is driven high*/
PIR1bits.CCP1IF=0;
TMR3=0;
T3CON=0xC0; /* Timer3 used for capture mode with TMR3 register in 16-bit format*/
CCPR1=0x320; /* Load a count for generating 0.4ms*/
T3CONbits.TMR3ON=1; /* Turn On Timer3*/
while(1)
{
/* Wait for CCP Interrupt flag to set, it is set on compare match between CCPR1 and TMR3*/
while(PIR1bits.CCP1IF==0);
PIR1bits.CCP1IF=0; /* Clear interrupt flag*/
TMR3=0; /* Clear Timer3 Register*/
CCP1CON=8; /* CCP1 module is configured for compare mode and is set up so that upon a compare match
of CCPR1 and TMR3, RC2 is driven low*/
CCPR1=0x04B0; /* Load count for generating 0.6ms */
while(PIR1bits.CCP1IF==0);
PIR1bits.CCP1IF=0;
TMR3=0; /* Clear Timer3 Register*/
CCP1CON=9;
CCPR1=0x0320; /* Load count for generating 0.4ms */
}
}
Output
PIC18F4550 PWM
Introduction
Pulse Width Modulation (PWM) is a technique by which width of a pulse is varied while keeping the frequency of the wave constant.
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.
E.g. A pulse with a period of 10ms will remain ON (high) for 2ms.Therefore, duty cycle will be
D = 2ms / 10ms = 20%
Through PWM technique, we can control the power delivered to the load by using 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
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 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 which is used to load a count for a period of the pulse (TPWM).
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 for capture and compare modes.
Calculations
Now how to set value for the PR2 register which defines the period value of a pulse.
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 PR2 (period)
register. For example, if PR2 is 199 then 20% duty cycle of the 199 is given by,
e.g.
(CCPR1L: CCP1CON<5: 4>) = (199 + 1) x (20/100)
(CCPR1L: CCP1CON<5: 4>) = 40 = 0b0010100000
So, load MSB 8-bits of 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 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
bits
The PWM duty cycle must be a value between 0 and (2 ^ PWM Resolution) - 1.
Application 1
Let us generate 10KHz PWM with 20% duty cycle.
/*
Generating 10KHz PWM with 20% duty cycle
www.electronicwings.com
*/
#include "osc_config.h"
#include <pic18f4550.h>
void main()
{
while(1);
Application 2
Now, let us control the intensity of LED by generating PWM with different duty cycle using PIC18F4550.
/*
* Control intensity of LED using PIC18F4550
* www.electronicwings.com
*/
#include "osc_config.h"
#include <p18f4550.h>
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);
}
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 */