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: RTC_C RTCTEV not always triggering

Part Number: MSP430FR5994
Other Parts Discussed in Thread: ENERGYTRACE

I am using the RTC_C module in calendar mode and I am using the RTCTEV interrupt to check if it is time to do a sensor sample. I am running a test in which I am performing sensor samples once per minute. It will run correctly when I am debugging my project, but then when I stop the debugger the interrupt does not trigger.

I am using LPM3.5, and I've verified that every code path from startup enables RTCTEVIE prior to clearing LOCKLPM5; I have also verified that the code also verifies that RTCTEVIE is set prior to enabling LPM3.5. For my environment, I am using CCS version 7.2.0.00013 for my IDE, and I am using TI/RTOS version 2.20.00.06. If I stop the debugger while the debug session is running (not when it is paused) and the MSP430 is in LPM3.5, then the RTCTEV interrupt does not occur. Even if I do a hard reset (pull RST line low) the RTCTEV interrupt never triggers. However, if I set a breakpoint somewhere in the code and stop the debugger while the MSP430 is in an active mode and paused at the breakpoint and then perform a hard reset, then the RTCTEV interrupt will trigger every minute as it should.

I am monitoring when the RTCTEV interrupt triggers by using EnergyTrace and watching the power draw. There is a noticeable change in power draw when my system does a sensor sample. Even if for some reason the RTCTEV interrupt is triggering but the sensor sample would not somehow occur, there still is a measurable power draw when the the MSP430 services the RTC interrupt. Through this method, I am verifying that in some cases the RTCTEV interrupt never triggers.

Is there something else that may be set incorrectly prior to entering LPM3.5 that can prevent the RTCTEV interrupt from triggering? Here are the RTC_C register settings as they are set prior to entering LPM3.5 (as read from the debugger, paused just prior to entering LPM3.5):

RTCCTL0: 0x9660

RTCCTL13: 0x0030

RTCOCAL: 0x0000

RTCTCMP: 0x4000

RTCPS0CTL: 0x0101

RTCPS1CTL: 0x0101

RTCPS: 0xD776

RTCIV: 0x0006

RTCTIM0: 0x0206

RTCCNT12: 0x0408

RTCTIM1: 0x0000

RTCCNT34: 0x0000

RTCDATE: 0x0405

RTCYEAR: 0x07E3

RTCAMINHR: 0x8480

RTCADOWDAY: 0x0000

RT0PS: 0xEE

RT1PS: 0xB2

RTCCNT1: 0x13

RTCCNT2: 0x06

RTCCNT3: 0x00

