Capture Mode in PIC16F877A

Thread Starter

Fanfire174

Joined Mar 13, 2018
209
How to use capture mode in PIC16F877A (MPLABX). I have written simple code to turn on led on Every Rising Edge but its not working
Please look at code, anything missing ? Please help..
Code:
void main(void)
{
   //Make all PORTD pins low
   PORTA = 0;
   PORTB = 0;
   PORTC = 0;
   PORTD = 0;

// Configured PORTD as Output
   TRISA = 0;
   TRISB = 0;
   TRISD = 0;

//Configured RC2/CCP1 pin as input
   TRISC2 = 1;    

   ADCON1 = 0x07;
   CMCON = 0x07;

//CCP1 MODULE INITIALIZATION
   CCPR1L = 0;       //Clear Lower byte of CCP Register
   CCPR1H = 0;       //Clear Higher byte of CCP Register
   T1CON = 0;        // Prescale in timer mode
   CCP1CON = 0b00000101;
   INTCON = 0xc0;    //Enabled Global interrupts & Peripherals interrupt
 
   while(1);

}

void interrupt CCP1_ISP()
{
   // CCP1 Interrupt
   if(PIR1.CCP1IF == 1)   // if the CCP1 Interrupt flag is set...
   {
      PORTB.F5 = 1;
      PIR1.CCP1IF = 0;
   }

   else PORTB.F5 = 0;
}
 

JohnInTX

Joined Jun 26, 2012
3,809
For a start you need to enable CCP interrupt.
CCP1IE (PIE1<2>) = 1;
That and:
You turn the LED on on a capture interrupt but there is nothing that turns it off. The only source of interrupt is a rising edge capture and it will never execute the 'else' in the interrupt service routine.
TIMER0 is not running so the capture value will always be 0000h.
You don't need to clear CCPR1L/H, those will be loaded with the value of TIMER1 when it detects the rising edge.

If you are just looking for the rising edge, consider using the external INT pin. You can select the polarity with the INTEDG bit in the OPTION_REG.

Good luck!
 

Thread Starter

Fanfire174

Joined Mar 13, 2018
209
For a start you need to enable CCP interrupt.
CCP1IE (PIE1<2>) = 1;
I am running following code but its not working
Code:
void main(void)
{
   //Make all PORTD pins low
   PORTA = 0;
   PORTB = 0;
   PORTC = 0;
   PORTD = 0;

// Configured PORTD as Output
   TRISA = 0;
   TRISD = 0;
   TRISB0 = 0; //RB0 as Output PIN
 
//Configured RC2/CCP1 pin as input
   TRISC2 = 1;  
   ADCON1 = 0x07;
   CMCON = 0x07;

//CCP1 MODULE INITIALIZATION

   T1CON = 0;            // Prescale in timer mode
   CCP1CON = 0b00000101;
   INTCON = 0xc0;         //Enabled Global interrupts & Peripherals interrupt
   CCP1IE = 1;        //Enabled CCP1 interrupt
   while(1);
}

void interrupt CCP1_ISP()
{
   // CCP1 Interrupt
   if(CCP1IF == 1)   // if the CCP1 Interrupt flag is set...
   {
       RB0 = 1;       //LED ON
       CCP1IF == 1;
   }
}
 

Ian Rogers

Joined Dec 12, 2012
662
TIMER0 is not running so the capture value will always be 0000h.
Timer 0 on those chips always run, it doubles as the watchdog ( when enabled ).. He never clears it though!!

But you are correct... If he needs to see it working, the LED isn't going to show anything..

Also the CCP1IF == 1; isn't right either... Try CCP1IF = 0;
 

AlbertHall

Joined Jun 4, 2014
8,381
The only thing I can see looking at the code is that timer1 is turned off.
I am not sure if timer1 running is a requirement for capture mode so try:
T1CON = 0x01;
 

Thread Starter

Fanfire174

Joined Mar 13, 2018
209
Hello albertHall and Ian

I used below code but still no luck

Code:
void main(void)
{
   //Make all PORTD pins low
   PORTA = 0;
   PORTB = 0;
   PORTC = 0;
   PORTD = 0;

// Configured PORTD as Output
   TRISA = 0;
   TRISD = 0;
   TRISB0 = 0; //RB0 as Output PIN

//Configured RC2/CCP1 pin as input
   TRISC2 = 1;

   ADCON1 = 0x07;
   CMCON = 0x07;
 
//CCP1 MODULE INITIALIZATION
   T1CON = 0x01;            // Prescale in timer mode
   CCP1CON = 0b00000101;
   INTCON = 0xc0;         //Enabled Global interrupts & Peripherals interrupt
   CCP1IE = 1;        //Enabled CCP1 interrupt
   while(1);
}

void interrupt CCP1_ISP()
{
   // CCP1 Interrupt
   if(CCP1IF == 1)   // if the CCP1 Interrupt flag is set...
   {
       RB0 = 1;       //LED ON
       CCP1IF == 0;
   }
}
 

jpanhalt

Joined Jan 18, 2008
7,918
When I have done capture, I used TMR1, and it ran continuously. Unfortunately, I don't have code in C. Hopefully the comments will help. I used polling instead of an interrupt, but that is a small change.
Code:
Start
  
     movlb     0              ;                                                 |B0
     bcf       T1CON,TMR1ON   ;turn TMR1 off
     clrf      TMR1H          ;                                                 |B0
     clrf       TMR1L          ;                                                 |B0
     bsf       T1CON,TMR1ON   ;turn TMR1 on                                     |B0
     banksel   CCP1CON        ;                                                 |B5
     movlw     b'00000101'    ;                                                 |B5
     movwf     CCP1CON        ;CCP1 rising edge capture                         |B5      
     movlb     0              ;                                                 |B0
  
