Using CCP module’s compare mode on PIC18

On this article I talk about the compare mode available on the CCP units of every PIC18.

The compare mode works almost the same way as the capture mode: the counting from the reference timer is constantly compared to a programmed value (on the CCPRxH and CCPRxL registers). When the counting matches that value a compare event is triggered which then can trigger one of the following events:

  1. The output pin (RC2/CCP1 or RC1/CCP2 depending on the module) is set;
  2. The output pin is cleared;
  3. The output pin is toggled (0->1 or 1->0);
  4. The output pin state is not modified;
  5. A special event is triggered (on CCP1 it resets timer 1 and on CCP2 it resets timer 1 or timer 3 and also starts an ADC conversion as long the ADC module is enabled).

In all cases the compare event also sets the module interrupt flag (CCP1IF or CCP2IF).

This feature can be used to generate periodic signals, by modifying the output pin state at a periodic time interval or simply generating periodic interrupts (for any internal timed process).

The following figure shows that as soon as timer 1 counting reaches the value stored on channel 1 compare registers (CCPR1H and CCPR1L), a compare event is triggered, toggling the state of pin CCP1. The ISR code should provide the proper CCPR1H and CCPR1L reloading with the next desired time interval.


Note that for signal generation as shown on the figure it is mandatory reloading register pair CCPR1H and CCPR1L with the next trigger point (by adding half the signal period to the contents of those registers).

The following example demonstrates th use of modules CCP1 and CCP2 generating interrupt-only events. The ISR code detects which module triggered the interrupt and reload the next compare point on the corresponding register pair).

Both modules are configured to use timer 1 as the timebase. The timer 1 is clocked by the system clock (FOSC/4) divided by 8. On the PK2Lab board that results on 4MHz/4/8 = 125kHz, meaning that every timer count is done in 1/125,000=8µs.

By adding 40,000 counts to each CCP1 compare we can have an interrupt getting triggered each 40,000*8µs=320ms. That means that a led connected to RC0 will blink at a rate of 1.56250Hz (a period of 640ms) and a led connected to RC1 will blink at a rate of 3.125Hz (320ms).

#include <p18f4520.h>
#include <stdio.h>
#include "pic_simb.h"

#pragma config OSC = XT, WDT = OFF, MCLRE = ON
#pragma config DEBUG = OFF, LVP = OFF, PWRT = ON, BOREN = OFF
#pragma config CCP2MX = PORTBE

#define L0	LATCbits.LATC0
#define L1      LATCbits.LATC1

#pragma code isr = 0x000008
#pragma interrupt ISR
void ISR(void)
	if (PIR1bits.CCP1IF)
	{	// CCP1 interrupt
		PIR1bits.CCP1IF = 0;	// clear CCP1 interrupt flag
		CCPR1 += 40000;		// add 40000 to the period
		L0 = !L0;		// toggle led L0 (pin RC0)
	if (PIR2bits.CCP2IF)
	{	// CCP2 interrupt
		PIR2bits.CCP2IF = 0;	// clear CCP2 interrupt flag
		CCPR2 += 20000;		// add 20000 to the period
		L1 = !L1;		// toggle led L1 (pin RC1)
#pragma code

void main(void)
	ADCON1 = 0x0F;			// turn off analog inputs
	TRISC = 0;			// RC0 to RC7 as outputs
	T1CON = bTMR1ON | bT1CLK_PRE8;	// Timer 1 on with prescaler = 8
	CCP1CON = bCCP_COMPARE_INT;	// CCP1 interrupt compare mode
	CCP2CON = bCCP_COMPARE_INT;	// CCP2 interrupt compare mode
	LATC = 0;			// LEDs off
	INTCON = bGIE | bPEIE; 		// GIE and PEIE = 1
	PIE1 = bCCP1IE;			// enable CCP1 interrupt
	PIE2 = bCCP2IE;			// enable CCP2 interrupt
	while(1);			// wait for an interrupt!

Leave a Reply