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 mode not triggering ISR

I am trying to get the timer capture mode to work on the MSP430. Currently I am using a g2353 model of the MSP430 family. 
I have found a few examples for this but have been unable to get any of them to work.
My goal is to use this functionality to calculate the frequency of an input signal. I realize that the ISR in my code below is wrong because I am not resetting the interrupt flag among other things.
I am just trying to get the program to reach the ISR as my first goal, which I have been unable to accomplish. 
If anyone could give me some pointers on where I should look to fix this I would greatly appreciate it. 

#include "msp430g2353.h"

void main(void){
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ;

P1DIR = 0x01; // Set P1.0 out,1.1 input dir
P1OUT &= ~0x01; // LED off
P1SEL |= BIT1; // P1.1 CCI0A

TACTL = TASSEL_2 + MC_2; // SMCLK, contmode
TACCTL0 = CCIS0 + SCS + CAP + CCIE + CM_1;
_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt

while(1){};
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void TimerA0(void)
{
P1OUT ^= 0x01; // Toggle P1.0 using exclusive-OR
}
  • RyanN said:
    TACCTL0 = CCIS0 + SCS + CAP + CCIE + CM_1;

    You set CCIS0, which is the LSB of the CCIS bitfield and selects (if CCIS1 is clear) CCI0B.
    You probably wanted to set CCIS_0, which is the first option of this bitfieald and equals 0x00 (none of the two bits set).

  • I have adjusted my code some and appear to have interrupts firing now but there is still 
    something wrong that I cannot figure out. 
    I created a overflow counter that should count the amount of times that the counter 
    recycles back to 0.
    I think it is working correctly because I enter the ISR for TIMER0_A0_VECTOR. however 
    it doesnt appear that my counter is incrementing.
    It also seems like the other ISR is firing off too fast. previously I had an array that I would 
    save the counter differences to, 10 times.
    Whenever the program got 10 data points a breakpoint would pause the program and I 
    would observe the values. 
    I am feeding in a signal of 2Hz and the program would pause after only running no more 
    than a second. This is clearly wrong. 
    I am unsure if the interrupt is being called for the rising edge or if there is some other cause. 
    
    
    Is there a built in interrupt that will fire whenever the counter rolls over or should I 
    continue doing what I am doing?
    Any suggestions or comments would be appreciated. I have been working on this 
    problem for a while and still no luck. 
    Thanks 
    
    
    
    
    #include "msp430g2353.h"

    unsigned int count1, count2, ovCnt;
    int diff;

    void main(void){
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;

    P1DIR = BIT6; // Set P1.0 out,1.1 input dir
    P1OUT &= BIT6; // LED on

    P1DIR &= ~BIT2; //Port 1.2/TACCR1 - Input
    P1SEL |= BIT2; // Select Timer Capture option

    TACTL = TASSEL_2 + TACLR + MC_2; // SMCLK = 1 Mhz, continuous mode
    TACCTL1 = CM_1+CCIS_0+SCS+CAP+CCIE; // CCR1 used for timer capture
    // capture on rising edge, CCI0
    // Enable timer interrupt
    TACCR0 = 0xFFFF; //Set mode to watch for timer max
    TACCTL0 = CM_0 + CCIS_0 + CCIE; //CCR0 enabled interrupt, compare mode

    TACTL |= MC_2; // Start Timer, Continuous mode

    count1 = 0; count2 = 0; //initialize variables
    index = 0; ovCnt = 0;

    _BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt

    while(1){};
    }

    /*
    * used to count the amount of times the counter rolls over
    */
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void overflow_counter(void){
    ovCnt++; //increment overflow counter
    }

    /*
    * used to get count of times of signal rising edge
    */
    #pragma vector=TIMER0_A1_VECTOR
    __interrupt void TimerCapture(void){
    _DINT();
    switch(TAIV){
    case TA0IV_TACCR1://0x02:
    TACCTL1 ^= CCIFG; //clear interrupt flag

    P1OUT ^= BIT6; // Toggle P1.0 using exclusive-OR

    if(count1 == 0 && count2 == 0){ //get time of first edge
    count1 = TACCR1;
    ovCnt = 0;
    }else if(count1 != 0 && count2 == 0){ //get time of second edge
    count2 = TACCR1;
    if(count1 > count2){ //count1 has a higher count
    diff = ((0xFFFF - count1) + count2) + ((ovCnt - 1) * 0xFFFF);
    }else if(count1 < count2){ //count2 has a higher count
    diff = (count2 - count1) + ((ovCnt) * 0xFFFF);
    }
    count1 = 0;
    count2 = 0; //<<----breakpoint here
    }
    break;
    case TA0IV_TAIFG://0x0A://COV:
    // ovCnt++;
    // TACCTL1 ^= COV;
    break;
    }
    _EINT();
    }

  • You don't need CCR0 for getting the timer overflow. Setting TAIE in TA0CTL triggers an interrupt when the timer count rolls over from 0xffff to 0. You alread had it in your TIMER0_A1 ISR.

    However, it should work as you wrote it too. With the advantage that the CCR0 interrupt has preference to the CCR1 interrupt, so if a capture happens exactly at 0x0000, the overflow will be (correctly) counted first. However, if a capture happens at 0xffff or 0xfffe, I don't know which one will get executed first if the CPU is currently executing an instruction that takes more than one or two cycles. The interrupt logic isn't explained to that detail.

    The reason why you don't see the counter incrementing is that the compiler doesn't know that your ovCnt 'magically' changes inside an ISR. you shuld declare it volatile to tell teh compiler (and the debugger) that the value may change outside of the program flow.
    Interrupts (or parallel threads in a multithreading environment) are outside the scope of the C language´and changes done by an ISR are a 'surprise' to the main loop. 

  • Thank you very much for your assistance. I believe that I have mostly figured it out but once

    again something doesnt seem to be working correctly. 

    I changed my calculation function to the following: (I believe this should work for any values received)

    diff = (((0xFFFF - count1) + count2) + ((ovCnt - 1) * 0xFFFF));

    and when I do the math by hand using the gathered values I get the right value I would expect for

    a given input frequency.

    However the microcontroller always gets the wrong value. I noticed this earlier today on another section 

    of my code as well (when trying to convert a Celsius temp to a Fahreneit temp). Is there a limitation with

    the MSP430 when it comes to calculations such as this?

    I changed my calculations to the following just to see if I could find out what was up,

    but I am unable to see the value of temp1, and temp2. 

    unsigned int temp, temp1, temp2;
    temp = ((0xFFFF - count1) + count2);
    temp1 = (ovCnt - 1);
    temp2 = (temp1 * 0xFFFF);
    diff = temp + temp2;

    I assume this is caused by the debugger but was wondering if there was a way to 

    fix this so that I could see the values. 


  • I figured out that my problem was that I was using an integer to store the difference even

    though the value was more than what a 16-bit integer could store. 

    Now I am using doubles and everything seems to be working fine. 

    I am still curious as to why some values are able to be

    seen in the debugger and why some cant be seen.

  • RyanN said:
    I figured out that my problem was that I was using an integer to store the difference even though the value was more than what a 16-bit integer could store.

    Indeed.

    x*0xffff will result in 0x10000-x, since 0xffff and x are both integers, so the result is truncated to an integer.

    Using (long)0xFFFF, or, as it is faster, using "(long)(x)<<16", which is actually x*(long)0x10000, but much, much faster than a multiplication) would have resulted in the multiplication being done in long range (32 bit). However, the difference building before would still be subject to truncation since (here again (long)0xFFFF is required, so a long value of 65535L is used to do the additions and subtraction.
    However, if the variable DIV wher ethe result is stored to, is only a 16 bit int, then you have the correctly calculated result still truncated.

    RyanN said:
    Now I am using doubles and everything seems to be working fine. 

    not fine. It' sworking. But slow, and you unnecessarily included all the math code for floating point calculations.

    Make the variables unsigned long (so they store 32bit up to 0xFFFFFFFF) is by magnitudes faster and more than just a bit smaller.

    RyanN said:
    I am still curious as to why some values are able to be seen in the debugger and why some cant be seen.

    Likely because of code optimization.
    Unless you declare a varaible as volatile,the compiler may optimize access to a variable, hold intermediate results in a processor register and write it to the variables memory location only when necessary (e.g. before calling a function or before exiting the current function). The debugger, however, only knows that a variable has a certain memory location. Not what the compiler might have optimized or reordered, So some changes are invisible until the funciton ends or calls another function. You shouldn't do debugging with enabled code optimization.

    In case of local variables, they may solely exist in a processor register. In this case, mabye no reference is generated for it at all and the debugger doesn't know of its existence. It depends on how much the compiler can tell the debugger about the variables.
    The variable, while being declare at funciton start, may even ot exist at all until required. Or may have been optimized away totally, since it was of no use (e.g. it had a fixed value, so in the generated binary, an immediate value was used rather than a variable). Also, if the variable is not used anymore later in the code, it may be discarded and the register is used for something else later.

    The first goal of a (microcontroller) compiler is to produce fast and efficient code, not to support debugging at the expense of unnecessarily big and slow code.

    Personally, I never used a debugger for the MSP. Never.

  • Some How you have executed your ISR successfully for capture mode.

    But, I can not get in to the capture ISR by writing below code.

    #include <msp430.h>

    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    __enable_interrupt();
    P1DIR |= 0x01; //  P1.0 as o/p direction
    P1DIR &= ~0x02;  // P1.1 as inpur direction
    P1SEL &= ~0x01; // P1.0 for I/O operation
    P1SEL |= 0x02; // P1.1 for capture input 
    P1OUT &= ~0x01; // LED P1.0 output zero

    CCTL0 = CAP + SCS + CM_1 + CCIE + CCIS_0; // CCR0 interrupt enabled

    TACTL = TASSEL_1 + MC_2; 

    __bis_SR_register(LPM3_bits + GIE); // Enter LPM3 w/ interrupt

    while(1);
    }

    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A (void)

    {

    P1OUT ^= 0x01 ; // Toggle P1.0 using exclusive-OR

    }

    I can not be able to toggle LED, means even i am giving rising edges to P1.1 port.

    Please help. 

  • It would be really helpful to know on which MSP you’re working. Because port pin functions are device specific.

    How do you generate the edge on the pin? By default, the pin has no defined level. It might be already high when you try to give rising edges, and when you release it, it won’t return to 0. You should have a pull-up (or rather pull-down, if you want to measure rising edges) resistor (on some MSPs, there are internal resistors, but you need to activate them).

  • I am working on : MSP430F1122 Controller.

    Launch Pad : MSP-TS430PW28 Texas Instrumentation Launch Pad.

    I am giving giving falling edges within interval of 6 milli seconds, so if any delay by SCS bit it will be tolerable.

    Regarding giving edges, i have verified that by giving similar edges 

    i have successfully detect positive to negative edges by giving it to pin

    configured as general purpose input (GPIO PIN). So I think edges i am giving are proper.

    But there is some thing wrong when I am going for capture edges.

    Please help.

  • I guess I found what’s happening.
    In your case, ‘any’ delay will be ‘eternal’ delay. You’re running the timer from ACLK (TASSEL_1). However, on 1x family, ACLK needs an external crystal on XT1 to have any frequency at all.
    No timer clock, no synchronize (SCS bit), no capture, no interrupt. Your code doesn’t select any load capacitors for an external watch crystal nor does it set XT1 for HF crystal or bypass operation.
    Try using TASSEL_2 to use SMCLK (which runs from DCO, with about 600kHz by default) as timer clock. Or connect a crystal to XT1 to give ACLK a source.

**Attention** This is a public forum