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.

MSP430FR2311: Programatically changing power modes

Part Number: MSP430FR2311

I have two projects right how that are using the MSP430 low power modes.  I understand the need to put the part to sleep, but don't understand the need to clear the bit after there has been an interrupt. 

I've noticed a few things so far.  I can only use LPM0 if I want to receive UART messages at 115200 because the low frequency oscillator is too slow.  I also have to purposely exit/clear the LPM0 bit in the I2C interrupt routine in order to process them in main.

So I'm really unsure if I have all my code correct.  I was hoping someone could review and give any suggestions.

Project 1

// Wake up to process data received over the UART as it comes in.
// Wake when I2C slave commands come in and response appropriately.
// Also noticed I had to clear the lpm bit in order to wake up from I2C transactions

int main(void)
{
    //Stop WDT
    WDT_A_hold(WDT_A_BASE);
	
	initClockTo16MHz();
    initUART(0);
    initGPIO();
    initI2C();
	
	while(1)
	{
	
		if(rxFlag == 1)
		{
			processUART();
		}
		
		__bis_SR_register(LPM0_bits + GIE);
	}
}


// UART Interrupt
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG))
    {
        case USCI_NONE: break;
        case USCI_UART_UCRXIFG:

              UCA0IFG &=~ UCRXIFG;            // Clear interrupt
              rxData = UCA0RXBUF;
              rxFlag =  1;
              __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 on reti
              break;
        case USCI_UART_UCTXIFG: break;
        case USCI_UART_UCSTTIFG: break;
        case USCI_UART_UCTXCPTIFG: break;
    }
}


//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  //Must read from UCB0RXBUF
  uint8_t rx_val = 0;
  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
  {
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
      break;
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:
        UCB0IFG &= ~(UCTXIFG0);
        break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
        rx_val = UCB0RXBUF;
        switch (SlaveMode)
        {
          case (RX_REG_ADDRESS_MODE):
              ReceiveRegAddr = rx_val;
              I2C_Slave_ProcessCMD(ReceiveRegAddr);
              break;
          case (RX_DATA_MODE):
              ReceiveBuffer[ReceiveIndex++] = rx_val;
              RXByteCtr--;

              if (RXByteCtr == 0)
              {
                  //Done Receiving MSG
                  SlaveMode = RX_REG_ADDRESS_MODE;
                  UCB0IE &= ~(UCTXIE);
                  UCB0IE |= UCRXIE;                          // Enable RX interrupt
                  I2C_Slave_TransactionDone(ReceiveRegAddr);
				  
				  
                  __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 on reti
              }
              break;
          default:
              __no_operation();
              break;
        }
        break;
    case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
              UCB0TXBUF = TransmitBuffer[TransmitIndex++];
              TXByteCtr--;
              if (TXByteCtr == 0)
              {
                  //Done Transmitting MSG
                  SlaveMode = TX_REG_ADDRESS_MODE;
                  UCB0IE &= ~(UCTXIE);
                  UCB0IE |= UCRXIE;                          // Enable RX interrupt
                  I2C_Slave_TransactionDone(ReceiveRegAddr);
              }
        break;                      // Interrupt Vector: I2C Mode: UCTXIFG
    default: break;
  }
}

Project 2

int main(void) {
    //Stop WDT
    WDT_A_hold(WDT_A_BASE);

    initClockTo16MHz();
    initUART();

/* Initialize peripherals */
    initGPIO();
    initPWM();
    initI2C();
    initRTC();
	
	while(1)
	{
		currentSample();
    	led_current_reading = ADC_Conversion_Result;
	
		tempSample();
		current_temp_reading = ADC_Conversion_Result;
		
		doOtherStuff();
	
	
	        //LPM3, RTC or I2C will force exit
        __bis_SR_register(LPM3_bits + GIE);
	}
	
}



void currentSample(void)
{
    enable_ADC10(ADCINCH_0);
    __delay_cycles(15);

    //Enable and Start the conversion
    //in Single-Channel, Single Conversion Mode
    ADC_startConversion(ADC_BASE, ADC_SINGLECHANNEL);

    //LPM3, ADC conversion complete will force exit
    __bis_SR_register(LPM0_bits + GIE);

    disable_ADC10();
}

