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.

Measuring Pulse Width via CCP

Status
Not open for further replies.

dkw

New Member
Below was compiled in MikroC for measuring pulse width using CCP1 capture. Using a 16F690 with a 20MHz crystal. Trying to capture alternating edges. For now, just trying to UART out the value. The code compiles ok but I'm not getting data output. Any help on this would be greatly appreciated. Thanks!


Code:
#include "built_in.h"

unsigned int T1;                                              //1st capture edge (High)
unsigned int T2;                                               //2nd capture edge (Low)
unsigned const numerator = 170454.55;         //Distance = 3" .....(May chng if prescale chngs)
unsigned int MPH;

void hardwareInit(){                                         // Configure Hardware

    TRISC.RC5 = 1;                                            // CCP1 pin - RC5 set to input
    ANSEL = 0;                                                  // Set pins to digital
    ANSELH = 0;                                               //  ""
    ADCON0 = 0;                                              // Turn off comparitors
    ADCON1 = 0;                                             //  ""
    INTCON = 0;                                              // Turn off interrupts..
    T1CON.CCP1IE = 1;                                  // ...Except for CCP1
    T1CON = 0b00000011;                            // External clock, Turn on timer 
    CCP1CON = 0x05;                                   // Capture rising edge (101)
}
void main(){
    hardwareInit();
    while(1){}
}
void writedata(){

    UART1_Init(4800);
    Delay_ms(100);
    MPH = (numerator/(T2-T1));                  //Calculation - based on 3" distance
    UART1_Write_Text("Start");                 //Just a test to see if there is output
    UART1_Write(10);                                //From MikroC library
    UART1_Write(13);                               //   ""

  while(1) {                                              // Endless loop
    if (UART1_Data_Ready()) {                  // If data is received,
      MPH = UART1_Read();                       // read the received data,
      UART1_Write(MPH);                          // and send data via UART
    }
}
}
void interrupt() {

    if (CCP1CON.CCP1IF){               // Flag for 1st CCP interrupt
    hi(T1) = CCPR1H;                      //Put T1 (hi) in register
    lo(T1) = CCPR1L;                      //Put T1 (lo) in register
    CCP1CON.CCP1IE = 0;            // Clear bit per datasheet
    PIR1.CCP1IF = 0;                    // Reset flag
    CCP1CON.CCP1IE = 1;           // Renable CCP interrupt
    }
    if (CCP1CON.CCP1IF){           // Flag for 2nd capture (low)
    hi(T2) = CCPR1H;                  //Put T2 in register
    lo(T2) = CCPR1L;                  //       "
    
    writedata();
    hardwareInit();
    }
}
 
Last edited by a moderator:
PLEASE use the (code) and (/code) tags for your code (they need SQUARE brackets) . You can edit your post and add the tags. :)

When you say "I'm not getting data output" please be more specific in describing the problem. It looks like you are using the compiler's inbuilt functions to send text and data out the UART, so are you saying you have a problem where the serial output is not working?
 
Not sure about this, but don't you need the GIE bit & PEIE bit set in INTCON ???

Loooooong time since I did capture ..

D.
 
I don't quite understand why you have an infinite loop in "writedata()" -function and then you call that function from the interrupt. The result is that you never leave the interrupt function. If nested interrupts are allowed, you run out of (stack) memory fast as the interrupts are entered but never returned. If nested interrupts are not allowed, then your code practically halts after one interrupt.

I think you better start from scratch step by step. First try to get the capture working. You can blink a LED in the interrupt so you know that the interrupts work. Use a push-button to generate rising and falling edges at your command. Then create a completely new project only to get the UART working. After this you can combine the two codes to send captured data through uart.
 
Last edited:
Thanks all for the advice - very helpful. I started over as suggested and was able to get interrupts working and UART. Working on getting it all put together. I will post updated code when complete.
 
Ok. Able to get everything working (code below). Getting the interrupt on the high edge, then switches to low edge. UART also working. I'm sure there's a lot of improvements that can be made but it's a start (seem to be getting many of the same values when testing). Just a few follow-up questions:

1) I don't have an oscilloscope. Just started w/ Oshonsoft simulator. Will the later suffice (or a different simulator?) or do I really need to get an oscilloscope? Trying to better understand what is happening when etc.

2) I need to substitute an LCD for the UART. Have an i2c LCD which I have tried very briefly w/ no luck. Is there a "best" / "easiest" interface to use ie i2c, SPI etc.

Thanks again for all the help. Never would have gotten this far without it!

Code:
//Measuring Pulse Width in MPH via CCP & Alternate Edge Capture

