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.

MSP430FR5994: GPIO toggle, interrupt and delay not working together

Part Number: MSP430FR5994

I am trying to toggle a GPIO pin of one board (power controller) which provides power to the other similar board (DUT). The DUT sets a GPIO pin when it completes a specific task. The power controller board monitors the GPIO pin and service the interrupt generated with ISR. The power pin toggles itself every 2 seconds. I am also trying to timestamp the start of GPIO toggle and start of interrupt. The problem is I am not able to do these two operation of toggling power GPIO and servicing the interrupt together.
Any help is much appreciated. Let me know if you have questions.

Here is my power controller code:

while(1){
// GPIO for power toggling
       P5OUT ^= 0x01;
 // Timestamp start of gpio toggle
        tmp_start = TA0CCR2;

        __bis_SR_register(LPM0 + GIE);

// Send data to UART
        sprintf(readstr, "%x", tmp_start);
        UART_TX(readstr);
        UART_TX(",");
        sprintf(readstr, "%x", tmp_stop);
        UART_TX(readstr);
        UART_TX(",");
        sprintf(readstr, "%d", overflow_count);
        UART_TX(readstr);
        UART_TX("\n");
        int_flag = 0;

// Delay to keep power GPIO high for 2 seconds        
       __delay_cycles(16000000);

    }
}

// Port 1 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=PORT1_VECTOR
__interrupt void port1_isr_handler(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(PORT1_VECTOR))) port1_isr_handler (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(P1IV, P1IV__P1IFG7))
    {
        case P1IV__NONE:    break;          // Vector  0:  No interrupt
        case P1IV__P1IFG0:  break;          // Vector  2:  P1.0 interrupt flag
        case P1IV__P1IFG1:  break;                // Vector  4:  P1.1 interrupt
        case P1IV__P1IFG2:  break;          // Vector  6:  P1.2 interrupt flag
        case P1IV__P1IFG3:  break;          // Vector  8:  P1.3 interrupt flag
        case P1IV__P1IFG4:
            tmp_stop = TA0CCR2;
            P1OUT ^= BIT1;
            overflow_count = 0;
            __bic_SR_register_on_exit(LPM0 + GIE);
            break;          // Vector  10:  P1.4 interrupt flag
        case P1IV__P1IFG5:  break;          // Vector  12:  P1.5 interrupt flag
        case P1IV__P1IFG6:  break;          // Vector  14:  P1.6 interrupt flag
        case P1IV__P1IFG7:  break;          // Vector  16:  P1.7 interrupt flag
        default: break;
    }
}

