This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Timer Capture on external pin on both edges

Hello,

I'm using MSP430F22xx controller.

I need to detect both edges on timer capture external signal and I found it problematic.

I've got CCR0 in capture mode on both edges of the signal.

I search for the method of detecting which edge has been captured: rising or falling. I found only method with sampling pin state in interrupt:

#pragma vector=TIMER0_A0_VECTOR
__interrupt void sensorTimer0Impulse(void)
{
    if ((TA0CCTL0 & CCI)== CCI)      //we have rising edge interrupt
    {

But unfortunately this method is not 100% faultless.

Do you know any other approach to this task?

Best regards

Krzysztof Grabinski

  • Two connected together capture I/O pins, two capture registers - one for rising and another for falling edge?

  • Read the Port state of the captured pin in your Timer-ISR; (PxIN & BITx)

  • Krzysztof Grabinski said:
    But unfortunately this method is not 100% faultless.

    Well, it is (sort of). When CCI is set, you have a rising edge interrupt and CCR0 contains the capture for it.. However, it might be the rising edge after the falling edge that originally caused the interrupt, and the capture form this other interrupt is lost. :)
    And even if not, after checking CCI but before reading CCR0 (and/or resetting CCIFG), there might be another edge, so you either read the wrong capture or clear the next edge interrupt.

    Using two pins for different edges ensures that one edge won't overwrite the previous. You still need to ensure that interrupt execution is faster than two subsequent identical edges.

  • I remember I had in the past similar problems with the CCI bit and change it to reading the Port, this works 200%.

  • Jens-Michael Gross said:

    But unfortunately this method is not 100% faultless.

    Well, it is (sort of). When CCI is set, you have a rising edge interrupt and CCR0 contains the capture for it.. However, it might be the rising edge after the falling edge that originally caused the interrupt, and the capture form this other interrupt is lost. :)
    And even if not, after checking CCI but before reading CCR0 (and/or resetting CCIFG), there might be another edge, so you either read the wrong capture or clear the next edge interrupt.

    [/quote]

    Perhaps COV bit will tell if that is happening.

  • Thanks for all responses.

    To summarize the topic:

    If we want to detect both edges the best way is to connect together capture I/O pins and use two capture registers.

    If we don't have two CCR or we can't make changes in schematics we have software solutions:

    1. Check the PxIN & BITx
    2. Check the CCI bit and COV bit
    3. Switching capture register edge detection: rising or falling

    1. Not secure approach: If distance between two edges will be very small (eg. 1us) we could have situation that before we check the state of the pin in first interrupt, second edge will be detected. Reading the pin state will give wrong edge detection.

    2. Used in proper order can give us correct edge detection:

    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void sensorTimer0Impulse(void)
    {
        uint8_t RisingEdgeDetected;
        uint8_t SomeVariable;
    
        RisingEdgeDetected = (TA0CCTL0 & CCI);
    
        if((TA0CCTL0 & COV) == COV) 
        {
            if(RisingEdgeDetected == (TA0CCTL0 & CCI))//capture overflow appeared before our first edge test
            {
               RisingEdgeDetected = RisingEdgeDetected ^ CCI;
            }
        }
    
        RisingEdgeDetected = (RisingEdgeDetected >> 3u);
    
        if (RisingEdgeDetected == TRUE)      //we have rising edge interrupt
       {
        ...
       }
    
           SomeVariable = TA0CCR0; // this read is needed to re-enable COV bit detection
           TA0CCTL0 &= ~COV;   //clear capture overflow
    }

    3. Check the CCI bit before the change is coming and if it´s high set the edge control to falling edge, while if its low set the edge control to rising edge. After setting the edge you should read CCI once again to make sure that did not change while doing the setup and the edge control is still valid. Doing a control on CCI before the edge appears, would let you enable the right one that would happen.

    I choose 2'nd solution and it work fine for me. I'm wondering what is happening if we already have capture overflow in interrupt routine (COV ==1) and 3'rd interrupt will came. Will this 3'rd interrupt be handled after 1'st and 2'nd interrupt routines will finish?

  • Krzysztof Grabinski said:
    2. Used in proper order can give us correct edge detection:

    Unfortunately not. Between checking the CCI and COV bits and reading CCR0, another edge may happen, updating the capture value.

    You need to read CCR0, then CCTL into a variable, then read CCR0 again to ensure it hasn't changed.

    int control= CCTL0;
    int capture = CCR0;
    char overflow= control & COV;
    while (capture !=CCR0) {
      control=CCTL0;
      overflow |= COV; // if this while is executed, an overflow has happened.
      capture = CCR0;
    }

    After this loop, you know that the copy of CCTL0 belongs to the copy of CCR0 and you can proceed analyzing the data. Also, any overflow is noticed, even though COV might have been cleared by reading CCR0.

    Krzysztof Grabinski said:
    Check the CCI bit before the change is coming and if it´s high set the edge control to falling edge, while if its low set the edge control to rising edge.

    A racing condition. What if the edge comes between reading CCI but before setting the edge interrupt?

  • For my situation this solution works but you show me that I'm not fully understand COV behavior. I was looking into older post but I haven't found clear description what is going on while second capture is detected before first one was handled. Correct me if I'm wrong:

    Rising edge detected
    TAR of rising edge is copied into CCR0
    CCIFG is set in TACCTL
    1'st capture interrupt handler start (rising edge)
    CCIFG is reset in TACCTL

               Falling edge detected in background
               TAR of falling edge is copied into CCR0   <-   is this true ??
               COV bit is set in TACCTL because we didn't read CCR0 for rising edge
               CCIFG is set in TACCTL

     SomeVariable = CCR0;  // we copy the TAR from falling edge instead of rising edge
     TACCTL &= ~COV;          //If we don't clear COV bit it will remain in high state -> this line is only needed when we use COV bit in our analysis
    1'st capture interrupt handler end

    2'nd capture interrupt handler start (falling edge)
    CCIFG is reset in TACCTL

    SomeVariable = CCR0;  // we copy the TAR from falling edge

    2'nd capture interrupt handler end

    So in this situation CCR0 will have the same value in SomeVariable in both interrupt handlers.

    2'nd situation:

    Rising edge detected
    TAR of rising edge is copied into CCR0
    CCIFG is set in TACCTL
    1'st capture interrupt handler start (rising edge)

     SomeVariable = CCR0; // we copy the TAR from falling edge instead of rising edge

               Falling edge detected in background
               TAR of falling edge is copied into CCR0   <-   is this true ??
               COV bit is not set in TACCTL because we read CCR0 for rising edge

     1'st capture interrupt handler end

    2'nd capture interrupt handler start (falling edge)

    SomeVariable = CCR0;  // we copy the TAR from falling edge

    2'nd capture interrupt handler end

    So we will have proper SomeVariable content in both interrupts.

    3'rd situation

    Rising edge detected ...
    1'st capture interrupt handler start (rising edge)

         Falling edge detected in background (COV = 1 and CCIFG is set in TACCTL)
         Next rising edge detected in background (COV  and CCIFG is still = 1)

    ...
    1'st capture interrupt handler end

    2'nd capture interrupt handler start (falling edge)
    CCIFG is reset in TACCTL
    ...
    2'nd capture interrupt handler end (falling edge)

    3'rd capture interrupt handler start (risign edge)     <--- is this 3rd interrupt will be executed?? How long is the queue of the same interrupt vector. I think that we have only one place in queue as we have one CCIFG flag for one interrupt vector
    ...                                                                                             
    3'rd capture interrupt handler end (falling edge)             so the 3'rd interrupt will be not called.


    So we will have proper SomeVariable content in both interrupts.

    I'm aware that interrupt handlers should be as short as possible and that there need to have enough time to execute before next signal will call them.
    In my application my measured signal is max 1kHz with duty cycle of 50% but I need too secure against very short spikes that will came very rarely.

    If you know where can I read more detailed description of timers please let me know.

    Thanks
    Krzysiek

  • Just curious - why don't you think about using two CCR inputs? Not enough CCR registers or input pins?

  • I have 5 CCR in my uC and I use all of them already.

    No more pins available on uC.

    But even if I have this possibility of using two captures I would like to know exactly how timer is working ;]

  • Your first situation description is correct. Except for one minor thing:

    Krzysztof Grabinski said:
    TACCTL &= ~COV;          //If we don't clear COV bit it will remain in high state -> this line is only needed when we use COV bit in our analysis

    Actually, this line is always required. If COV is set due to an overflow, i tis not reset by reading the CCR value. But also, no further captures will happen. At least that's what the flowchart in the users guide tells. If a capture was taken and before reading CCRx, another capture happens, COV is set and the state machine enters a state where the only exit is clearing the COV bit.

    Second situation description is also correct, except for

    Krzysztof Grabinski said:
     SomeVariable = CCR0; // we copy the TAR from falling edge instead of rising edge
    Here the comment is of course wrong, we do read the rising edge capture value. The falling edge didn't happen yet.

    Third situation: no, the third interrupt is not executed. You did reset CCIFG after the 3rd edge happened, so you cleared the 3rd edge IFG bit, not the 2nd edge IFG bit (well, it's the same physical bit anyway). There is only one IFG bit, It can be set or reset. As long as it is set, interrupts happen. Interrupts are not triggered by the event of setting the IFG bit, but rather by the fact that it is set. If you don't reset it in your ISR, infinite interrupts will happen. (exception: the CCR0 interrupt, which auto-clears CCR0.CCIFG on ISR entry)

    However, even if you would clear the CCIFG bit before the third edge happens, it won't be set by the third edge and CCR won't be updated, as long as COV bit is still set.

    The problem with capturing both edges in the same ISR is that if you ever miss one edge, you'll have a phase switch in your analysis (unless you detect the missing edge by also analyzing the current state of the line - before it changes again. Lots of racing conditions here, and each software check is subject to a new racing condition itself.
    That's why two separate capture units are preferable.

    Krzysztof Grabinski said:
    If you know where can I read more detailed description of timers please let me know.

    Main source of information is the users guide. In addition to the one of your MSP, you might want to consult the 3x family users guide. While the 3x devices are obsolete and discontinued, their timer module is the same as in newer families, and the description of the timer module in the users guide contain much more details than the later versions.

  • Krzysztof Grabinski said:

    In my application my measured signal is max 1kHz with duty cycle of 50% but I need too secure against very short spikes that will came very rarely.

    The SCS bit is used to synchronize the capture input signal with the timer clock. Perhaps setting that bit will prevent interrupts from spikes that occur when the timer clock is not making its active transition thus increasing the reliability of whatever algorithm you use.

  • Jens-Michael Gross said:
    Your first situation description is correct. Except for one minor thing:
    TACCTL &= ~COV;          //If we don't clear COV bit it will remain in high state -> this line is only needed when we use COV bit in our analysis


    Actually, this line is always required. If COV is set due to an overflow, i tis not reset by reading the CCR value. But also, no further captures will happen. At least that's what the flowchart in the users guide tells. If a capture was taken and before reading CCRx, another capture happens, COV is set and the state machine enters a state where the only exit is clearing the COV bit.[/quote]

    You haven't right in this. When the COV bit is set the interrupt is working properly so when you don't use COV you can omit this line. Once COV bit is set it just stay in high state not influencing captures.

    Jens-Michael Gross said:
     SomeVariable = CCR0; // we copy the TAR from falling edge instead of rising edge

    Here the comment is of course wrong, we do read the rising edge capture value. The falling edge didn't happen yet.[/quote]

    Yes, my copy-paste mistake.

    Jens-Michael Gross said:
    However, even if you would clear the CCIFG bit before the third edge happens, it won't be set by the third edge and CCR won't be updated, as long as COV bit is still set.

    This is not true. I tested it and COV bit don't influence CCIFG flag at all.

    Jens-Michael Gross, thank you very much for your help. Now I better understand the timers. It's a pity that documentation is not 100% clear in this topic. I will check 3x family user guide.

    Joseph, yes, I use SCS but events that I describe in earlier posts are still appearing and as Jens-Michael wrote it is not so simple to analysis this behavior because of lots of racing conditions.

    In my case the solution to detect properly very short spikes was:

    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void sensorTimer0Impulse(void)
    {
        uint8_t RisingEdgeDetected;
        uint8_t SomeVariable;
     
        RisingEdgeDetected = (TA0CCTL0 & CCI);
     
        if((TA0CCTL0 & COV) == COV)
        {
            if(RisingEdgeDetected == (TA0CCTL0 & CCI))//capture overflow appeared before our first edge test
            {
               RisingEdgeDetected = RisingEdgeDetected ^ CCI;
            }
        }
     
        RisingEdgeDetected = (RisingEdgeDetected >> 3u);
     
        if (RisingEdgeDetected == TRUE)      //we have rising edge interrupt
       {
        ...
       }
     
           SomeVariable = TA0CCR0; // this read is needed to re-enable COV bit detection
           TA0CCTL0 &= ~COV;   //clear capture overflow
    }

    This solution is not perfect - value of SomeVariable can have wrong timer captured value (but very close to the originall) and also if there are more than 2 fast edges we can just loose them and not handle properly.

  • Krzysztof Grabinski said:
    You haven't right in this. When the COV bit is set the interrupt is working properly so when you don't use COV you can omit this line. Once COV bit is set it just stay in high state not influencing captures.

    Interesting. Then the users guide is wrong. The state diagram (Fig. 17-11) clearly states that the only way to continue operation after COV was set is to clear COV. No further captures are taken (but maybe interrupts are still triggered) when COV is set.

    Well, I've added this to my documentation buglist.

**Attention** This is a public forum