_Data                         ;TMR1 runs continuously                           |B0
     bcf       PIR1,CCP1IF    ;flag must be cleared after mode change           |B0
     btfss     PIR1,CCP1IF    ;test CCP1 interrupt flag (interrupt not enabled) |B0
     goto      $-1            ;                                                 |B0
     banksel   CCP1CON        ;                                                 |B5
     movf      CCPR1H,w       ;start time high byte                             |B5
     movwf     CCP_T1H        ;save value in shadow register                    |B?
     movf      CCPR1L,w       ;start time low byte                              |B5
     movwf     CCP_T1L        ;save value in shadow register                    |B?
 

AlbertHall

Joined Jun 4, 2014
8,381
I am assuming you are using the XC8 compiler. If not then let me know.

I have taken your program and fixed the 'CCP1IF == 0' and added config and XC.h include.
The config assumes you are using the XT crystal oscillator. If this is wrong you should change this for whatever oscillator you are using.

That program works in the simulator so maybe the problem is with either the button or the LED connections.
I have further added some code to test the button and the LED. To make this work you should set the #define _XTAL_FREQ to whatever your oscillator frequency actually is (my code assumes 4MHz).

Now when you run the program the LED should blink at a 1Hz rate.
Then when you press the button (keep it pressed for at least a second) the LED should turn off.
When you release the button, the program is ready to generate an interrupt and turn on the LED the next time the button is pressed.

Code:
// PIC16F877A Configuration Bit Settings

// 'C' source line config statements

// CONFIG
#pragma config FOSC = XT  // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF  // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON  // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF  // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF  // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF  // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF  // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF  // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ 4000000

void main(void)
{
  //Make all PORTD pins low
  PORTA = 0;
  PORTB = 0;
  PORTC = 0;
  PORTD = 0;
// Configured PORTD as Output
  TRISA = 0;
  TRISD = 0;
  TRISB0 = 0; //RB0 as Output PIN
//Configured RC2/CCP1 pin as input
  TRISC2 = 1;
   
// Test I/O pins
  while(RC2 == 0)
  {
  RB0 = 1;
  __delay_ms(500);
  RB0 = 0;
  __delay_ms(500);
  }
   
  while(RC2 == 1)
  ;
  ADCON1 = 0x07;
  CMCON = 0x07;
//CCP1 MODULE INITIALIZATION
  T1CON = 0x01;  // Prescale in timer mode
  CCP1CON = 0b00000101;
  INTCON = 0xc0;  //Enabled Global interrupts & Peripherals interrupt
  CCP1IE = 1;  //Enabled CCP1 interrupt
  while(1);
}
void interrupt CCP1_ISP()
{
  // CCP1 Interrupt
  if(CCP1IF == 1)  // if the CCP1 Interrupt flag is set...
  {
  RB0 = 1;  //LED ON
  CCP1IF = 0;
  }
}
 

Thread Starter

Fanfire174

Joined Mar 13, 2018
209
I am assuming you are using the XC8 compiler. If not then let me know.

I have taken your program and fixed the 'CCP1IF == 0' and added config and XC.h include.
The config assumes you are using the XT crystal oscillator. If this is wrong you should change this for whatever oscillator you are using.

That program works in the simulator so maybe the problem is with either the button or the LED connections.
I have further added some code to test the button and the LED. To make this work you should set the #define _XTAL_FREQ to whatever your oscillator frequency actually is (my code assumes 4MHz).

Now when you run the program the LED should blink at a 1Hz rate.
Then when you press the button (keep it pressed for at least a second) the LED should turn off.
When you release the button, the program is ready to generate an interrupt and turn on the LED the next time the button is pressed.
yes I am using XC8 compiler I used this code but still led doesn't turn ON

Push button connected to RC2 and led connected RB0

look at screenshot for connection
 

JohnInTX

Joined Jun 26, 2012
3,809
Timer 0 on those chips always run, it doubles as the watchdog ( when enabled ).. He never clears it though!!

But you are correct... If he needs to see it working, the LED isn't going to show anything..

Also the CCP1IF == 1; isn't right either... Try CCP1IF = 0;
Oops! I meant timer 1. Thanks.
The test for CCP1IF==1 is correct to detect the CCP interrupt. It’s not really needed since there is only one interrupt source but it is good programming practice to check it.
 

AlbertHall

Joined Jun 4, 2014
8,381
Oops! I meant timer 1. Thanks.
The test for CCP1IF==1 is correct to detect the CCP interrupt. It’s not really needed since there is only one interrupt source but it is good programming practice to check it.
Yes, but good practice would check both the enable and the flag.
 

JohnInTX

Joined Jun 26, 2012
3,809
Yes, but good practice would check both the enable and the flag.
Agreed and checking the enable is necessary if more than one interrupt source is present and the interrupt in question can be disabled. If there is only one interrupt active and it's never turned off, you can get away without even checking the interrupt flag since that's the only way you'll get there with only one.

But it is a good idea to check both IF and IE just to keep things standard. In assembler, I also check at the end of the interrupt routine for any stray interrupts i.e. none of the implemented ones caused the vector to the interrupt location and take remedial action if necessary.

Cheers!
 

Ian Rogers

Joined Dec 12, 2012
662
Oops! I meant timer 1. Thanks.
The test for CCP1IF==1 is correct to detect the CCP interrupt. It’s not really needed since there is only one interrupt source but it is good programming practice to check it.
I Have an oops as well.... The clearing of the flag is the one I meant!!
 
Top