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.

wake up from LPM0

Other Parts Discussed in Thread: LMP91000

hopefully a quick question, in my code I can enter LPM0 successfully, have an interrupt serviced & use it to exit LPM0 when "__bis_SR_register(LPM0_bits + GIE);" is called from the bottom of the while(1) in main.c.


I would also like to enter LPM0 in other parts of the code that aren't in main.c or the while(1) & it looks like I can enter LPM0 ok but my interrupts although they're serviced they don't seem to be able leave LPM0 (assert "__bic_SR_register_on_exit(LPM0_bits);" correctly)

Is there some limitation on where you can enter LPM0?

  • _bic_SR_register / _bis_SR_register directly change the status register. This has immediate effect. If you se tthe CPUOFF bit (which is part of all LPMx_bits), MCLK will go off immediately.

    However, when entering an ISR, the status register (and with it the LPM bits) is saved on stack and the LPM bits (and the GIE bit) are cleared. So MCLK starts agian and the ISR executes.

    Now when the ISR returns, it loads the old SR value form stack and the old LPM bit settings are immediately in effect again. If you want to change the LPM state after the ISR to somethign different than befor eth eISR, you'll have to change the settings on teh stack. This is what the _on_exit intrinsics do (only the compiler knows where on stack the data is, as it depends on the ISR code generated).

    Inside an ISR, use the _on_exit intrinsics, ot change the behaviour/LPM after the ISR. Outide an ISR use teh other two for immediate entering LPM (you obviously cannot leave LPM outside an ISR, as the CPU is not running to execute the code, and entering LPM inside an ISR would either lock your system totally or cause a stack verflow soon)

  • Yeah that's what I thought, but here's what I'm seeing. When my code looks like:

    
    

    void main (void)

    {

    initStuff();

    while(1)

    {

    myReallyBigAndComplexFunction();

    __bis_SR_register(LPM0_bits + GIE);

    }

    }

    __interrupt void anIsr(void)

    {

    __bic_SR_register_on_ext(LPM0_bits );

    }

    everything works ok & as you described above.

    However when __bis_SR_register(LPM0_bits + GIE); is called from deep inside myReallyBigAndComplexFunction();  with interrupts still enabled the code doesn't leave LPM0

  • To answer your question: The code can sleep (LPMx) and be awakened at any call level; I do it all the time.

    Stating the obvious here: The LPMx_EXIT "wakeup" is only effective if the foreground is sleeping at the moment of the interrupt. Reversing perspective: If the foreground goes to sleep, it needs to know that there is an interrupt coming (sooner or later) to wake it up.

    An unguarded "LPMx" as in your example can be susceptible to a race where it goes to sleep just after the interrupt that was supposed to wake it. That's why you see code that looks like:

        __disable_interrupt();
        if (!work_to_do)

            LPM0;                                           // Stop CPU and enable interrupts

    A system with a periodic interrupt, e.g. timer tick, can avoid, or at least mask, a missed-interrupt hang. But I've seen a fair amount of code that appears to just be lucky.

  • Bruce already pointed out the possible reason: a race condition.

    Note that your ISR, when ending LPM, lets GIE set. So on the next while loop, it may be that you enable the interrupt source, but before you actually enter LPM, the interrupt is triggered, the LPM is ended (which is a NOP then, as main wasn't in LPM at all) and when finally entering LPM, the ISR is no longer 'armed', and the waking call never comes.

  • in the end it looked to work when I gave the code that was deep inside the code something to "bite" on:

    i.e. I went from:

                while(busy)
                {       
                    __bis_SR_register(LPM0_bits + gie);                 // Restore original GIE state
                }

    to:

                while(dmaBusy)
                {        
                    __no_operation();
                    __bis_SR_register(LPM0_bits + gie);                 // Restore original GIE state
                }



  • I don't know why this should make a difference. However, the whole approach is not how the LPM should be used.

    Instead of looping anfd hgoing into LPM is still busy, teh ISR shouldn't exit LPM at all if still busy, So you enter LPM only once and exit is inside the ISR when the busy state has ended (and not always).

    BTW: you shouldn't use the arithetic '+' operator when combining bits. use the bit operator '|' instead.
    because GIE+GIE results in something completely different while GIE|GIE results in GIE again. Imagine what happens when for some reason, GIE is already part of the LPM0_bits macro. Same for any othe roperation where you add bits or bitfields with individual meanings and not arithmetic values.

  • Regarding this topic, I have read various posts about the LMP0, but Im still having some trouble. I am trying to change  a reference register of an LMP91000 through I2C using the msp4305529. I initializes all peripherals and modules then I set the msp4305529 to LMP0.

    In my ISR  I take ADC readings, and after 8 readings I take the average and store it in an array. I want to acquire on "averaged reading" per bias of the LMP91000 ( the bias is the value I'm changing through I2C). After acquiring this 8th reading I want to exit LMP0, so that I can increase my counter, "count". I want to perform "X" amount of readings and then use a whole different array of values to change my bias.

    #include <stdint.h>
    #include "TI_LMP91000.h"
    #include "TI_LMP91000_register_settings.h"
    #include "TI_MSP430.h"
    #include "TI_MSP430_hardware_board.h"
    #include "TI_MSP430_i2c.h"
    #include <intrinsics.h>
    
    /*----------------------------------------------------------------------------*/
    void ADC12_Init(void);                                                         // To init MSP430F5528 ADC12 & Start Conversion
    void ADC10_Init(void);                                                         // To init MSP430F6736 ADC10 & Start Conversion
    /*----------------------------------------------------------------------------*/
    #define NUM_OF_RESULTS  8                                                      // Number of temp sensor samples to take
    #define SCALE_FACTOR    3                                                      // For averaging converted samples
    #define TEMP_EQN_OFFSET 1562.2                                                 // LMP91000 DS pg 12: V=(-8.16mV/°C)*T+1562.2mV
    #define TEMP_EQN_SLOPE  8.16                                                   // Linear approximation over temp range 20C to 50C
    #define ADC12_RATIO     0.61035                                                // 2500/4096 (2.5V reference & 12bit converter)
    #define ADC10_RATIO     1.46484375                                             // 1500/1024 (1.5V reference & 10bit converter)
    
    #define ADC12_MODULE   2
    #define ADC10_MODULE   1
    #define ONBOARD_ADC_MODULE     ADC12_MODULE                                    // ADC module available in selected MSP430
                                                                                   // choose between ADC12_MODULE & ADC10_MODULE
    const uint8_t bias [] = {0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC8, 0xC7, 0xC6, 0xC5, 0xC4, 0xC3, 0xC2, 0xC1, 0xC0, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
    		  0xD9, 0xDA, 0xDB, 0xDC, 0xDD};
    const uint8_t rbias [] = {0xDD, 0xDC, 0xDB, 0xDA, 0xD9, 0xD8, 0xD7, 0xD6, 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xD0, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
    		  0xC9, 0xCA, 0xCB, 0xCC, 0xCD};
    float points [56];
    volatile uint8_t count;
    //******************************************************************************
    void main(void)
    {
    
      uint8_t status = TI_LMP91000_NOT_READY;
      uint8_t read_val[2];                                                         // buffer to store register values
    
      WDTCTL = WDTPW + WDTHOLD;                                                    // Stop watchdog timer
    
      TI_LMP91000_LED_PxOUT |= TI_LMP91000_LED_PIN;                                // Set LED ON
      TI_LMP91000_LED_PxDIR |= TI_LMP91000_LED_PIN;                                // Set pin direction is output
    
      I2CSetup(LMP91000_I2C_Address);                                              // Initialize I2C module
    
      TI_LMP91000_MENB_PxOUT &= ~TI_LMP91000_MENB_PIN;                             // Enable \MENB Pin
      TI_LMP91000_MENB_PxDIR |= TI_LMP91000_MENB_PIN;                              // Set pin direction is output
    
      while (status == TI_LMP91000_NOT_READY)
          status = LMP91000_I2CReadReg(TI_LMP91000_STATUS_REG);                      // Read device ready status
    
      for (count = 0; count < 28; count++){
    
    
      LMP91000_I2CWriteReg(TI_LMP91000_LOCK_REG, TI_LMP91000_WRITE_UNLOCK);        // unlock the registers for write
    
      LMP91000_I2CWriteReg(TI_LMP91000_TIACN_REG, TI_LMP91000_TIACN_REG_VALUE);    // Modify TIA control register
      LMP91000_I2CWriteReg(TI_LMP91000_REFCN_REG, bias[count]);    // Modify REF control register
    
      read_val[0]   = LMP91000_I2CReadReg(TI_LMP91000_TIACN_REG);                  // Read to confirm register is modified
      read_val[1]   = LMP91000_I2CReadReg(TI_LMP91000_REFCN_REG);                  // Read to confirm register is modified
    
      if ((read_val[0] != TI_LMP91000_TIACN_REG_VALUE) ||
          (read_val[1] != TI_LMP91000_REFCN_REG_VALUE))                            // test values took effect
        while (1);                                                                 // otherwise error
    
      LMP91000_I2CWriteReg(TI_LMP91000_LOCK_REG, TI_LMP91000_WRITE_LOCK);          // lock the registers
      LMP91000_I2CWriteReg(TI_LMP91000_MODECN_REG, TI_LMP91000_MODECN_REG_VALUE);  // 3-lead amperometric cell
    
      ADC12_Init();                                                                // Initialize MSP430F5528 ADC12 & Start Conversion
    
      __disable_interrupt();														// To avoid a race condition
      __bis_SR_register(LPM0_bits | GIE);                                          // Enter LPM0, Enable interrupts
    
      count++;
    }
    }
    
    
    
    void ADC12_Init(void)
    {
      TI_LMP91000_VOUT_ADC12_PxSEL |= TI_LMP91000_VOUT_ADC12_PIN;                  // Enable A/D channel A0
      REFCTL0 &= ~REFMSTR;                                                         // Reset REFMSTR to hand over control to
                                                                                   // ADC12_A ref control registers
      ADC12CTL0 = ADC12ON+ADC12SHT0_8+ADC12MSC;                                    // Turn on ADC12, set sampling time
                                                                                   // set multiple sample conversion
      ADC12CTL0 |= ADC12REFON+ADC12REF2_5V;                                        // Turn on Ref Gen & set to 2.5V
      ADC12MCTL0 = ADC12SREF_1;                                                    // Vr+ = Vref+ and Vr- = AVSS
      ADC12CTL1 = ADC12SHP+ADC12CONSEQ_2;                                          // Use sampling timer, set mode
      ADC12IE = ADC12IE0;                                                          // Enable ADC12IFG.0
      __delay_cycles(500);                                                         // delay to allow Ref to settle
      ADC12CTL0 |= ADC12ENC;                                                       // Enable conversions
      ADC12CTL0 |= ADC12SC;                                                        // Start conversion
    
    }
    
    
    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12ISR (void)
    {
      static uint8_t index = 0;
      static volatile uint16_t results[NUM_OF_RESULTS];                            // To store ADC output
      static uint32_t sum_adc_data = 0;                                            // accumulate and avg adc results
      static volatile  float vout;                                                  // lmp91000 vout
    
      switch(__even_in_range(ADC12IV,34))
      {
      case  0: break;                                                              // Vector  0:  No interrupt
      case  2: break;                                                              // Vector  2:  ADC overflow
      case  4: break;                                                              // Vector  4:  ADC timing overflow
      case  6:                                                                     // Vector  6:  ADC12IFG0
        results[index] = ADC12MEM0;                                                // Move results
        sum_adc_data += ADC12MEM0;
        index++;                                                                   // Increment results index, modulo;
    
        if (index == NUM_OF_RESULTS)
        {
          sum_adc_data >>= SCALE_FACTOR;                                           // Divide by NUM_OF_RESULTS
          vout = sum_adc_data * ADC12_RATIO;                                       // LMP91000 vout
         points [count]= sum_adc_data * ADC12_RATIO;
          sum_adc_data = 0;                                                        // Set Breakpoint here & see measured vout
          index = 0;
          _BIC_SR(LPM0_bits);														// to continue with main ( same as _BIC_SR(LPM0_bits))
    
        }
        break;
      case  8: break;                                                              // Vector  8:  ADC12IFG1
      case 10: break;                                                              // Vector 10:  ADC12IFG2
      case 12: break;                                                              // Vector 12:  ADC12IFG3
      case 14: break;                                                              // Vector 14:  ADC12IFG4
      case 16: break;                                                              // Vector 16:  ADC12IFG5
      case 18: break;                                                              // Vector 18:  ADC12IFG6
      case 20: break;                                                              // Vector 20:  ADC12IFG7
      case 22: break;                                                              // Vector 22:  ADC12IFG8
      case 24: break;                                                              // Vector 24:  ADC12IFG9
      case 26: break;                                                              // Vector 26:  ADC12IFG10
      case 28: break;                                                              // Vector 28:  ADC12IFG11
      case 30: break;                                                              // Vector 30:  ADC12IFG12
      case 32: break;                                                              // Vector 32:  ADC12IFG13
      case 34: break;                                                              // Vector 34:  ADC12IFG14
      default: break;
      }
    }
    

    I only get one value into the points array, and then the program stays stuck in the ISR.

  • How a ISR can wake up a main routine, is a trick, a very clever trick.

    When a hardware ISR is invoked, the msp430 puts two values on reserve (to the stack)
    So it can restore those values when it's done.

    What your ISR routine can do is change those values so a RETI does not do what it think it's doing,
    restoring those values back to original state.

    You could clear some bits that your main routine set before going to sleep.
    Setting bits turns things OFF, and clearing them starts OCS and/or CPU back on.


  • Thanks, I understand what you are saying, hence, why I have included the _on_exit intrinsics, yet it seems my program returns to sleep mode.

    I tried several variations of my code hoping it was my logic that is wrong. The code I posted earlier is one of the variations I have used. Let me know what you think.

    I was trying to think through the problem, and I realized from reading the above postings that if I create a task scheduler I may solve my problem. I can check the ADC every second for a value and store that value then returning to sleep mode, and waiting a second again before checking the ADC.

    What do you think?

    Regards,

    Jonathan Velez

  • In the code you posted, you clear the LPM0_bits inside the ISR. But inside the ISR they are already clear (or the ISR wouldn't run), so this has no effect.

    You need to clear the bits in the stored copy of the status register on the stack. To do so, you need to use the BIC_SR_on_exit() intrinsic, not the BIC_SR().

    Also, it is a good idea to disable interrupts to avoid a race condition, but you should do so before you do something that could cause the race condition (like starting the conversion). Doing it immediately before entering LPM is pointless.

    However, you should init your ADC before your for loop, so it is only initialized once. But the actual start (setting ADC12SC) should be done right before entering LPM - and after you disabled interrupts.

    What happens when your loop has run for 28 times? You will fall out of main into nowhere. There is no OS to return to, and future behavior of the MSP is undefined. Add a while(1) loop a thte end of main or enter LPM if you still need interrupts running.

  • I took your advice so know my code looks like:

     TI_LMP91000_LED_PxOUT |= TI_LMP91000_LED_PIN;                                // Set LED ON
      TI_LMP91000_LED_PxDIR |= TI_LMP91000_LED_PIN;                                // Set pin direction is output
    
      I2CSetup(LMP91000_I2C_Address);                                              // Initialize I2C module
    
      TI_LMP91000_MENB_PxOUT &= ~TI_LMP91000_MENB_PIN;                             // Enable \MENB Pin
      TI_LMP91000_MENB_PxDIR |= TI_LMP91000_MENB_PIN;                              // Set pin direction is output
    
    
      while (status == TI_LMP91000_NOT_READY)
          status = LMP91000_I2CReadReg(TI_LMP91000_STATUS_REG);                      // Read device ready status
    
      LMP91000_I2CWriteReg(TI_LMP91000_LOCK_REG, TI_LMP91000_WRITE_UNLOCK);        // unlock the registers for write
    
      LMP91000_I2CWriteReg(TI_LMP91000_TIACN_REG, TI_LMP91000_TIACN_REG_VALUE);    // Modify TIA control register
      LMP91000_I2CWriteReg(TI_LMP91000_REFCN_REG, bias[count]);    // Modify REF control register
    
      read_val[0]   = LMP91000_I2CReadReg(TI_LMP91000_TIACN_REG);                  // Read to confirm register is modified
      read_val[1]   = LMP91000_I2CReadReg(TI_LMP91000_REFCN_REG);                  // Read to confirm register is modified
    
      if ((read_val[0] != TI_LMP91000_TIACN_REG_VALUE) ||
          (read_val[1] != TI_LMP91000_REFCN_REG_VALUE))                            // test values took effect
        while (1);                                                                 // otherwise error
    
      LMP91000_I2CWriteReg(TI_LMP91000_LOCK_REG, TI_LMP91000_WRITE_LOCK);          // lock the registers
      LMP91000_I2CWriteReg(TI_LMP91000_MODECN_REG, TI_LMP91000_MODECN_REG_VALUE);  // 3-lead amperometric cell
    
      ADC12_Init(); 														 // Initialize MSP430F5528 ADC12 & Start Conversion
    
     for (count = 0; count < 56; count++){
    
    
    	 __disable_interrupt();														// To avoid a race condition
    	 ADC12CTL0 |= ADC12SC;                                                        // Start conversion
    	 __bis_SR_register(LPM0_bits | GIE);
    
    
    
     }
    
    while(1)
    {
    printf(" almost done");
      }
    }
    
    
    
    
    void ADC12_Init(void)
    {
      TI_LMP91000_VOUT_ADC12_PxSEL |= TI_LMP91000_VOUT_ADC12_PIN;                  // Enable A/D channel A0
      REFCTL0 &= ~REFMSTR;                                                         // Reset REFMSTR to hand over control to
                                                                                   // ADC12_A ref control registers
      ADC12CTL0 = ADC12ON+ADC12SHT0_8+ADC12MSC;                                    // Turn on ADC12, set sampling time
                                                                                   // set multiple sample conversion
      ADC12CTL0 |= ADC12REFON+ADC12REF2_5V;                                        // Turn on Ref Gen & set to 2.5V
      ADC12MCTL0 = ADC12SREF_1;                                                    // Vr+ = Vref+ and Vr- = AVSS
      ADC12CTL1 = ADC12SHP+ADC12CONSEQ_2;                                          // Use sampling timer, set mode
      ADC12IE = ADC12IE0;                                                          // Enable ADC12IFG.0
      __delay_cycles(500);                                                         // delay to allow Ref to settle
      ADC12CTL0 |= ADC12ENC;                                                       // Enable conversions
    
    
    }
    
    
    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12ISR (void)
    {
      static uint8_t index = 0;
      static volatile uint16_t results[NUM_OF_RESULTS];                            // To store ADC output
      static uint32_t sum_adc_data = 0;                                            // accumulate and avg adc results
     static volatile  float vout;                                                  // lmp91000 vout
    
      switch(__even_in_range(ADC12IV,6))
      {
      case  0: break;                                                              // Vector  0:  No interrupt
      case  2: break;                                                              // Vector  2:  ADC overflow
      case  4: break;                                                              // Vector  4:  ADC timing overflow
      case  6:                                                                     // Vector  6:  ADC12IFG0
        results[index] = ADC12MEM0;                                                // Move results
        sum_adc_data += ADC12MEM0;
        index++;                                                                   // Increment results index, modulo;
    
        if (index == NUM_OF_RESULTS)
        {
          sum_adc_data >>= SCALE_FACTOR;                                           // Divide by NUM_OF_RESULTS
          vout = sum_adc_data * ADC12_RATIO;                                       // LMP91000 vout
    
          // points [count]= sum_adc_data * ADC12_RATIO;
          sum_adc_data = 0;                                                       
          index = 0;
    
          printf("%f\n\r", vout);
          printf("%d\n\r", count);
          __bic_SR_register_on_exit(LPM0_bits);
        }
        break;
      default: break;
      }
    }
    
    int fputc(int _c, register FILE *_fp)
    {
      while(!(UCA1IFG&UCTXIFG));
      UCA1TXBUF = (unsigned char) _c;
    
      return((unsigned char)_c);
    }
    
    int fputs(const char *_ptr, register FILE *_fp)
    {
      unsigned int i, len;
    
      len = strlen(_ptr);
    
      for(i=0 ; i<len ; i++)
      {
        while(!(UCA1IFG&UCTXIFG));
        UCA1TXBUF = (unsigned char) _ptr[i];
      }
    
      return len;
    }
    

    I only get up to count = 34…I included a while(1) so that we would have somewhere to return to, but the program doesn't continue. When I stop the program putty prints out 34 points again. Any suggestions?

  • Actually, I don't know what's going on.

    In main, you don't need to set ADC12SC on each loop. In repeated single mode, ADC12SC is never reset, so the ADC is constantly running anyway.

    Also, since the ADC is constantly generating interrupts, there is no race condition (well, at least none you could prevent by disabling interrupts). The ADC is already firing after the first loop, so unless your ISR disables interrupts on exit, you could well get an interrupt before entering LPM0. In fact, after printing your output (inside the ISR!) you'll already have more than one ADC conversion interrupt pending (and an overflow interrupt, as you missed some more conversion results due to the slow, blocking printf). However, nothing of this should make the program stall.

    You could put a conditional breakpoint into the ISR:

    If(count==34){
    __no_operation;
    __no_operation; // place breakpoint here
    }

    Then use single-stepping to perhaps find out what's going on.

    Same for count==35 at the beginning of the ISR, or even inside the main loop after the LMP entry.

**Attention** This is a public forum