RTCCNT4: 0x00

  • Hi,

    1. I advice you not use Energy Trace to monitor the interrupt. Can you use a LED or GPIO trigger to show whether the code enter  RTCTEV interrupt.

    2. "However, if I set a breakpoint somewhere in the code and stop the debugger while the MSP430 is in an active mode and paused at the breakpoint and then perform a hard reset, then the RTCTEV interrupt will trigger every minute as it should." You mean the breakpoint will result a different MCU performance, even you don't in debug mode? Quite strange.

    3. Can you post the code and put some pictures?

    Eason

  • Eason,

    In reply to each question:

    1. I've also been measuring the voltage by my sensor to see if it gets turned on, which it isn't.  But, I am working at modifying my code to trigger a GPIO directly in the RTC ISR so I can see if the ISR has been reached.

    2. Yes, I see different operation on my hardware if I pause the debug session (or stop at a breakpoint) and then stop the debug session as opposed to stopping the debug session while the code is free running.

    3. I'm working on pulling the proprietary code to show you what you would need to see for this problem.  However, the RTC register settings that I showed you I obtained by setting a breakpoint just prior to the following code:

        PMMCTL0_H = PMMPW_H;                      // Open PMM Registers for write
        PMMCTL0_L |= SVSHE_1 | PMMREGOFF;         // and set PMMREGOFF and SVSHE
        __bis_SR_register(LPM4_bits | GIE);       // Enter LPM3.5
    

    The next thing that happens after I exited the debug session was enter LPM3.5.  This is also the only place that I enter LPM3.5 in my code, and I haven't been able to find a path in which RTCEVIE cannot be set prior to reaching this spot.

  • Hi Bryan,

    I may know the reason. You are not in LPM3.5. You are in LPM4.5. Here is the right code.

    PMMCTL0_H = PMMPW_H;                      // Open PMM Registers for write
    PMMCTL0_L |= SVSHE_1 | PMMREGOFF;         // and set PMMREGOFF and SVSHE
    __bis_SR_register(LPM3_bits | GIE);       // Enter LPM3.5

    Please see the datasheet, in LPM4.5 mode, RTC can't work. Hope it can solve your question. About the reason that why you can use RTC in debug mode, please don't care about it, because you are using the MCU out of spec.
    Eason

  • Eason,

    From my understanding from looking at the TI examples:

    PMMCTL0_H = PMMPW_H;                      // Open PMM Registers for write
    PMMCTL0_L |= PMMREGOFF;                   // and set PMMREGOFF
    
    // Enter LPM3.5 mode with interrupts enabled. Note that this operation does
    // not return. The LPM3.5 will exit through a RESET event, resulting in a
    // re-start of the code.
    __bis_SR_register(LPM4_bits | GIE);

    enters LPM3.5 and:

    PMMCTL0_H = PMMPW_H;                // Open PMM Registers for write
    PMMCTL0_L |= PMMREGOFF;             // and set PMMREGOFF
    PMMCTL0_H = 0;                      // Lock PMM Registers
    
    // Enter LPM4 Note that this operation does not return. The LPM4.5
    // will exit through a RESET event, resulting in a re-start
    // of the code.
    __bis_SR_register(LPM4_bits);
    

    enters LPM4.5.  I pulled these examples from examples msp430fr599x_lpm3-5_01.c and msp430fr599x_lpm4-5_01.c respectively.  While debugging, EnergyTrace recognizes my sleep mode as LPM3.5 with the way I have been entering sleep mode.

    Bryan

  • Hi Bryan,

    I consult my colleague, the different between LPM4.5 and LPM3.5 is the RTC module. The result of __bis_SR_register(LPM4_bits) and __bis_SR_register(LPM3_bits) is same.

    The difference lies on RTC_Init(), because it open RTC before enter LPM3.5.

        // Init board & RTC, then enter LPM3.5
        Board_Init();                           // Regular init code for GPIO and CS
        RTC_Init();                             // Regular init code for RTC_B
        EnterLPM35();                           // This function will NOT return.

    Please check the code of your part is right. If it also can't solve your question, can you make a smallest code example that can recreate the problem and post it on E2E?

    Eason

  • Eason,

    I'm including code below that should be a fair representation of how we manipulate the RTC peripheral.  The RTCTEV interrupt is setup to trigger sensor samples.  In the code shown below it should trigger once every minute.  We also are using the RTC alarm for certain activities that are less frequent.  In the code below, the alarm is setup for once every hour; in reality it is more complex than that, but it is a fair representation of how we use the RTC alarm and initialize the registers.  I will be doing testing with this version of the code to see if I can narrow down cases in which the RTC interrupt does or does not trigger.

    /** Last known good time from the RTC */
    #pragma PERSISTENT(backupTime)
    static sClockDateTime backupTime = {
        .year = 2019,
        .month = 11,
        .day = 11,
        .hour = 0,
        .min = 0,
        .sec = 0
    };
    
    /** Boolean to track whether an RTC wakeup alarm has been set */
    #pragma PERSISTENT(isAlarmSet)
    static bool isAlarmSet = false;
    
    /** Boolean to track whether to do special alarm actions */
    #pragma PERSISTENT(enactAlarmActions)
    static bool enactAlarmActions = false;
    
    /** Boolean to track whether it is time to sample the sensors */
    #pragma PERSISTENT(sampleSensors)
    static bool sampleSensors = false;
    
    /**
     * RTC interrupt handler
     */
    void RTC_C_hwiIntFxn(void)
    {
        uint16_t rtciv = RTCIV;
        while(rtciv)
        {
            switch(__even_in_range(rtciv, RTCIV__RT1PSIFG))
            {
                case RTCIV__NONE:      break;       // No interrupts
                case RTCIV__RTCOFIFG:  break;       // RTCOFIFG
                case RTCIV__RTCRDYIFG:              // RTCRDYIFG
                {
                    /* Update the backup time */
                    backupTime.year = RTCYEAR;
                    backupTime.month = RTCMON;
                    backupTime.day = RTCDAY;
                    backupTime.hour = RTCHOUR;
                    backupTime.min = RTCMIN;
                    backupTime.sec = RTCSEC;
                    break;
                }
                case RTCIV__RTCTEVIFG:              // RTCEVIFG - Interrupts every minute
                {
                    /* Sample the sensors once every minute; normally performs other checks to
                     * trigger a sample less frequently, but set to once per minute for testing. */
                    sampleSensors = true;
                    break;
                }
                case RTCIV__RTCAIFG:                // RTCAIFG
                {
                    /* Note it is time for special operations due to the RTC alarm */
                    enactAlarmActions = true;
    
                    /* Note the alarm is no longer set */
                    isAlarmSet = false;
                    break;
                }
                case RTCIV__RT0PSIFG:  break;       // RT0PSIFG
                case RTCIV__RT1PSIFG:  break;       // RT1PSIFG
                default: break;
            }
            rtciv = RTCIV;
        }
    }
    
    /**
     * Set the next RTC alarm
     */
    void rtcSetNextAlarm(const sClockDateTime *newTime)
    {
        /* Validate our input pointer */
        if(newTime != NULL)
        {
            /* Set the alarm for the indicated time */
    
            // Configure RTC_C
            RTCCTL0_H = RTCKEY_H;               // Unlock RTC key protected registers
            RTCAHOUR = newTime->hour | HOURAE_H; // Set the alarm hour
            RTCAMIN = newTime->min | MINAE;      // Set the alarm minute
            RTCADAY = 0;                        // Cancel the alarm day of the month; we don't use it
            RTCADOW = 0;                        // Cancel the day of week alarm; we don't use it
            RTCCTL0_L |= RTCAIE;                // enable RTC alarm interrupt
            RTCCTL0_H = 0;                      // Lock RTC key protected registers
    
            /* Note the alarm is set */
            isAlarmSet = true;
        }
    }
    
    /**
     * Initialize the RTC
     */
    void rtcInit(void)
    {
        UInt hwKey;
    
        /* Disable interrupts while we are changing the RTC time */
        hwKey = Hwi_disable();
    
        // Configure RTC_C
        RTCCTL0_H = RTCKEY_H;                     // Unlock RTC key protected registers
        RTCCTL0_L = RTCRDYIE | RTCTEVIE;          // enable RTC event interrupt for each minute and RTC ready interrupt each second
        RTCCTL1 = RTCHOLD | RTCMODE | RTCTEV__MIN; // Calendar mode, RTC hold, in RTC mode
    
        /* Set the RTC from the backup time */
        RTCYEAR = backupTime.year;
        RTCMON = backupTime.month;
        RTCDAY = backupTime.day;
        RTCHOUR = backupTime.hour;
        RTCMIN = backupTime.min;
        RTCSEC = backupTime.sec;
    
        /* Note that the alarm is not yet set */
        isAlarmSet = false;
    
        RTCCTL1 &= ~RTCHOLD;                      // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock RTC key protected registers
    
        /* Restore interrupts to their previous state */
        Hwi_restore(hwKey);
    }
    
    /**
     * Enter LPM3.5
     */
    static void EnterLPM35(void)
    {
        /*
         * NOT SHOWN: Disabling all the UARTs, watchdog timer, and setting all GPIO
         * as needed prior to entering LPM3.5.
         */
    
        /* Setup the RTC for sleep mode */
        if(!isAlarmSet)
        {
            /* Set alarm to trigger at the start of the next hour */
            sClockDateTime alarmTime = backupTime;
            alarmTime.min = 0;
            alarmTime.sec = 0;
            alarmTime.hour++;
            while(alarmTime.hour > 23)
            {
                alarmTime.hour -= 24;
            }
    
            rtcSetNextAlarm(&alarmTime);
        }
    
        // Configure RTC_C
        RTCCTL0_H = RTCKEY_H;   // Unlock RTC key protected registers
        RTCCTL0_L |= RTCTEVIE;   // Set the RTC event interrupt for each minute
        RTCCTL0_L &= ~RTCRDYIE; // Clear the RTCRDY interrupt
        RTCCTL0_H = 0;          // Lock RTC key protected registers
    
        /* Disable TA0 */
        TA0CCTL0 = 0;
        TA0CCR0 = 0;
        TA0CTL = 0;
    
        PMMCTL0_H = PMMPW_H;                      // Open PMM Registers for write
        PMMCTL0_L |= SVSHE_1 | PMMREGOFF;         // and set PMMREGOFF and SVSHE
    
        // Enter LPM3.5 mode with interrupts enabled. Note that this operation does
        // not return. The LPM3.5 will exit through a RESET event, resulting in a
        // re-start of the code.
        __bis_SR_register(LPM4_bits | GIE);
    }
    
    /**
     * Initial setup after wakeup from LPM3.5
     */
    void WakeUpLPM35(void)
    {
        /* NOT SHOWN: Initialize GPIO for our specific configuration needs */
    
        /* Keep the lines configured for the 32 kHz crystal */
        PJSEL0 |= BIT4 | BIT5;
    
        /* Setup the RTC from wakeup */
        RTCCTL0_H = RTCKEY_H;                     // Unlock RTC key protected registers
    
        /* If the alarm is set, then re-activate the alarm interrupt.  Otherwise, only activate
         * the minute and second interrupts. */
        if(isAlarmSet)
        {
            RTCCTL0_L = RTCAIE | RTCRDYIE | RTCTEVIE;   // enable RTC event interrupt for each minute, RTC ready interrupt each second and alarm interrupt
        }
        else
        {
            RTCCTL0_L = RTCRDYIE | RTCTEVIE;          // enable RTC event interrupt for each minute and RTC ready interrupt each second
    
        }
        RTCCTL1 = RTCHOLD | RTCMODE | RTCTEV__MIN; // Hexadecimal mode, RTC hold, in RTC mode
        RTCCTL1 &= ~RTCHOLD;                      // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock RTC key protected registers
    
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings. This will also re-activate the RTC
        // settings.
        PM5CTL0 &= ~LOCKLPM5;
    }
    
    int main(void)
    {
        /* Stop the watchdog timer immediately */
        WDTCTL = WDTPW | WDTHOLD;
    
        // Determine whether we are coming out of an LPMx.5 or a regular RESET.
        if (SYSRSTIV == SYSRSTIV_LPM5WU)
        {
            /* Restore I/O state prior to entering LPM3.5 */
            WakeUpLPM35();
            __enable_interrupt();
        }
        else
        {
            /* Initialize the RTC */
            rtcInit();
        }
    
        /* Initialize the GPIO and the rest of the peripherals.  Specifics not
         * shown. */
        Board_initGPIO();
        Board_initGeneral();
    
        /* If we don't have anything to do, just go right back to sleep */
        if(!enactAlarmActions  && !sampleSensors)
        {
            EnterLPM35();
        }
    
        /* Start BIOS.  NOT SHOWN: tasks that handle sensor sampling and the
         * special alarm actions. */
        BIOS_start();
    
        return (0);
    }
    
    

    Bryan

  • Hi,

    I found the reason.

    RTC module needs to use an external low-frequency oscillator. It must be operated at 32768 Hz. You need to add this code.

    // Configure LFXT 32kHz crystal
        CSCTL0_H = CSKEY >> 8;                  // Unlock CS registers
        CSCTL4 &= ~LFXTOFF;                     // Enable LFXT
        do
        {
          CSCTL5 &= ~LFXTOFFG;                  // Clear LFXT fault flag
          SFRIFG1 &= ~OFIFG;
        } while (SFRIFG1 & OFIFG);              // Test oscillator fault flag
        CSCTL0_H = 0;                           // Lock CS registers

    About why in debug mode it can work. Because the Clock is supplied by JTAG. RTC will also be guarded by JTAG module. The clock source will be different.

    Eason

  • Eason,

    I see what's going on.  TI-RTOS does setup the clocks so the RTC module is using the low-frequency oscillator.  But, for that to happen BIOS_start() needs to get called.  I have a code path in main() in which I could go into LPM3.5 prior to calling BIOS_start(); depending on the previous clock register settings, it is then possible to enter LPM3.5 without the low-frequency oscillator getting setup.  I think I was hitting this case depending on how I started and stopped everything on the debugger.

    I've modified my code to guarantee that the low-frequency oscillator is always initialized prior to calling LPM3.5 and I can no longer reproduce the problem.  This seems to be the fix I needed.

    Thanks,

    Bryan

**Attention** This is a public forum