unsigned long t1;                            //1st capture edge (High)
unsigned long t2;                            //2nd capture edge (Low)
unsigned short int stringLength;
unsigned short int i;
char periodData[] = "orig value";
unsigned const numerator = 170455;           //Distance = 3". Using 16MHz w/ 1:4 Prescale - 1 pulse per uS
unsigned char MPH;

void writeString(char periodData[])           //Output to UART
{
     // Get string length
     stringLength = strlen(periodData);
     for(i = 0; i<stringLength; i++)
     {
              UART1_Write(periodData[i]);
     }
      UART1_Write(0x0D);
      UART1_Write(0x0A);
}
void hardwareInit(){

    TRISC.RC5 = 1;                              // CCP1 pin - RC5 set to input
    ANSEL = 0;                                  // Set pins to digital
    ANSELH = 0;                                         //  ""
    ADCON0 = 0;                                 // Turn off comparitors
    ADCON1 = 0;                                        //  ""
    INTCON = 0;                                 // Turn off interrupts...
    PIE1 = 0b00000100;                          // ...Except for CCP1
    INTCON = 0b11000000;                        // ...And global & peripheral interrupts
    T1CON = 0b00100001;                         // Internal clock, Turn on timer
    CCP1CON = 0x05;                             // Capture rising edge (101)
    PIR1 = 0b00000000;                          // Clear all interrupt flags
}

void interrupt() {    if(PIR1.CCP1IF==1){       // Flag for 1st interrupt
     t1 = TMR1H;                                // Assign timer value of 1st capture
     t1 = t1 <<8;
     t1 = t1 | TMR1L;

    PIE1.CCP1IE = 0;                            // Clear bit per datasheet
    PIR1.CCP1IF = 0;                            // Reset flag
    CCP1CON = 0x04;                             // Switch edge to falling
    PIE1.CCP1IE = 1;                            // Renable CCP interrupt
    }
    if(PIR1.CCP1IF==1){                         // Flag for 2nd capture (low)

    t2 = TMR1H;                                 // Store values for 2nd capture
    t2 = t2 <<8;
    t2 = t2 | TMR1L;
    MPH = (numerator/(t2-t1));                  // MPH calculation
    LongToStr(MPH, periodData);                 // Output
    writeString(periodData);
    PIE1.CCP1IE = 0;                            // Clear bit per datasheet
    PIR1.CCP1IF = 0;                            // Reset flag
    CCP1CON = 0x05;                             // Switch edge to rising
    PIE1.CCP1IE = 1;                            // Reset CCP interrupt
    }
}
void main(){
    hardwareInit();
    UART1_Init(4800);
    Delay_ms(25);
    MPH = (numerator/(t2-t1));                  //Calculation - based on 3" distance
    while(1){}
}
 
Do I read your code right that you are capturing the period between a rising edge and a falling edge? It will be better to capture the period between two rising edges, as that will negate any errors caused by duty cycle, or because duty cycle might change with speed.
 
Correct. That is my preference due to physical design. If it doesn't work I can go to two same edges but it means compromises elsewhere and design changes. What is the best way to evaluate the magnitude and frequency of duty cycle errors and is there a way to minimize them? or can this be calculated from the datasheet for a given speed?

Thanks!
 
The problem with duty cycle errors is they can give a non-linear speed reading. Imagine if there is a turn-off delay on the \ edge, common with optodetectors which turn on fast, but turn off slowly.

At slow speed if the HI period is 50mS and the turn off delay is 10uS the measured HI period might be 49.99mS.
At high speed if the HI period is 50uS and turn off delay is 10uS the measured Hi period is 40uS.

Obviously that's an exaggerated example to highlight the problem but the problem exists. If it's a magnetic sensor the problem might be bad too due tot he way the mag field might be affected by speed.

Either way there's no need to deal with it, all you do is time the period between two / edges, that will always represent the true period and true speed. If you need to get a speed reading on every revolution that's still possible.
 
1) I don't have an oscilloscope. Just started w/ Oshonsoft simulator. Will the later suffice (or a different simulator?) or do I really need to get an oscilloscope? Trying to better understand what is happening when etc.

If you are going to keep working with microcontrollers you could consider buying a logic analyzer (I have this one: ). You can easily debug UART, I2C, SPI etc. communications with it and also measure frequencies and duty cycles of digital signals. Of course an oscilloscope that also has logic analyzer would be a good investment, but if you are on a budget, buy the logic analyzer first.
 
Thanks for the replies - very helpful. For now, I'm not using the sensors, just a bare wire to a resistor.

My problem is I'm getting the same values over and over (either 23 or 98, sometimes 148). I've tried changing the code to just do rising edge and I get the same values. I've changed the variables to output t1 and t2 (vs. MPH) and those seem to be working - seem to be getting different timer values on each touch. Obviously, something is wrong in the code but I can't seem to figure it out. Any help/insight on this would be greatly appreciated.

