Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

CCP1 and CCP2 in compare mode conflict

Status
Not open for further replies.

POWERMAN

New Member
Hi,
I work with PIC16F876 and i used timer1 with CCPx in Compare mode.

Code:
void init_pic (void)
{

	RBPU = 1; 		// Pull up désactivés
   
// Timer 0 (8 bits) utilisé pour le wachtdog a 2,3 s.
//	T0CS = 0 ;		// internal Clock
//	PSA = 1 ;			// timer0 = Wachtdog
//	PS2 = 1 ;			// \
//	PS1 = 1 ;			//  => 111 + WDT => 18 ms x 128 = 2,304 s 
//	PS0 = 1 ;			// /
//	T0IE = 1;

	ADCON1= 6 ;				// Passe le port A en mode digital
        TRISA = ConfigIOPortA ;	 	// Configuration du port A
        TRISB = ConfigIOPortB ;		// Configuration du port B 
	TRISC = 0 ;					// Configuration du port C 
   
        PORTA = 0x3F ;		// Toutes les sorties à 5V
        PORTB = 0xFF ;		// Toutes les entrées à 5V
	PORTC = 0 ;				// Toutes les sorties à 0V
	GIE = 1;				// Valide toutes les interruptions
	PEIE = 1 ;   

	CCP1IE = 1 ;				// Interruption en mode comparaison autorisée
	CCP2IE = 1 ;
	TMR1IE = 1 ;				// Interruption du Timer1 autorisée 

}

static void interrupt inter(void)		// Gestion du timer1 en mode compare

{
	if (TMR1IF)
		{
		RC6 = 1 ;
		RC7 = 1 ;
		TMR1H = 0xEC ;
		TMR1L = 0x77 ;
		TMR1IF = 0 ;
		}
	if (CCP1IF)
		{
		CCP1IF = 0 ;
		RC6 = 0 ;	
//		CCP1IF = 0 ;
		}
	if (CCP2IF)
		{
		CCP2IF = 0 ;
		RC7 = 0 ;
//		CCP2IF = 0 ;
		}	
}

void Init_compare (void)
{
	{
	TMR1H = 0xEC ; //on fixe la fréquence 65535-20000µs/4µs = 0xEC77
	TMR1L = 0x77 ;
	CCP1CON = 0b00001010 ;
	CCP2CON = 0b00001010 ;
	T1CON = 0b00100001 ; //Prescaler=1:4 Timer1=on
	}
}

void Config_rapport_cycliqueV(unsigned char)
{
	
	
		unsigned int Val1_tmp ;
		Val1_tmp = (139*Vpos)/100 ;
		Val1_tmp += 60785 ;

		CCPR1L = Val1_tmp ;
		CCPR1H = Val1_tmp >> 8 ;
	
	
}


void Config_rapport_cycliqueH(unsigned char)
{

	
		unsigned int Val2_tmp ;
		Val2_tmp = (139*Hpos)/100 ;
		Val2_tmp += 60785 ;

		CCPR2L = Val2_tmp ;
		CCPR2H = Val2_tmp >> 8 ;
	
	
}

The problem is when CCP1IE and CCP2IE occurs in the same time.
I have a jitter for this condition and maybe i'll must to created a special condition for this case.
I would like to know if anybody have a solution for my problem.
Thanks by advance.
Regards.
 
Perhaps we can offer suggestions if you explain what signals you're trying to generate?

When I use CCP "compare" mode I don't reset the Timer 1 registers but modify the CCPR1L:CCPR1H compare "match" registers instead.

Also, if you use the CCP "compare" mode options that automatically set or clear the CCP1 and CCP2 pins directly you shouldn't have any jitter unless your "match" values are very close to 00% or 100% of the Period.

Not sure if these suggestions apply for your particular application.

Regards, Mike
 
Hi Mike,

my signal is for drive 2 servo motorS, i have one for vertical axis and an other for horizontal axis.

So my first signal put RC6 to 1 for variable d.c and other put RC7 for the other condition.

With scope i see for the same d.c (1,5ms) no jitter, but when i move for example vertical axis condition only, i see the second signal who followed it just a short time and don't move after but keep this fault short time.
When the X signal come back, this phenomen appears again when they are the same d.c.
It's not really a jitter but an error produced when times are equal.
I think that is conditionning by interrupts who appears at the same time.
But i don't know how to correct this conflict.
If you have an idea.
Regards.
 
