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.

MSP430FR6989: Timer delay function to enter LMP3 for one hour then exit

Part Number: MSP430FR6989

I am trying to make a delay function that will enter LMP3 for one hour, then exit LMP3 using the TimerA1 interrupt. No other operations should occur during this time, it is only a 1 hour delay with power saving utility. I have modified the msp430fr69xx_ta1_06.c example code. Here is my current code:

#include <msp430.h>

void delay_hour(float hour)
{
        TA1CCR0 = (int)(16524 * hour); //Set count target, up to 65535, or 3.96 hours
        TA1CTL |= MC__UP; //Set count mode to up (Changed from TA1CTL = MC__UP;)
        __bis_SR_register(LPM3_bits | GIE); //Enter LPM3
        //while((TA1CTL & TAIFG) == 0) { __no_operation(); } //Wait until count target is reached and interrupt flag turns on
}

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

  // Configure GPIO
  P1DIR |= BIT0;                            // LED interrupt
  P1OUT |= BIT0;
  PJSEL0 |= BIT4 | BIT5;

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  // Clock setup
  CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
  CSCTL1 = DCOFSEL_0;                       // Set DCO to 1MHz
  CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; // Set ACLK = VLO; MCLK = DCO
  CSCTL3 = DIVA__32 | DIVS__1 | DIVM__1;     // Set S and M dividers to 1, ACLK to 239.75Hz
  CSCTL0_H = 0;                             // Lock CS registers

  TA1CCTL0 = CCIE;                          // TACCR0 interrupt enabled
  TA1CCR0 = 0;                              // Set count target to 0 by default
  // Set timer clock speed to 4.5898 ticks/s, or 16524 ticks/hour
  TA1CTL = TASSEL__ACLK | MC__UP | ID__8; //Set clock source to ACLK, the count mode to stop, and the clock divider to 8
  TA1EX0 = TAIDEX_7; //Set expansion clock divider to 8
  __bis_SR_register(LPM3_bits | GIE);       // Enter LPM3 w/ interrupt

  while(1)
  {
      delay_hour(0.000278); //Delay for one second (0.000278hr or 4.59 ticks) for fast toggling of LED (testing purposes)
      P1OUT ^= BIT0; //Toggle LED
  }
}

// Timer A1 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER1_A0_VECTOR
__interrupt void Timer1_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER1_A0_VECTOR))) Timer1_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    TA1CTL |= MC__STOP; //  Stop timer to prevent repeat counting (Changed from TA1CTL = MC__STOP;)
    __bic_SR_register_on_exit(LPM3_bits);
}

I am running this on the MSP430FR6989 Launchpad. The P1.0 led turns on, and does not turn off. I know that the setup of ACLK with VLOCLK is working properly. I tested it by making this script which toggles the LED on and off at an approximate rate of once per second, shown here:

#include <msp430.h>

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

  // Configure GPIO
  P1DIR |= BIT0;                            // LED interrupt
  P1OUT |= BIT0;
  PJSEL0 |= BIT4 | BIT5;

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  // Clock system setup
  CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
  CSCTL1 = DCOFSEL_0;                       // Set DCO to 1MHz
  CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; // Set ACLK = VLO; MCLK = DCO
  CSCTL3 = DIVA__32 | DIVS__1 | DIVM__1;     // Set all dividers to 1
  CSCTL0_H = 0;                             // Lock CS registers

  TA1CCTL0 = CCIE;                          // TACCR0 interrupt enabled
  TA1CCR0 = 2;                              // 2 ticks or 0.44s
  TA1CTL = TASSEL__ACLK | MC__UP | ID__8; //Set clock source to ACLK, the count mode to stop, and the clock divider to 8
  TA1EX0 = TAIDEX_7; //Set expansion clock divider to 8

  __bis_SR_register(LPM3_bits | GIE);       // Enter LPM3 w/ interrupt
}

// Timer A1 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER1_A0_VECTOR
__interrupt void Timer1_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER1_A0_VECTOR))) Timer1_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  P1OUT ^= BIT0;
}

Because the clock setup and timer setup are correct, I assume my error lies within the delay function or the interrupt. The delay function sets the count and starts the timer counting up before entering LPM3. Once the count is reached, the interrupt should set the counter to stop mode to avoid the count repeating. It should then exit LPM3. 

I am using a while loop in my main function. The delay function should enter LPM3, reach the timer target count in 1s, then exit LPM3. I am assuming that because the CPU is off in LPM3, the main function cannot continue to the LED toggle until LPM3 is exited. Therefore, once the timer interrupt exits LPM3, the LED will toggle and the while loop will return to the delay function. Is this assumption wrong? If so, where does the main function resume after LPM3 is exited? If it is restarting at the beginning of the main function every time it leaves LPM3, I can understand that it will never reach the LED toggle. In that case, how can I modify my delay function to have the desired result?

  • Typo alert:

    > TA1CTL = MC__UP; //Set count mode to up

    This sets TASSEL=0 (as well as ID=0) so it effectively stops it. Try:

     > TA1CTL |= MC__UP; //Set count mode to up

  • Thank you for spotting that. I changed it in my code. I also tried changing TA1CTL = MC__STOP in the interrupt to an |= as well. Unfortunately, the LED still does not toggle. 

  • I removed this line (just before the while(1)):

    >  __bis_SR_register(LPM3_bits | GIE); // Enter LPM3 w/ interrupt

    and I get a 1-second toggle (0.5Hz). This will never finish since TA1CCR0==0.

    Unsolicited: 

    > TA1CTL |= MC__STOP; // Stop timer to prevent repeat counting

    This doesn't stop the timer since MC__STOP==0. Try:

    >  TA1CTL &= ~MC_3; // Stop timer to prevent repeat counting

    [Note to archaeologists: The code in the original post changed.]

  • This solved everything! Thank you very much for your patience and your help. I see now that the LPM3 entrance before the while loop and the count target of 0 stopped the interrupt from triggering and exiting LPM3, effectively locking it into LPM3. The use of MC_3 is also clear.

    In the future, should I not change the original post at all so that the solutions offered in the replies are clear to later readers? I am not sure what is considered good practice in this case.

  • It's a judgment call -- there's a case for editing-in-place. If I edit a post (even to fix a typo) I just append "[Edit: Fixed typo]" or some such which at least tells people they're not hallucinating.

**Attention** This is a public forum