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.

MSP430F5419A: LPM3_EXIT in timer0 B1 interrupt function invoked, but system not recovered from LPM3 mode

Part Number: MSP430F5419A


These days we've encountered a problem on timer0 B1 interrupt process. We're using IAR MSP430 7.20.1 as the development environment. And we're using ACLK from VLO 9.4KHz clock as the clock source of timer 0 B1 in LPM3 mode. We uses continuous mode, ID /8, TBSSEL ACLK and TBIE enabled in TB0CTL initialization and TBIDEX /2 in TB0EX0. So the timer B interrupt occurs every 3569.6s, about 59.5minute, we uses the time as about 1 hour. And we need the system keeps in LPM3 sleep mode until about 3 hours arrives and then it's waken up. So we implemented the interrupt as following:

static U_WORD timeoutCounter = 0U;

#pragma vector = TIMER0_B1_VECTOR
static __interrupt void timerBInt( void ) {
    switch(TB0IV) {
        case TB0IV_TBIFG:
            timeoutCounter++;
            break;
        default:
            break;
    }

    if(timeoutCounter >= 3) {
        LPM3_EXIT;
    }
}

void ctrlHostSupplyEnterSleepMode( void ) {

        ...
        LPM3;
        __no_operation();

        drvDioCurMeasSetOn(HIGH);

        ...

}

But the system doesn't work as we expected. When the timeoutCounter arrives 3 in the 3rd timeout interrupt, it do invoked LMP3_EXIT, but the system is not waken up and break at the breakpoint on drvDioCurMeasSetOn(HIGH) line after LPM3 line in ctrlHostSupplyEnterSleepMode() function in debugger mode(We're sure LPM3 line already invoked and it's in LPM3 mode in the whole test. We tested with dozens of times and added breakpoints in other later lines to avoid there's maybe some code optimization problem to skipped the breakpoint.). So we started to track this issue. After debugging dozens times, we finally found if we remove the if(timeoutCounter >= 3) line and exactly let the interrupt of timerBInt() to invoke LPM3_EXIT before exit. The drvDioCurMeasSetOn(HIGH) line break occurs and the system is waken up. So that mean only the 1st interrupt to invoke LPM3_EXIT will wakeup the system. Code Modified like this:

#pragma vector = TIMER0_B1_VECTOR
static __interrupt void timerBInt( void ) {
    switch(TB0IV) {
        case TB0IV_TBIFG:
            timeoutCounter++;
            break;
        default:
            break;
    }

    LPM3_EXIT;
}