Ah, Servo output timing makes the task much more managable. Here's an example for a single channel output on the CCP1 pin;

Code:
/*                                                               *
 *  set Pulse to some value between 400 and 2400 (usecs)         *
 *                                                               *
 *  setup the TMR1 prescaler for 1.0-usec 'ticks'                *
 *                                                               */

unsigned int Pulse  =  1500;            // 1-usec TMR1 'ticks'
unsigned int Period = 20000;            // 1-usec TMR1 'ticks'

/*****************************************************************
 *  setup interrupt vectors                                      *
 *****************************************************************/
#pragma code high_interrupt = 0x08
void high_interrupt (void)
{ _asm goto isr_hi _endasm
}

#pragma code

/*****************************************************************
 *  ISR (high)                                                   *
 *****************************************************************/
#pragma interrupt isr_hi

void isr_hi ()
{ if (PIR1bits.CCP1IF == 1)             // CCP1 interrupt?
  { PIR1bits.CCP1IF = 0;                // yes, clear int flag
    if (CCP1CONbits.CCP1M0 == 0)        // CCP1 is hi, make it go
    { CCP1CONbits.CCP1M0 = 1;           // lo next match interrupt
      CCPR1H += (Pulse/256);            // setup Pulse on time
      CCPR1L += (Pulse%256);            // match/interrupt value
    }
    else                                // CCP1 is lo, make it go
    { CCP1CONbits.CCP1M0 = 0;           // hi next match interrupt
      CCPR1H += ((Period-Pulse)/256);   // setup Pulse off time
      CCPR1L += ((Period-Pulse)%256);   // match/interrupt value
    }
  }
}
This interrupt code provides "no jitter" output and there's plenty of time to service the interrupt and setup the next CCP "compare" register match variables.

Adding a second "no jitter" channel for the CCP2 output pin simply involves setting up another Pulse and Period variable pair and code to process the CCP2IF interrupt.

Good luck with your project. Regards, Mike
 
Hi Mike, thanks a lot for this code and for your interest, i look it and i'll be back for results.
Regards.
 
Hi Mike,

i have adapted your solution in my project, but i'not sure totally for timer1 (i don't write it? just prescaler 1:1) , so if you can look my code.
Thanks by advance.

Code:
#include	<pic.h>
#include 	"joystick.h"
#include	"ad16f87x.h"

__CONFIG(XT & WDTDIS & BORDIS & LVPDIS & UNPROTECT);

// Varibles declaration

unsigned int val ;
unsigned char Vpos ;
unsigned char Hpos ;
unsigned int Val1_tmp ;
unsigned int Val2_tmp ;
unsigned int Period ;
#define Period 20000

# define  Time  5
#define  Clign  10
#define time_led 10
#define time2 40


// prototypes functions

void init_pic (void);
void Delais_Milli (int val) ;
void PosLed (void) ;
void Test_Leds (void) ;
void Reset (void) ;
static void interrupt inter(void) ;
void Init_compare(void) ;
void Config_rapport_cycliqueV(unsigned char) ;
void Config_rapport_cycliqueH(unsigned char) ;


// PIC initialization

void init_pic (void)
{

    RBPU = 1; 		// Pull up off

    ADCON1= 6 ;				// port A digital
    TRISA = ConfigIOPortA ;	 	// Config port A
    TRISB = ConfigIOPortB ;		// Config port B
    TRISC = 0 ;				// Config port C
   
    PORTA = 0x3F ;		// output to 5V
    PORTB = 0xFF ;		// input to 5V
    PORTC = 0 ;			// output to 0V
    GIE = 1;			// global interrupt enable
    PEIE = 1 ;
    CCP1CON = 0b00001010 ;  // compare mode CCP1
    CCP2CON = 0b00001010 ;  // compare mode CCP2
    T1CON = 0b00000001 ;    //Prescaler=1:1 Timer1=on

    CCP1IE = 1 ;	    // Interupt mode compare CCPx enable
    CCP2IE = 1 ;
    TMR1IE = 1 ;	    // Interupt Timer1 enable

}