Thanks!
 
hey im new to the PIC stuff so im sure this is not the most elegant solution.. but the way the interrupts were set up i think it was only hitting the first part with t1, i added an AND condition to the if(...) command using the LATC.B0 parts to switch back and forth between which interrupts (t1 or t2) get hit... now it successfully calculates the time (within 10 or so uS of what my scope says)

im doing a project where i use RC constant to calculate resistance digitally so ignore the end part of the code.. i switched CCP pin and im also starting capture on falling edge and stopping on rising so you have to switch that back

thanks, even though it required a little modification it saved me a lot of time, hope this helps

Code:
unsigned long t1;                            //1st capture edge (High)
unsigned long t2;                            //2nd capture edge (Low)
unsigned long value;                         //Using 16MHz w/ 1:4 Prescale - 1 pulse per uS
unsigned short int stringLength;
unsigned short int i;

void init() {
     OSCCON.IRCF2 = 1;           // Internal Oscillator Frequency 16Mhz
     OSCCON.IRCF1 = 1;
     OSCCON.IRCF0 = 1;
     OSCTUNE.PLLEN = 0;          // 4x PLL to 64MHz (OFF)
     CM1CON0 = 0b11111100;       // C1 on, inverted, - input tied to CxVREF
     CM2CON1.C1RSEL = 1;         // FVR BUF routed to C1Vref input
     VREFCON0 = 0b11100000;      // Comparator ref voltage 2.048V
     ANSELA = 0b00000001;        // Configure RA0 analog
     ANSELB = 0b00000000;        // Configure PORTB pins as digital
     ANSELC = 0b00000000;        // Configure PORTC pins as digital
     ANSELD = 0b00000000;        // Configure PORTD pins as digital
     ANSELE = 0b00000000;        // Configure PORTE pins as digital
     TRISA = 0b00000001;         // Set RA0 input.
     TRISB = 0b00000000;         // Set PORTB pins as output
     TRISC = 1;                  // Set PORTC pins as output
     TRISD = 0b00000000;         // Set RD3, RD2, RD1, RD0 pins as input
     TRISE = 0b00000000;         // Set PORTE pins as output
     LATA = 0b00000000;          // Drive PORTA outputs low
     LATB = 0b00000000;          // Drive PORTB outputs low
     LATD = 0b00000000;          // Drive PORTD outputs low
     LATE = 0b00000000;          // Drive PORTE outputs low
     }

void hardwareInit(){
    TRISC.RC2 = 1;                              // CCP1 pin - RC2 set to input
    INTCON = 0;                                 // Turn off interrupts...
    PIE1 = 0b00000100;                          // ...Except for CCP1
    INTCON = 0b11000000;                        // ...And global & peripheral interrupts
    T1CON = 0b00100001;                         // Internal clock, Turn on timer
    CCP1CON = 0x04;                             // Capture falling edge
    PIR1 = 0b00000000;                          // Clear all interrupt flags
}

void interrupt() { if(PIR1.CCP1IF==1 && LATC.B0 == 0){       // Flag for 1st interrupt
     t1 = TMR1H;                                // Assign timer value of 1st capture
     t1 = t1 <<8;
     t1 = t1 | TMR1L;

    PIE1.CCP1IE = 0;                            // Clear bit per datasheet
    PIR1.CCP1IF = 0;                            // Reset flag
    CCP1CON = 0x05;                             // Switch edge to rising
    PIE1.CCP1IE = 1;                            // Renable CCP interrupt
    LATC.B0 = 1;                                // Grab rising edge next time
    }
    if(PIR1.CCP1IF==1 && LATC.B0 == 1){         // Flag for 2nd capture (low)
    t2 = TMR1H;                                 // Store values for 2nd capture
    t2 = t2 <<8;
    t2 = t2 | TMR1L;
    value = t2-t1;                              // time calculation
    PIE1.CCP1IE = 0;                            // Clear bit per datasheet
    PIR1.CCP1IF = 0;                            // Reset flag
    CCP1CON = 0x04;                             // Switch edge to falling
    PIE1.CCP1IE = 1;                            // Reset CCP interrupt
    LATC.B0 = 0;                                // Grab falling edge next time
    }
}

void main() {
init();
hardwareInit();

do {
TRISA = 0b00000001;         // Set RA3 out.
LATA.B3 = 0;
Delay_ms(1);
TRISA = 0b00000101;         // Set RA3 in.
Delay_ms(10);

} while(1);

}
 
Status
Not open for further replies.

Latest threads

Back
Top