So we're confused with this because it’s really strange. So now I’d like to inform this issue to your side if anyone of you had met with similar issues like this and give us some suggestions. Thanks!

  • Hi Gong Qiqi,

    I'll look into this and get back to you tomorrow.

    Regards,

    Evan

  • Hi Gong Qiqi,

    Can you confirm that TB0IV_TBIFG is firing and that timeoutCounter is getting incremented? The only way I can explain the behavior you have described is that another timer interrupt is firing. If that isn't the issue please provide your timer configuration code so I can try to reproduce on my end.

    Thanks,

    Evan

  • Hi Evan,

    Thank you for your reply. Yes, the TB0IV_TBIFG is firing and the timeoutCounter is getting incremented. Here I'll attach the code. I've tide up the code because the original code is too complicated. The code can also reproduce this issue:

    #define _SYS_CTRL_C
    
    /* ====================================================================================
     * INCLUDE FILES
     ==================================================================================== */
    #include "msp430.h"
    #include "cdefs.h"
    #include "boardDef.h"
    
    /* ====================================================================================
     * LOCAL DEFINITIONS
     ==================================================================================== */
    #define DRV_INT_MASK_WAKEUP_AC      0x0020
    #define DRV_INT_MASK_START_BUTTON   0x0040
    /* RESERVED                         0x0080*/
    
    /* RESERVED                         0x1000*/
    /* RESERVED                         0x2000*/
    #define DRV_INT_MASK_SYS_TIME       0x4000U
    #define DRV_INT_MASK_GLOBAL_INT     0x8000U
    
    #define DRV_INT_MASK_SUPPLY_ALL     0x000FU
    #define DRV_INT_MASK_BOOT           (DRV_INT_MASK_GLOBAL_INT | DRV_INT_MASK_SYS_TIME | DRV_INT_MASK_START_BUTTON)
    #define DRV_INT_MASK_LPM            (DRV_INT_MASK_GLOBAL_INT | DRV_INT_MASK_WAKEUP_AC | DRV_INT_MASK_START_BUTTON)
    #define DRV_INT_MASK_ALL            (DRV_INT_MASK_BOOT | DRV_INT_MASK_WAKEUP_AC)
    
    #define DRV_INT_START_FLAG          P2IV_P2IFG5
    #define DRV_INT_AC_FLAG             P2IV_P2IFG7
    
    #define DEFAULT_AUX_TIMEOUT 3U
    
    /* ====================================================================================
     * LOCAL VARIABLES
     ==================================================================================== */
    static U_WORD timeoutCounter = 0U;
    
    /* ====================================================================================
     * LOCAL FUNCTION PROTOTYPES
     ==================================================================================== */
    #pragma vector = TIMER0_B1_VECTOR
    static __interrupt void timerBInt( void );
    
    static void initSystemTime(void);
    
    void drvIntEnable( U_WORD intFlags )
    {
        U_BYTE ucb0ie, ucb1ie, ucb3ie;
    
        if( (intFlags & DRV_INT_MASK_START_BUTTON) == DRV_INT_MASK_START_BUTTON ) {
            P2IES |= P2_IN_START; /* set flag on transition from high to low */
            P2IFG &= (U_BYTE)~P2_IN_START;
            P2IE |= P2_IN_START;
        }
    
        if( (intFlags & DRV_INT_MASK_WAKEUP_AC) == DRV_INT_MASK_WAKEUP_AC ) {
            P2IES &= (U_BYTE)~P2_IN_AC_WAKEUP; /* set flag on transition from low to high */
            P2IFG &= (U_BYTE)~P2_IN_AC_WAKEUP;
            P2IE |= P2_IN_AC_WAKEUP;
        }
    
    /* global interrupt */
       if( (intFlags & DRV_INT_MASK_GLOBAL_INT) == DRV_INT_MASK_GLOBAL_INT )
       {
           /* workaround for errata USCI39 */
           ucb0ie = UCB0IE;
           UCB0IE &= ~(UCSTTIE | UCNACKIE | UCSTPIE);
           ucb1ie = UCB1IE;
           UCB1IE &= ~(UCSTTIE | UCNACKIE | UCSTPIE);
           ucb3ie = UCB3IE;
           UCB3IE &= ~(UCSTTIE | UCNACKIE | UCSTPIE);
    
           __enable_interrupt();
    
           UCB0IE = ucb0ie;
           UCB1IE = ucb1ie;
           UCB3IE = ucb3ie;
       }
    }
    
    void drvIntDisable( U_WORD intFlags )
    {
        if( (intFlags & DRV_INT_MASK_START_BUTTON) == DRV_INT_MASK_START_BUTTON ) {
            P2IE &= (U_BYTE)~P2_IN_START;
        }
    
        if( (intFlags & DRV_INT_MASK_WAKEUP_AC) == DRV_INT_MASK_WAKEUP_AC ) {
            P2IE &= (U_BYTE)~P2_IN_AC_WAKEUP;
        }
    
        /* global interrupt */
       if( (intFlags & DRV_INT_MASK_GLOBAL_INT) == DRV_INT_MASK_GLOBAL_INT ) {
          __disable_interrupt();
       }
    }
    
    U_WORD drvIntGetEnabled( void )
    {
        U_WORD intFlags = 0U;
    
        if( (P2IE & P2_IN_START) == P2_IN_START ) {
            intFlags |= DRV_INT_MASK_START_BUTTON;
        }
    
        if( (P2IE & P2_IN_AC_WAKEUP) == P2_IN_AC_WAKEUP ) {
            intFlags |= DRV_INT_MASK_WAKEUP_AC;
        }
    
    /* global interrupt */
       if(__get_interrupt_state() != 0){
           intFlags |= DRV_INT_MASK_GLOBAL_INT;
       }
       return intFlags;
    }
    
    void drvIntRestore( U_WORD intFlags )
    {
       drvIntDisable(DRV_INT_MASK_ALL);
       drvIntEnable(intFlags);
    }
    
    void drvTimerBStartAux( void ) {
        timeoutCounter = 0U;
    
        TB0CTL = TBCLR;
        TB0CCR0 = 0U;
        TB0CCTL2 = 0U;
        TB0CCR2 = 0U;
        TB0R = 0U;
        TB0EX0 = TBIDEX_1;  /* divide by 2 */
    
        TB0CTL = MC__CONTINUOUS | ID__8 | TBSSEL__ACLK | TBIE;  /* 293.75 Hz  / (2*8) ~> 18,359375 Hz*/
    }
    
    BOOL drvTimerBIsAuxExpired( void ) {
        if (timeoutCounter >= DEFAULT_AUX_TIMEOUT) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
    
    void drvTimerBStop( void ) {
        TB0EX0 = 0U;
        TB0CTL = TBCLR;
        timeoutCounter = 0U;
    }
    
    int main (void)
    {
        WDTCTL  = WDTPW + WDTHOLD;
    
        initSystemTime();
        drvIntEnable(DRV_INT_MASK_BOOT);
    
       drvTimerBStartAux();
       while( 1 )
       {
          drvIntRestore(DRV_INT_MASK_LPM);
    
          LPM3;
          __no_operation();
    
          drvIntRestore(DRV_INT_MASK_BOOT);
        }
    }
    
    static void initSystemTime(void) {
        P11OUT |= BIT2; /* SMCLK output on Pin 11.2 */
        P11SEL |= BIT2;
    
        /* MCLK = f_dcoclk = 4194304 Hz
         * f_dcoclk = D x (N+1) x (f_fllrefclk / n)
         * with D = 4 (FLLD__4)
         * N = 31 (FLLN)
         * f_fllrefclk = 32768 Hz
         * n = 1 (FLLREFDIV__1)
         * */
        UCSCTL1 = DCORSEL_2; /* select DCO range of 0.75 MHz to 7.68 MHz */
        UCSCTL2 = FLLD__4 | 31; /* CLOCKDIV 4, FLLN 31 */
        UCSCTL3 = SELREF__REFOCLK | FLLREFDIV__1; /* select 32768 ref clk, divide by 1 */
        /* source SMCLK from DCOCLKDIV, MCLK from DCO, ACLK from VLO (9.4Hz typical) */
        UCSCTL4 = SELS__DCOCLKDIV | SELM__DCOCLK | SELA__VLOCLK;
        UCSCTL5 = DIVM__1 | DIVA__32 | DIVS__1 ;
    
        do
        {
            UCSCTL7 &= (U_BYTE)~(XT2OFFG | XT1LFOFFG | DCOFFG);
            SFRIFG1 &= (U_BYTE)~OFIFG;
            for ( U_BYTE i = 80; i > 0; i--) {
                __no_operation();
            }
        } while ((UCSCTL7 & DCOFFG) == DCOFFG);
    }
    
    /*****************************************************************************
     * timerBInt - interrupt handler for timer b
     *
     * Aux interrupt is 3569.6s, about 59.5 minutes.
     * The VLO frequency may range from 6..14 kHz, while its typical frequency
     * is 9.4 kHz. Additionally, a temperature drift of 0.5 % / deg C and a
     * supply voltage drift of 4% / V may apply.
     *
     ****************************************************************************/
    static __interrupt void timerBInt( void ) {
        switch(TB0IV) {
            case TB0IV_TBIFG:
                timeoutCounter++;
                break;
            default:
                break;
        }
    
        if(timeoutCounter >= DEFAULT_AUX_TIMEOUT) {
            LPM3_EXIT;
        }
    }

    #ifndef UTILS_CDEFS_H_
    #define UTILS_CDEFS_H_
    
    /* ====================================================================================
     * GLOBAL DEFINITIONS
     ==================================================================================== */
    
    #define TRUE    ((BOOL)(1==1))  /* Boolean true  */
    #define FALSE   ((BOOL)(0==1))  /* Boolean false */
    
    #define OK      ( 0 )       /* regular return value */
    #define ERROR   ( -1 )
    
    #ifndef NULL
      #define NULL          ((void *) 0)
    #endif
    
    typedef enum {
        DISABLE = 0U,
        ENABLE = 1U,
    } EnableState;
    
    typedef enum {
        HIGH = 1,
        LOW = 0,
    } PinLevel;
    
    #define BYTE_ERR    0xFF
    #define WORD_ERR    0xFFFF
    #define WORD_MAX    0x7FFF
    
    /* ====================================================================================
     * GLOBAL TYPES
     ==================================================================================== */
    
    typedef            signed    char      BOOL ;  /* 1 bit boolean */
    typedef            signed    char     STATUS;
    typedef            signed    char      BYTE ;  /* 8 bit signed integer data type */
    typedef            signed    short     WORD ;  /* 16 bit signed integer data type */
    typedef            signed    long     DWORD ;  /* 32 bit signed integer data type */
    
    typedef            unsigned  char    U_BYTE ;  /* 8 bit unsigned integer data type */
    typedef            unsigned  short   U_WORD ;  /* 16 bit unsigned integer data type*/
    typedef            unsigned  long   U_DWORD ;  /* 32 bit unsigned integer data type*/
    
    typedef                      float    FLOAT ;  /* 32 bit floating point value */
    typedef                      double  DOUBLE ;  /* 64 bit floating point value */
    
    /* pointer definitions used for test compatibility */
    
    #ifndef pREG8
    #define            pREG8    U_BYTE*
    #endif
    
    #ifndef pREG16
    #define            pREG16   U_WORD*
    #endif
    
    
    #endif

    #ifndef UTILS_BOARDDEF_H_
    #define UTILS_BOARDDEF_H_
    
    /* =================================================================
     * GLOBAL INCLUDES
     ================================================================= */
    #include "msp430.h"
    
    /* =================================================================
     * GLOBAL TYPES
     ================================================================= */
    
    
    /* =================================================================
     * GLOBAL DEFINITIONS
     ================================================================= */
    
    
    /* =================================================================
     * PIN DEFINITIONS
     ================================================================= */
    
    #define P2_OUT_NOT_USED             (BIT4 | BIT6)
    #define P2_OUT_D_ACT                (BIT0)
    #define P2_IN_FAN_SPEED             (BIT1)
    #define P2_IN_BAT1_SOURCE_HIGH      (BIT2)
    #define P2_IN_BAT2_SOURCE_HIGH      (BIT3)
    #define P2_IN_START                 (BIT5)
    #define P2_IN_AC_WAKEUP             (BIT7)
    
    #endif /* UTILS_BOARDDEF_H_ */

  • Thanks for sharing. I'll try to reproduce later today and get back to you.

  • Hi Gong Qiqi,

    Apologies it is going to be more challenging than I thought to reproduce your code since I'm working with slightly different hardware.

    In the meantime, can you try something for me. Can you place a trap in the ISR (see below) to confirm all the surrounding code is working as expected?

    static __interrupt void timerBInt( void ) {
        switch(TB0IV) {
            case TB0IV_TBIFG:
                timeoutCounter++;
                break;
            default:
                break;
        }

        if(timeoutCounter >= DEFAULT_AUX_TIMEOUT) {

            while(1);
            //LPM3_EXIT;
        }
    }

    Thanks,

    Evan

  • I put together an even simpler version of your code to test on my end. I wasn't able to reproduce your problem. When ii==3, the device exits LPM3 and lands on the while(1) in the main().

    Can you run this code on your end and see if you have the same behavior?

    #include <msp430.h>
    
    unsigned int ii = 0;
    
    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    
    
      UCSCTL1 = DCORSEL_2; /* select DCO range of 0.75 MHz to 7.68 MHz */
      UCSCTL2 = FLLD__4 | 31; /* CLOCKDIV 4, FLLN 31 */
      UCSCTL3 = SELREF__REFOCLK | FLLREFDIV__1; /* select 32768 ref clk, divide by 1 */
      /* source SMCLK from DCOCLKDIV, MCLK from DCO, ACLK from VLO (9.4Hz typical) */
      UCSCTL4 = SELS__DCOCLKDIV | SELM__DCOCLK | SELA__VLOCLK;
      //UCSCTL5 = DIVM__1 | DIVA__32 | DIVS__1 ;
      UCSCTL5 = DIVM__1 | DIVA__2 | DIVS__1 ;
    
      do
      {
          UCSCTL7 &= ~(XT2OFFG | XT1LFOFFG | DCOFFG);
          SFRIFG1 &= ~OFIFG;
          int i;
          for ( i = 80; i > 0; i--) {
              __no_operation();
          }
      } while ((UCSCTL7 & DCOFFG) == DCOFFG);
    
    
      P5DIR |= BIT1;                            // P1.0 output
      TB0CTL = TBCLR;
      TB0CCR0 = 0U;
      TB0CCTL2 = 0U;
      TB0CCR2 = 0U;
      TB0R = 0U;
      TB0EX0 = TBIDEX_1;  /* divide by 2 */
    
      //TB0CTL = MC__CONTINUOUS | ID__8 | TBSSEL__ACLK | TBIE;  /* 293.75 Hz  / (2*8) ~> 18,359375 Hz*/
      TB0CTL = MC__CONTINUOUS | ID__1 | TBSSEL__ACLK | TBIE;
    
      __bis_SR_register(GIE);
    
      LPM3;
      __no_operation();
    
      while(1); // recovered from LPM
    }
    
    // Timer B0 interrupt service routine
    #pragma vector=TIMER0_B1_VECTOR
    __interrupt void TIMER0_B1_VECTOR_ISR (void)
    {
      switch(__even_in_range(TBIV,TB0IV_TB0IFG)){
      case TB0IV_TB0CCR1: while(1); break;
      case TB0IV_TB0CCR2: while(1); break;
      case TB0IV_TB0CCR3: while(1); break;
      case TB0IV_TB0CCR4: while(1); break;
      case TB0IV_TB0CCR5: while(1); break;
      case TB0IV_TB0CCR6: while(1); break;
      case TB0IV_TB0IFG :
        ii++;
        P5OUT ^= BIT1;
        break;
      default: while(1); break;
    
      }
    
      if (ii == 3) {
        LPM3_EXIT;
      }
    
    }
    
    

  • The code is rather convoluted so it is not apparent to me how timeoutCounter gets reset after waking up from LPM3. I only see it being cleared in the timer start and stop code. Without being reset, it would be greater than 3 and cause a wakeup most every interrupt.

    I would structure this a bit differently with the timer ISR just incrementing the counter and exiting LPM3. The foreground code would look at the counter and decide what to do: go back to sleep, or reset the counter and execute the time out tasks.

  • Hi Evan,

    I've tested with the code you provided. The behavior is just as same as my code as I described previously. You can see some detail debug information here:

    Debug screen

    If it's OK on your side, is it possible the issue is caused by IAR or my debugger? I'm using IAR MSP430 7.20.1 and the debugger is TI MSP430 USB-Debug-Interface MSP-FET430UIF.

    And also I added the remaining include files' code in my previous reply.

    Best regards,

    Gong Qiqi

  • Hi David,

    Thank you for your concerning on it!

    In fact, the code I showed is not the original code. I tided up the code and removed all the unnecessary files and lines. So this code is just to reproduce this issue. To make things simple, you can also try with Evan's code. I've reproduced this issue on his code.

    Thanks a lot!

    Qiqi

  • Hi Gong Qiqi,

    Could you try my example in CCS and see if it has the same behavior?

    Thanks,

    Evan

  • Hi Evan,

    I've tried your code in CCS and the same behavior occurred. And it seems it will occur in the case that we pause the running program in LPM3 mode in debug. If there're any pause operations after the 1st timer 0 B1 ISR had occurred, this issue will occur.

    Thanks,

    Gong Qiqi

  • Hi Gong Qiqi,

    Thanks for the follow-up. I'm also able to create some bad behavior in the example code I gave you by repeatedly pausing and resuming the debugger during the operation of the program. I don't understand the root cause exactly, but the debugger is invasive and can create unexpected behavior.

    Regards,

    Evan

**Attention** This is a public forum