// Timer0_A3 CC1-4, TA Interrupt Handler
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer0_A1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_A1_VECTOR))) Timer0_A1_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch (__even_in_range(TA0IV, TAIV__TAIFG))
    {
        case TAIV__TACCR1: break;
        case TAIV__TACCR2:
            break;
        case TAIV__TAIFG:
            overflow_count++;
            break;
        default: break;
    }
}


  • Hello Vishal,

    It looks like your code was cut off.  Can you please send it again?  

  • Hello Eddie,

    Can you see my code now?

    Regards,

    Vishal

  • Yes, I can see it.  I will have a look at get back to you with suggestions.

  • > __bic_SR_register_on_exit(LPM0 + GIE);

    This is disabling interrupts (GIE) in main, so you won't get any more of them. ("bic" means "clear bit".)  Also use "LPM0_bits", not "LPM0".Try:

    > __bic_SR_register_on_exit(LPM0_bits);

  • Thanks for the input Bruce!

    Vishal,

    Can you please confirm if this resolves your issue?

  • Thanks Bruce for the input.

    I changed LPM0 to LPM0_bits, at both places "__bis_SR_register(LPM0_bits | GIE);" and "__bic_SR_register_on_exit(LPM0_bits);" but still it doesn't goes in the ISR. It stays in the LPM0 mode and just waits for the interrupt and GPIO stays high.

    I am trying to solve this problem by adding another board which just controls the power and a different board which monitors the interrupt.

    Vishal

  • Which GPIO remains high? P1.4 (Trigger) or P1.1 (Indicator)?

    I'm trying to figure out whether the ISR runs (a) never (b) exactly once or (c) a few times.

    Your initialization code (not shown) may be relevant.

  • Sorry P5.0 remains high always from this toggle P5OUT ^= 0x01;

    To my understanding the ISR never runs. But when I don't go into LPM mode and just enables the global interrupts with "__bis_SR_register(GIE);". Then it always goes in the ISR but generates a wrong timestamps. It generates timestamps only after the delay of 2 seconds.

    Here is my setup code:

    void clk_setup() {
        // Init clocks and I/O:
        // Startup clock system with max DCO setting ~8MHz
        CSCTL0_H = CSKEY >> 8;                      // CSKey=A500.  Unlock clock registers
        CSCTL1 = DCOFSEL_6;                         // Set DCO to 8MHz.  6|40
        CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
        CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;       // Set all dividers
        CSCTL0_H = 0;                               // Lock CS registers
    }
    
    void gpio_config() {
        P5DIR |= 0x01;               // Set P5.0 to output direction
        P1DIR |= 0x01;
    
        P1DIR |= BIT0;
        P1OUT &= ~BIT0;
        P1DIR |= BIT1;
        P1OUT &= ~BIT1;
        P1IE |= BIT4;               // P1.4 interrupt enabled
        P1IES &= ~BIT4;              // P1.4 lo to hi edge
        P1IFG &= ~BIT4;             // P1.4 IFG cleared
    
        P2OUT = 0;
        P2DIR = 0xFF;
        P3OUT = 0;
        P3DIR = 0xFF;
        P4OUT = 0;
        P4DIR = 0xFF;
        P6OUT = 0;
        P6DIR = 0xFF;
        P7OUT = 0;
        P7DIR = 0xFF;
        P8OUT = 0;
        P8DIR = 0xFF;
        PJOUT = 0;
        PJDIR = 0xFFFF;
    }
    
    void timer_setup() {
        // Timer0_A3 Setup
    //    TA0CCTL2 = CM_1 | CCIS_1 | SCS | CAP | CCIE;
        TA0CCTL2 = CM__BOTH | CCIS_1 | SCS | CAP | CCIE;
                                                // Capture rising edge,
                                                // Use CCI2B=ACLK,
                                                // Synchronous capture,
                                                // Enable capture mode,
                                                // Enable capture interrupt
        TA0CCTL2 ^= CCIS_0;
    
    
        TA0CTL = TASSEL__SMCLK | MC__CONTINUOUS | TACLR | TAIE;// Use SMCLK as clock source,
                                                // Start timer in continuous mode
    }
    

  • Have you tried setting a breakpoint in the Port1 ISR? I'm wondering if you're encountering a race where the DUT responds very fast, between the power-up (P5.0 transition) and the LPM, so the wakeup event gets lost. 

    Unsolicited: I'm not quite sure why you're doing your timestamp that way -- it's capturing TA0R (SMCLK) at 2xVLOCLK (about 20kHz), which has a large quantization error in addition to the inaccuracy of VLOCLK. I suspect you'd actually get better results by reading TA0R directly (rather than TA0CCR2) at the appropriate times (and you wouldn't need any capture event).

    Alternatively, if you could bring in the DUT trigger on P1.1, you could use that (CCI2A) for your capture input, combining timestamp and event (with no Port1 interrupt at all).

  • I will try setting breakpoints in the Poert 1 ISR and try to debug the problem today.

    On the other hand, I am now using a third board to control power which seems to work fine.

    Can you explain more about the timer suggestion you gave. I am using capture and compare because it will generate me interrupt at both edges of the clock. From that information, I calculate the actual time elapsed.

    Thanks,

    Vishal

  • If it does turn out to be a race, a simple fix is to insert "__disable_interrupt();" at the top of the loop, just before you toggle P5.0. This will hold off the Port1 interrupt until you re-enable at the LPM line (a few lines later).

    ---------------------------------------

    I should probably let you explain your intent, but what I see is: You're doing the capture using CCIS=1 (CCI2B) which is ACLK=VLOCLK. [Ref data sheet (SLASE54B) Table 6-12.] Since you're using both edges, the timer is capturing twice each VLOCLK period (~2x10kHz), regardless of what the DUT is doing. Then the Port1 interrupt reads the most recent captured value as the time stamp. 

    This will give you a timestamp of sorts, but it's somewhat roundabout and not very accurate.

    A more natural use of capture would be to trigger the capture directly from the pin you're interested in -- in this case the DUT trigger -- using CCIS=0 (CCI2A). When the edge happens, the timer will capture the exact time stamp and also generate a (timer) interrupt so you can do what you need to do. In this case, there's no need for a Port interrupt at all.

    P1.1, is CCI2A for Timer TA0, not P1.4; this is a (minor) inconvenience. [Ref data sheet Table 6-20.] Two options:

    1) Hardware: Physically move the DUT trigger wire currently connected to P1.4 over to P1.1 and use P1.1 as the capture input. This may or may not be easy depending on your hardware.

    2) Software: P1.4 is CCI1A for Timer TB0. [Ref data sheet Tables 6-17 and 6-21.] It looks like  you're not already using TB0, so you can switch to using that and use TB0CCR1 instead of TA0CCR2. For your purposes, Timer B works pretty much identically to Timer A, even though some of the names are different.

    In either case you would need to (a) Don't set P1IE and (b) Set P1SEL0 according to Table 6-20 or 6-21.

  • "I should probably let you explain your intent, but what I see is: You're doing the capture using CCIS=1 (CCI2B) which is ACLK=VLOCLK. [Ref data sheet (SLASE54B) Table 6-12.] Since you're using both edges, the timer is capturing twice each VLOCLK period (~2x10kHz), regardless of what the DUT is doing. Then the Port1 interrupt reads the most recent captured value as the time stamp. "

    This is exactly what I am trying to achieve.

    "1) Hardware: Physically move the DUT trigger wire currently connected to P1.4 over to P1.1 and use P1.1 as the capture input. This may or may not be easy depending on your hardware."

    For the Hardware approach, P1.1 port is LED on my board and it's not accessible as pin. So I don't think I can use it.

    "2) Software: P1.4 is CCI1A for Timer TB0. [Ref data sheet Tables 6-17 and 6-21.] It looks like  you're not already using TB0, so you can switch to using that and use TB0CCR1 instead of TA0CCR2. For your purposes, Timer B works pretty much identically to Timer A, even though some of the names are different."

    The software approach seems feasible in my case.

    You explained this solution in very detail. Thanks for that, it is not really apparent in the datasheet.

    My question is if I use for example CCIS=0 on Timer B, am I using SMCLK or VCLK?

  • Perhaps this is the missing piece: A timer has

    1) A Clock Source: This is the underlying clock that makes the counter (TA0R) tick. In your case, this is SMCLK (TASSEL=2).

    2) A Capture Source: This is a trigger (edge), normally on an external pin, which causes the timer to save a copy of TA0R into one of the CCRs. 

    The timer value is saved based on a trigger from the Capture Source; the value saved is based on the Clock Source.

    With CCIS=1 (CCI2B in your case) the Capture Source is an internal signal, in this case an internal clock. This is of limited value (though non-0 -- e.g. a software FLL), since there are other ways to read that internal clock. 

    With CCIS=0 (CCI2A in your case) the Capture Source is an external pin. Your DUT trigger signal is a perfect example.

    The actual CCI2A and CCI2B usages vary by device; there's a table in each data sheet. This may be a good moment to go back and re-read data sheet Tables 6-12 and 6-17.

  • Thanks Bruce!

    I got it now.

    Vishal

**Attention** This is a public forum