void tempSample(void)
{
    enable_ADC10(ADCINCH_12);

    __delay_cycles(15);

    //Enable and Start the conversion
    //in Single-Channel, Single Conversion Mode
    ADC_startConversion(ADC_BASE, ADC_SINGLECHANNEL);

    //LPM3, ADC conversion complete will force exit
    __bis_SR_register(LPM0_bits + GIE);

    disable_ADC10();
}


void disable_ADC10(void){
	ADC_disableConversions(ADC_BASE,0);
	ADC_disable(ADC_BASE);
}

void enable_ADC10(uint8_t adc_channel){
	//Initialize the ADC Module

	ADC_init(ADC_BASE, ADC_SAMPLEHOLDSOURCE_SC, ADC_CLOCKSOURCE_ADCOSC, ADC_CLOCKDIVIDER_1);

	ADC_enable(ADC_BASE);

	ADC_setupSamplingTimer(ADC_BASE, ADC_CYCLEHOLD_1024_CYCLES, ADC_MULTIPLESAMPLESDISABLE);

	if(adc_channel == ADCINCH_12)
	{
	    ADC_configureMemory(ADC_BASE, adc_channel, ADC_VREFPOS_INT, ADC_VREFNEG_AVSS);
	}
	else
	{
	    ADC_configureMemory(ADC_BASE, adc_channel, ADC_VREFPOS_AVCC, ADC_VREFNEG_AVSS);  // channel mis-map for A1 in driverlib?
	}

	ADC_clearInterrupt(ADC_BASE, ADC_COMPLETED_INTERRUPT);

	//Enable the Memory Buffer Interrupt
	ADC_enableInterrupt(ADC_BASE, ADC_COMPLETED_INTERRUPT);
}



// ADC interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC_VECTOR
__interrupt void ADC_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
#else
#error Compiler not supported!
#endif
{
    ADC_Conversion_Result = ADCMEM0;  

	__bic_SR_register_on_exit(LPM3_bits);              // Sleep Timer Exits LPM3
}


// RTC interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=RTC_VECTOR
__interrupt void RTC_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(RTC_VECTOR))) RTC_ISR (void)
#else
#error Compiler not supported!
#endif
{
    __bic_SR_register_on_exit(LPM3_bits);              // Timer Exits LPM3

   switch(__even_in_range(RTCIV,RTCIV_RTCIF))

   {

       case  RTCIV_NONE:   break;          // No interrupt

       case  RTCIV_RTCIF:                  // RTC Overflow
           P2OUT ^= BIT3;
           break;

       default: break;

   }

}