main()
    {
    Val1_tmp = 0 ;   // variables init
    Val2_tmp = 0 ;
    init_pic() ;

    Vpos = 90 ;				// position centrale servo Vertical
    Hpos = 90 ;				// position centrale servo Horizontal
    Config_rapport_cycliqueV (Vpos) ;
    Config_rapport_cycliqueH (Hpos) ;
	
    Test_Leds() ; 				// Test des leds au démarrage

    while(1) 					
          { ..............
		

	.............
          } 

static void interrupt inter(void)

{

	if (CCP1IF)
		{
		CCP1IF = 0;

			if (CCP1M0 == 0)
				{
				RC6 = 1 ;
				CCP1M0 = 1 ;
				CCPR1H += (Val1_tmp / 256) ;
				CCPR1L += (Val1_tmp % 256) ;
				}
			else
				{
				RC6 = 1 ;
				CCP1M0 = 0 ;
				CCPR1H += ((Period - Val1_tmp) / 256) ;
				CCPR1L += ((Period - Val1_tmp) % 256) ;
				}
		}

	if (CCP2IF)
		{
		CCP2IF = 0;
                	if (CCP1M0 == 0)
				{
				RC7 = 1 ;
				CCP1M0 = 1 ;
				CCPR2H += (Val2_tmp / 256) ;
				CCPR2L += (Val2_tmp % 256) ;
				}
			else
				{
				RC7 = 0 ;
				CCP1M0 = 0 ;
				CCPR2H += ((Period - Val2_tmp) / 256) ;
				CCPR2L += ((Period - Val2_tmp) % 256) ;
				}

		}


}
 

void Test_Leds (void)

{  .......
..........		
}

void Reset (void)  // Reset servos positions
{
...........	
}

void PosLed(void)   
{
...........
}


void Delais_Milli (int val)

{

static int val1 = 0 ;
static int val2 = 0 ;

	for (val2=0 ; val2 < val ; val2 ++)

		{
  			for (val1=0 ; val1 < 200 ; val1++) ;
		}
}


void Config_rapport_cycliqueV(unsigned char)
{
	
		unsigned int Val1_tmp ;
		Val1_tmp = (555*Vpos)/100 ;
                         	Val1_tmp += 1000 ;

// if Vpos=0 => Val1_tmp=1000/ if Vpos=180 => Val1_tmp= 2000
	
	
}


void Config_rapport_cycliqueH(unsigned char)
{
		unsigned int Val2_tmp ;
		Val2_tmp = (555*Hpos)/100 ;
		Val2_tmp += 1000 ;
}
 
I think you're on the right track.

Do you know what the CCP1MO bit does?
Do you think there may be a corresponding bit for the CCP2 module?

My example used 1.0-usec Timer 1 'ticks'. Have you setup your Timer 1 accordingly? Also, what is the range of values and resolution for your Val1_tmp and Val2_tmp variables?

Regards, Mike
 
Hi Mike,

yes timer1 is well according (1µs).
The problem appears when CCP1M0=0 because TMR1 is reset automatically when match, so CCPR2 too.
I would realize an offset between two signals but i don't know how...
It's more difficult that expected... :(
 
Timer 1 doesn't get reset in CCP "compare" mode. It's free running.

During the current interrupt you setup the CCPRn "compare" registers for the next 'match' interrupt.

The CCP1M0 and CCP2M0 registers are setup during their respective interrupt to tell the CCP module which way to toggle the CCP1 and CCP2 pins at the next 'match' interrupt.

You also did not mention the range of values expected in the Val1_tmp and Val2_tmp variables. Is it some value between 500 and 2500 (usecs)?
 
Last edited:
Hi Mike,

here is my complete code with interrupts use:

Code:
#include	<pic.h>
#include 	"joystick.h"
#include	"ad16f87x.h"

__CONFIG(XT & WDTDIS & BORDIS & LVPDIS & UNPROTECT);

unsigned int val ;
unsigned char Vpos ;
unsigned char Hpos ;

unsigned int PulseV ;
unsigned int PulseH ;

unsigned char fact ;

 
#define Time 10
#define time2 5

#define periode 21500

void init_pic (void) ;
void init_var (void) ;
void Delais_Milli (int val) ;
void PosLed (void) ;
static void interrupt inter(void) ;
void Config_rapport_cycliqueV(unsigned char) ;
void Config_rapport_cycliqueH(unsigned char) ;


void init_pic (void)
{

	RBPU = 1; 		// Pull up désactivés   

	ADCON1= 6 ;				// Passe le port A en mode digital
    TRISA = ConfigIOPortA ;	 	// Configuration du port A
    TRISB = ConfigIOPortB ;		// Configuration du port B 
	TRISC = 0 ;					// Configuration du port C 
   
    PORTA = 0x3F ;		// Toutes les sorties à 5V
    PORTB = 0xFF ;		// Toutes les entrées à 5V
	PORTC = 0 ;				// Toutes les sorties à 0V
	GIE = 1;				// Valide toutes les interruptions
	PEIE = 1 ; 
	CCP1CON = 0b00001011 ;
	CCP2CON = 0b00001011 ;

	T1CON = 0b00000001 ; //Prescaler=1:1 Timer1=on 

	CCP1IE = 1 ;				// Interruption en mode comparaison autorisée
	CCP2IE = 1 ;
	TMR1IE = 1 ;				// Interruption du Timer1 activée 

}

void init_var() 

	{
	fact = 5 ;
	Vpos = 100 ;
	Hpos = 100 ;
	}


main()
{

   	init_pic() ;
	init_var() ;	

	Config_rapport_cycliqueV (Vpos) ;
	Config_rapport_cycliqueH (Hpos) ;
   
    while(1) 					// Le programme tourne dans cette boucle principale 
            {	    

			while ((JOY12H == 0) && (Vpos < 200))  // Gestion des positions en fonctions des actions sur le joystick
				{
					
					Delais_Milli(time2) ;					
					Vpos = Vpos++ ;
					Config_rapport_cycliqueV(Vpos) ;
					Delais_Milli(Time) ;
					PosLed() ;				

				}

			while ((JOY18H == 0) && (Vpos > 0))
				{
					
					Delais_Milli(time2) ;
					Vpos = Vpos-- ;
					Config_rapport_cycliqueV(Vpos) ;
					Delais_Milli(Time) ;
					PosLed() ;
					
				}

			while ((JOY15H == 0) && (Hpos < 200))  // Gestion des positions en fonctions des actions sur le joystick
				{
					
					Delais_Milli(time2) ;					
					Hpos = Hpos++ ;
					Config_rapport_cycliqueH(Hpos) ;
					Delais_Milli(Time) ;
					PosLed() ;				

				}

			while ((JOY21H == 0) && (Hpos > 0))
				{
					
					Delais_Milli(time2) ;
					Hpos = Hpos-- ;
					Config_rapport_cycliqueH(Hpos) ;
					Delais_Milli(Time) ;
					PosLed() ;
					
				}

            }
} 

static void interrupt inter(void)		// Gestion du timer1 en mode compare

{

	if (CCP1IF)

			{
			
			CCP1IF = 0 ;

				if (CCP1M0 == 1)
					{
					CCP1M0 = 0 ;
					
					RC6 = 1 ;
					
   					
					CCPR1H = (PulseV / 256) ;					
					CCPR1L = (PulseV % 256) ;									
									
					}

				else 
					{
					CCP1M0 == 1 ;
					
					RC6 = 0 ;
   					
					CCPR1H = ((periode - PulseV) / 256) ;					
					CCPR1L = ((periode - PulseV) % 256) ;							
										
					}				

			}


	if (CCP2IF)

			{
			
			CCP2IF = 0 ;

				
				if (CCP2M0 == 1)
					{
					CCP2M0 = 0 ;
					
					RC7 = 1 ;
					
   					
					CCPR2H = (PulseH / 256) ;					
					CCPR2L = (PulseH % 256) ;									
									
					}

				else 
					{
					CCP2M0 == 1 ;
					
					RC7 = 0 ;
   					
					CCPR2H = ((periode - PulseH) / 256) ;					
					CCPR2L = ((periode - PulseH) % 256) ;							
										
					}				

			}			


}


void Config_rapport_cycliqueV(unsigned char)

{
	
		PulseV = (fact * Vpos) ;	//Valeurs adaptées à la maquette et au servo
		
		PulseV += 1000 ; 		
					
	
}

void Config_rapport_cycliqueH(unsigned char)

{
	
		PulseH = (fact * Hpos) ;	//Valeurs adaptées à la maquette et au servo
		
		PulseH += 1000 ; 		
					
	
}


void PosLed(void)   // Fonction de gestion des leds de direction

{

	if (Vpos > 100)
		{
		LED_BAS = 1 ;
		LED_HAUT = 0 ;	
		}
	

	if (Vpos < 80)
		{
		LED_HAUT = 1 ;
		LED_BAS = 0 ;
		}	
	
	if (Hpos > 100)
		{
		LED_GAUCHE = 1 ;
		LED_DROITE = 0 ;	
		}
	

	if (Hpos < 80)
		{
		LED_GAUCHE = 0 ;
		LED_DROITE = 1 ;
		}

}

void Delais_Milli (int val)

{

static int val1 = 0 ;
static int val2 = 0 ;

	for (val2=0 ; val2 < val ; val2 ++)

		{
			for (val1=0 ; val1 < 200 ; val1++) ;			
		}

}

With this i have nothing in RC6 and RC7...
 
It seems you're configuring the CCP1 and CCP2 modules for "compare mode, special event trigger". You should be initializing the CCP1CON and CCP2CON registers to 0b00001000 which is "compare mode, set output on match".

Please note that you do not need to service a Timer 1 interrupt as Timer 1 is free running.

You still have not mentioned the range of values for the PulseH and PulseV variables.

Regards, Mike
 
Last edited:
Hi Mike, thanks for your patience.

Yes i seen and put TMR1IF=0 since my last post.
PulseV and PulseH are calculated by:

Code:
 void Config_rapport_cycliqueV(unsigned char)

{
	
		PulseV = (fact * Vpos) ;	//Valeurs adaptées à la maquette et au servo
		
		PulseV += 1000 ; 		
					
	
}

When Vpos++ or Vpos--

So PulseV is 2000 max and 1000min.
same for PulseH.
 
POWERMAN said:
"Set output on match" means that i must used RC1 and RC2?
Yes, that's correct. "Set output on match" uses the RC2/CCP1 and the RC1/CCP2 pins.

If using the CCP1 and CCP2 pins is a problem then you might consider using a single CCP module in "compare mode, generate software interrupt on match" instead and go back to setting and clearing output pins manually in your interrupt service routine. This method can be used to provide up to 8 Servo outputs sequentially in a 20.0-msec period and still provides very low processor 'overhead' in the ISR but could result in one or two cycle pulse width 'jitter' for each output. You also should not disable interrupts with this version. Here's an example (8 channels);

Code:
static unsigned char n = 0;
static unsigned int  Qarray [] = { 1500, 1500, 1500, 1500,
                                   1500, 1500, 1500, 1500, 20000 };
static unsigned char Servo = 1;   // Port B shadow register

/*****************************************************************
 *  K8LH Crazy-8 'Soft' 8-channel (Port B) Servo Algorithm       *
 *                                                               */
void isr_hi ()
{ if (PIR1bits.CCP1IF == 1)     // if CCP "compare" interrupt
  { 
    LATB = Servo;               // output new Servo pulse

    CCPR1H += (Qarray[n]/256);  // update "match" period value
    CCPR1L += (Qarray[n]%256);  //  for this Servo pulse

    PIR1bits.CCP1IF = 0;        // clear CCP1 interrupt flag

    Qarray[8] -= Qarray[n];     // adjust end-of-period time
    Servo <<= 1;                // prep for next channel
    n++;                        // increment array index

    if (n =  9)                 // if end-of-cycle
    { n = 0;                    // reset array index
      Qarray[8] = 20000;        // reset the 20.0-msec period
      Servo = 1;                // reset the Servo to Servo 1
    }
  }
}
 
Last edited:
Ouupss!

it's more difficult for me to translate in my code.

For only 2 servos can you help me to transposed your code in my code?

Regards.
 
The 'easy' fix would be to simply use the Qarray[6] and Qarray[7] elements for Pulse "on-time" and do a "logical AND" for RB6 and RB7 when setting Port B.

Code:
    LATB = (Servo & 0b11000000);
Regards, Mike
 
Ok Mike i try it as soon as possible.
Thanks for your advices.

I'm habituated with 8051 family and i'm dissapointed with timer interrupt management for Microchip 16Fx controller....
CCPxCON is not an intelligent system because it can't make a prior about CCP1IF and CCP2IF when they appear in same time.
For 16F87X when this case appears you have only one of the 2 cases, the first win and the next......is out definitively.... incredible!
I have try all case for CCP1Mx, neither of them can generate 2 PWM in 2 output for same frequency and same timings.
I think it's a really handicap for applications timings.

Regards.
 
Hi Mike,

I have follow your advices and now it's functional with only CCP1 in trigger events!
I load my differents times in CCPR1H and CCPR1L, and i managed CCP1IF.

It's all right.

Thanks for all again.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top