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.

MSP430FR4133: RTC running under LPM0 fails when interrupts on P1.2 or P2.6 are set

Part Number: MSP430FR4133


Hi All,

This one took me a long time to figure out, and this MUST have a solution.

Readings are going to be taken 9 times per second, following an RTC interrupt.  Despite what the Users Manual stated, the RTC interrupt must be cleared in the ISR routine. This is just a test routine. A volatile unsigned long integer is incremented by one. A very short length led blinks with the updated number on the LCD. Then LPM0 is invoked to wait for the next RTC interrupt.

The problem is that when P1.2 or P2.6 have their interrupts set in the IO init, this block of code fails completely. Ultimately, there are other interrupts like these which will have to run concurrently with the RTC in the final project. This needs a solution.

    // Configure button S1 (P1.2) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN2);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN2);
//    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN2);

    // Configure button S2 (P2.6) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN6, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P2, GPIO_PIN6);
    GPIO_clearInterrupt(GPIO_PORT_P2, GPIO_PIN6);
//   GPIO_enableInterrupt(GPIO_PORT_P2, GPIO_PIN6);

...........

     __bis_SR_register(LPM0_bits | GIE);         // GO TO SLEEP
      __no_operation();         // End of main loop

}

#pragma vector = RTC_VECTOR
__interrupt void RTC_ISR(void)
{
    RTC_clearInterrupt(RTC_BASE, RTC_OVERFLOW_INTERRUPT_FLAG);

    __bic_SR_register_on_exit(LPM0_bits);                                                                    //edit, this now appears at the top of the ISR routine

    P1OUT |= BIT0;    // Turn LED1 On/
    __delay_cycles(100000);

    IReturn = IReturn+10;

    P1OUT &= ~BIT0;    // Turn LED1 off
  __delay_cycles(100000);

 
}

  • The LPM0 exit statement was moved to be right after the RTC_clearInterrupt statement, but while the loop still works fine, it still fails with P2.6 interrupt vector enable. Secondly, I thought both of these statements were unnecessary as they were automatic with the RTC interrupt. This is way above my pay grade, I hope someone helps with this. R.

  • What does "fails" mean? Resets? ISR_trap? Freezes? Sleeps forever? Displays an incorrect number?

    How often does the RTC interrupt? Do you have to actually push the buttons, or is the enablement sufficient?
  • Sleeps forever, sort of . It actually loops within the interrupt routine (that's what the led is for) as the interrupts are coming 5 times faster. Trying to reset the RTC modulo has no effect. That should not have to be done anyway.
  • Come to think of it, maybe as much as 20 times faster! R. The test blink, when working, is 1Hz, when not, at least 10 Hz. When blinking quickly, it never returns to int main() as the LCD digits are not incrementing. R.
  • There are no button pushes involved in any of this. This is a separate routine from the LaunchPad, although, the IO init and LCD init are very similar.
  • The following statements are for NO Px pins being interrupt enabled. Just for the

    //    RTC_clearInterrupt(RTC_BASE, RTC_OVERFLOW_INTERRUPT_FLAG);
    //  __bic_SR_register_on_exit(LPM0_bits);

    statements at the top of the routine.

    First, when the two extra statements at the top of the interrupt routine are there, everything works fine. Second, when they are not there, the interrupts are coming so fast the LED is blinking at least 10 times a second, and no modulo settings affect it.  Dare I say oscillating?

    When either of the two Px pins are interrupt enabled, with or without these two statements, there are no interrupts at all.

    The real question here is why setting the interrupt enables on Px pins affects the RTC at all. It is going to take an expert to fix this! R.

  • Yes, you do have to clear the RTC interrupt explicitly in the ISR. I don't see anything in the User Guide (SLAU445H) which suggests otherwise.

    In a bit of a curiosity, despite the statement (UG Table 15-2) that RTCIFG (aka RTCIF) is read-only and clearable only using the RTCIV, observed behavior is that it can be directly cleared.

    I wrapped your code fragments and ran it on a Launchpad, and I get the results I expect: (a) if the ISR clears the RTC interrupt, it blinks 1/second. (b) if the ISR does not clear the interrupt, it blinks faster (probably 5/sec due to the __delay-s). I see no side-effect from enabling (or not) the pin interrupts.

    I'm not sure what you and I are doing differently.

    #include "driverlib.h"
    int main(void)
    {
        WDT_A_hold(WDT_A_BASE);
    
        //  RTC setup lifted from Example rtc_ex1_countmode.c
        GPIO_setAsOutputPin( GPIO_PORT_P1, GPIO_PIN0);
        PMM_unlockLPM5();       // Engage GPIOs
        //Set DCO FLL reference = REFO
        CS_initClockSignal( CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
        //Set ACLK = REFO
        CS_initClockSignal(CS_ACLK, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
    
        //Initialize RTC
    #define INTERVAL_TIME 32768
        RTC_init(RTC_BASE, INTERVAL_TIME, RTC_CLOCKPREDIVIDER_1);
        RTC_clearInterrupt(RTC_BASE, RTC_OVERFLOW_INTERRUPT_FLAG);
        RTC_enableInterrupt(RTC_BASE, RTC_OVERFLOW_INTERRUPT);
        RTC_start(RTC_BASE, RTC_CLOCKSOURCE_XT1CLK);
    
        // Configure buttons, from E2E item:
        // Configure button S1 (P1.2) interrupt
        GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
        GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN2);
        GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN2);
        GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN2);
    
        // Configure button S2 (P2.6) interrupt
        GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN6, GPIO_HIGH_TO_LOW_TRANSITION);
        GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P2, GPIO_PIN6);
        GPIO_clearInterrupt(GPIO_PORT_P2, GPIO_PIN6);
        GPIO_enableInterrupt(GPIO_PORT_P2, GPIO_PIN6);
    
        while (1)
        {
            __bis_SR_register(LPM0_bits | GIE);         // GO TO SLEEP
            __no_operation();         // End of main loop
        }
        /*NOTREACHED*/
        return (0);
    }
    
    unsigned long IReturn; // uint32_t per E2E
    #pragma vector = RTC_VECTOR
    __interrupt void RTC_ISR(void)
    {
        RTC_clearInterrupt(RTC_BASE, RTC_OVERFLOW_INTERRUPT_FLAG);
        __bic_SR_register_on_exit(LPM0_bits); //edit, this now appears at the top of the ISR routine
        P1OUT |= BIT0;    // Turn LED1 On/
        __delay_cycles(100000);
        IReturn = IReturn + 10;
        P1OUT &= ~BIT0;    // Turn LED1 off
        __delay_cycles(100000);
    }
    
    #pragma vector=PORT1_VECTOR
    __interrupt void P1_ISR(void)
    {
        P1IFG = 0;        // Muzzle and ignore
        return;
    }
    #pragma vector=PORT2_VECTOR
    __interrupt void P2_ISR(void)
    {
        P2IFG = 0;        // Muzzle and ignore
        return;
    }
    

  • Hi Bruce, and Thank You for your efforts!

    After a good night's sleep, and thinking about my desire to "clean up" the rag-tag code placement of the LaunchPad author, I woke up with one real possibility, driven mostly by the hundreds of posts read from this forum.

    The problem clearly originated with the setting of the Px interrupt vectors, and in the LaunchPad these vectors are defined along with the rest of the IO in the main.c program. I thought that was odd the first time I looked at it, and I still think it odd now.

    I have three programs, periph.h, periph.c, and main.c.

    periph.h :include declarations, extern array declaratios, program declarations
    periph.c :array definitions, all init() definitions, and digit splitting subroutines
    main.c :just the main program and main program subroutines (just the way it should be, right? WELL, NOT QUITE!)

    The first attempt today was to move the Px interrupt vector sets to the main.c program. All other IO init statements stayed behind in periph.c ,

    void Init_IVectors()
    {
    // Configure button S1 (P1.2) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN2);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN2);
    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN2);

    // Configure button S2 (P2.6) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN6, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P2, GPIO_PIN6);
    GPIO_clearInterrupt(GPIO_PORT_P2, GPIO_PIN6);
    GPIO_enableInterrupt(GPIO_PORT_P2, GPIO_PIN6);
    }

    PROBLEM SOLVED!!

    The RTC interrupt vector still has to be cleared in the pragma vector ISR routine, but the LPM0 exit statement was removed. Now everything works as it should.

    I am going to go back and look for the statement which said the RTC interrupt flag would auto clear, and will probably find out that I didn't quite read it right. I will let you know what I find.
    Robert. :)
  • Epilogue:

    I'm sure this has been discovered before, but even after all the posts from the forum that I read, I never ran across THIS.

    The IO interrupt declarations are now back in periph.c , where they belong. And the program runs as it should.

    The original LaunchPad software example placed all the IO init statements in the main.c program. As mentioned before, I thought this was odd. After I placed these statements back into perif.c , which is where I thought they belonged, even simple test routines would not run.

    Then, the IO interrupt init statements were moved to the bottom of the main.c program, as they were in the original LaunchPad software example, and all runtime problems went away.

    But now with the IO interrupt statements split away from the rest of the IO init, and after thinking about this original "repair" over breakfast (my wife is right, I AM a boring person!), I placed the IO interrupt statements back into periph.c, still split off under the function Init_IVectors..  I thought that possibly changing the order of the main.c init() statements might make a difference.

    And what a surprise! The three init() statements which are critical are placed inline, so I won't ever forget to place them this way. The other two init() statements can be placed either before or after, but the three inline statements MUST be in this order, ALWAYS!

        Init_LCD();
        Init_RTC();
        Init_GPIO(); Init_Clock(); Init_IVectors();

    Go figure.

    Robert.

  • Hi again Bruce,
    I found this paragraph in the Users Manual. I not quite sure how things are defined, thank you for helping with this.

    1.3.4.1 Interrupt Acceptance

    5. The interrupt request flag resets automatically on single-source flags. Multiple source flags remain set for servicing
    by software.

    6. All bits of SR are cleared except SCG0, thereby terminating any low-power mode. Because the GIE bit is cleared, further
    interrupts are disabled.

    In paragraph 6, LPM0 looks to be terminated upon ISR entry, so the LPMx exit statement was not necessary.

    In paragraph 5, the interrupt flag resets automatically on single-source flags. What is a single source flag?

    Robert.
  • > In paragraph 5, the interrupt flag resets automatically on single-source flags. What is a single source flag?
    I don't know TI's textbook definition of "single source". The only example I can think of is TAxCCTL0:CCIFG. The existence of an IV register suggests TI doesn't consider the RTCIFG to be single source. In any case, I think "is set until it is cleared by a read of the RTCIV register" [UG Sec 15.2.4] takes precedence over any general statement.

    > In paragraph 6, LPM0 looks to be terminated upon ISR entry, so the LPMx exit statement was not necessary.
    ISR processing clears CPUOFF in SR so the ISR can run. The stacked SR (step 3) still has CPUOFF set. This is a feature.
    If you want main to wake up, you need to clear CPUOFF in the stacked SR ("_on_exit").

**Attention** This is a public forum