//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  //Must read from UCB0RXBUF
  uint8_t rx_val = 0;
  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
  {
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
      break;
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:
        UCB0IFG &= ~(UCTXIFG0);
        break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
        rx_val = UCB0RXBUF;
        switch (SlaveMode)
        {
          case (RX_REG_ADDRESS_MODE):
              ReceiveRegAddr = rx_val;
              I2C_Slave_ProcessCMD(ReceiveRegAddr);     /
              break;
          case (RX_DATA_MODE):
              ReceiveBuffer[ReceiveIndex++] = rx_val;
              RXByteCtr--;

              if (RXByteCtr == 0)
              {
                  //Done Receiving MSG
                  SlaveMode = RX_REG_ADDRESS_MODE;
                  UCB0IE &= ~(UCTXIE);
                  UCB0IE |= UCRXIE;                          // Enable RX interrupt
                  I2C_Slave_TransactionDone(ReceiveRegAddr);  

                  // SHOULD WE DO THIS HERE?
                  __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 on reti
              }
              break;
          default:
              __no_operation();
              break;
        }
        break;
    case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
              UCB0TXBUF = TransmitBuffer[TransmitIndex++];
              TXByteCtr--;
              if (TXByteCtr == 0)
              {
                  //Done Transmitting MSG
                  SlaveMode = TX_REG_ADDRESS_MODE;
                  UCB0IE &= ~(UCTXIE);
                  UCB0IE |= UCRXIE;                          // Enable RX interrupt
                  I2C_Slave_TransactionDone(ReceiveRegAddr);
              }
        break;                      // Interrupt Vector: I2C Mode: UCTXIFG
    default: break;
  }
}

  • You seem to have the basic idea. In some CPUs, any interrupt wakes up main(), in the MSP430 the ISR decides whether this happens.

    You're now into the next step -- system design.

    1) As soon as you have multiple wakeup sources (in your case ADC+RTC+I2C), you need to be careful about false wakeups. If the RTC or I2C issues a wakeup while you're waiting for the ADC, this code will assume the ADC is finished. A simple first approximation might be:

    adc_done = 0;
    ADC_startConversion();
    while (!adc_done) LPM0;  // adc_done is set to 1 in the ISR

    2) There's a race in using LPM: If the event (wakeup from ISR) occurs when main() isn't in LPM, no record is kept. If, e.g. the event happens during the last statement before going into LPM, there will be no wakeup. This leads to something like [untested, but about right I think]:

    while (1) {                 // inverted loop
       __disable_interrupt();   // For checking the flags
       if (RTC_done || I2C_done) break;  // Must have happened earlier
       __bis_SR_register(LPM0_bits | GIE);   // Re-enable and sleep
    } // end while(1)
    __enable_interrupt();       // Back to normal
    
    

  • Bruce,

    Thank you for the information.  I understand what you mean in #1, but I don't see what you mean in #2.  Could you elaborate further?

  • Consider what happens if an I2C interrupt/wakeup happens while DoOtherStuff() is executing. This code will then go into LPM but the wakeup will be long gone, so it will stall in LPM.

    In this code, one expects that an RTC interrupt will eventually issue another wakeup, but there will be a delay in processing whatever the I2C Master was asking for. In the absence of a periodic wakeup (viz. RTC), it will stall forever.

  • Would it be better to just re-enable interrupts as soon as I start executing in main()?

  • The goal should be to keep interrupts enabled (GIE) as much as possible. Usually this takes the form of an "__enable_interrupt();" after all the "init()" calls (before the while loop). In this code, GIE will be set (forever) after the first call to currentSample(), though this is a bit non-obvious to the reader.

  • Ok, I think I see what you are getting at.  If my interrupt requires some additional work to be done outside the ISR in main() and I'm already awake, there is the possibility that I don't run the main() code for the ISR until the next interrupt comes along, which could be an issue.

    So regarding GIE being enabled forever, I didn't realized this, but I guess it is OK.  So what am I even doing with __bic_SR_register_on_exit(LPM3_bits);  or __bic_SR_register_on_exit(LPM0_bits);  inside the ISR?  Am I just telling it that it is ok to jump to main().  Otherwise if I didn't do this, the CPU would just go back to LPM?

  • The "_on_exit" formulations set CPUOFF=0 in the Status Register as seen by main (supposing that it has set CPUOFF=1), so main starts up again. If you don't do this, main will still see CPUOFF=1 and not execute anything.

    [I have to get back to my Day Job, so:] This is probably a good time to re-read User Guide (SLAU445I) Sections 1.3.4.1-2 and the first few paragraphs of Section 1.4 -- now that you know the general idea, knowing the mechanics will be more useful.

  • Hi Bruce,

    Thanks again for all your help.  I ended up doing this, which I things resolves the issue for Project 1.  Does this look correct to you?  Basically I set RxFlag  to a 1 when there is a UART byte to be received and then I2Cflag to a 1 when there is an i2c message that changed a register.

        while(1){
            
            if(i2cFlag != 0)
            {
                i2cFlag = 0;
            }
    
            if(cntl == 0x00)  // i2c changed power control register
            {
                powerControl(0);
            }
            else if(cntl == 0x01)  // i2c changed power control register
            {
                powerControl(1);
            }
    
            if(rxFlag != 0)
            {
                rxFlag = 0;
                processUART();
            }
            
            //sleep if no flags
            __disable_interrupt(); 
            if(rxFlag == 0 && i2cFlag == 0)
            {
                __bis_SR_register(LPM0_bits + GIE);
            }
            else
            {
                __enable_interrupt(); 
            }
    }

  • Looks fine to me. 

    Be sure to declare all the "Flag" variables as "volatile", so they don't disappear when you turn on the optimizer.

  • Hi Bruce, Thanks for you supporting this thread!

  • In case the question has been solved, I will close this thread. Thanks

**Attention** This is a public forum