Jump to content
cde

LPM breaks code. Need Help

Recommended Posts

So finally coded up my lm4970 project. And using a interrupt on switch p1.3, and tried to get LPM. Well, it didn't work. If I remove LPM, it works, but with a huge issue where it keeps looping, but still works. So the only problem is my main code, not the usi/i2c/lm4970 code.

 

What I expect it to do:

  • Initialize everything (Works)
    • Go Into While Loop
      • Switch on State Variable
      • Run given Case, Break out of Switch
      • Enable Interrupts and Go to Sleep ( LPM4 + GIE )
        • Sleep until Interrupt
        • Interrupt Disables GIE and LPM4, Increases State Variable
        • Exit Interrupt, reach end of While Loop, back to Switch
      • Switch on State Variable, repeat

Doesn't do that. If I remove the LPM4_Bits, so only GIE is set and disabled, the code works (I need to debounce in the interrupt, planning on using watchdog or simple delay). But since it never goes to sleep, the while loop keeps running, causing other issues.

 

How can I fix this. In this given case, I could just have the switch/case in the interrupt, but I plan on doing some manual control that would not work well in it.

 

Code below:

#include "common.h"
#include "i2c.h"
#include "lm4970.h"

// PB is P1.3 Pushbutton on Launchpad

int lm4970_state = 0;     // lm4970 State

void cpu_init(void)
{
     WDTCTL = WDTPW + WDTHOLD;  // Stop WatchDog Timer

     BCSCTL1 = CALBC1_1MHZ;     // Set range to 1mhz Calibrated Range
     DCOCTL = CALDCO_1MHZ;      // Set DCO to 1mhz Calibrated DCO
                                // SMCLK = DCO = 1MHz
}

void pushbutton_init(void)
{
     P1SEL &= ~PB;      // Set PB as GPIO (Part 1)
     P1SEL2 &= ~PB;     // Set PB as GPIO (Part 2)
     P1DIR &= ~PB;      // Set PB as Input
     P1REN |= PB;       // Set PB Pull-Up/Pull-Down Mode
     P1OUT |= PB;       // Set PB Pull-Up

     P1IES |= PB;       // P1.3 Interrupt set for High to Low
     P1IFG &= ~PB;      // P1.3 Interrupt Flag Cleared
     P1IE  |= PB;       // P1.3 Interrupt Enabled
}

// Port 1 interrupt service routine
#pragma vector = PORT1_VECTOR
__interrupt void Port_1_ISR (void)
{
// Clear Low Power Mode and Disable Interrupts to Allow MSP to handle commands before returning to sleep
     _BIC_SR(LPM4_bits + GIE);
//     _BIC_SR(GIE);

     lm4970_state++;     // Increase LM4970 state by 1
     if (lm4970_state > 9) lm4970_state = 0;
     P1IFG &= ~PB;       // P1.3 IFG cleared

}

int main(void)
{
     cpu_init();
     i2c_init();
     pushbutton_init();
     lm4970_init();

     // Setup Complete, now check state, run function, then go to sleep with interrupts
     while(1)
     {
          switch ( lm4970_state )
          {
          case 0: // Off
               lm4970_shutdown();
               break;
          case 1: // Regular Audio Sync
               lm4970_audio_sync();
               break;
          case 2: // Randomized Audio Sync
               lm4970_random();
               break;
          case 3: // Blue
          *snipped*
          case 9: // White
               lm4970_led_color(lm4970_state - 2);
               break;
          case 10:
               // Future Options, Like Color Fade Mode
          default: // Reset State, Turn Off
               lm4970_state = 0;
               lm4970_shutdown();
               break;
          }          

          _BIS_SR(LPM4_bits + GIE); // Low Power Mode x, Enable Interrupts
//          _BIS_SR(GIE); // Enable Interrupts
     }
}

Share this post


Link to post
Share on other sites

_BIC_SR() ... you might try _BIC_SR_IRQ(...)

I tried that (since it's also the define for LPM4_On_Exit) but it didn't work. I'll try again.

Share this post


Link to post
Share on other sites

Try adding a no operation directly after each _BIS_SR. In your case you do two of these directly after each other. This may cause the sr to be copied in the pipeline just before actually going to sleep, then after waking up, the sr (with lpm4 bits still set in the pipelined copy) is written back to the real sr. This may cause the cpu yo go back to sleep again unintentionally.

This is also recommended in chapter 3 of the family guide.

Share this post


Link to post
Share on other sites

Which compiler are you using?

The one returning from the interrupt should definitely be _BIC_SR_IRQ(LPM4_bits) because it modifies the SR in RAM before shifting it back in the SR after returning from the interrupt. Using _BIC_SR() will clear the status register directly but overwrite it after the interrupt routine returns. In IAR __bic_SR_register_on_exit(...) also works.

 

BTW, is the button working correctly without debouncing it?

 

Share this post


Link to post
Share on other sites

The second _bis_sr is commented out, that's just how I tested it without lpm. Ill add the nop and make sure I try bic_sr_irq. I am using IAR.

 

And the button has bounce issues (no cap/resistor debounce on rev 1.5 launchpad ha) but works well enough.

Share this post


Link to post
Share on other sites

I recently learned that cap/resistor do only work as denounce when your pin is Schmitt triggered (goes from 1 to 0 on a lower voltage than the other way around), otherwise it might actually aggravate the bouncing issue! Non-schmitt triggered inputs will in fact stay oscillating when using cap/resistor as the voltage rises slow, presenting a slope to the pin.

Share this post


Link to post
Share on other sites

I typically wake from low-power as the very least step in an interrupt.  

Also - the TI code samples for pin interrupts clear the interrupt flag after setting P1IE and P1IES - you're clearing the flag right before you enable interrupts.  

 

Is your interrupt not working at all or are you just getting unexpected results?

Share this post


Link to post
Share on other sites

I typically wake from low-power as the very least step in an interrupt.  

Also - the TI code samples for pin interrupts clear the interrupt flag after setting P1IE and P1IES - you're clearing the flag right before you enable interrupts.  

 

Is your interrupt not working at all or are you just getting unexpected results?

Without the LPM, the interrupt works fine. With LPM, I'm not sure.

Share this post


Link to post
Share on other sites

I added the __no_operation(); before and after the _Bis_sr and _bic_sr_irq, and it's working as expected now. Thank you guys. Now I just need to add in debounce code, and try to add in some scripted light flashing. hmm.

Share this post


Link to post
Share on other sites

I added the __no_operation(); before and after the _Bis_sr and _bic_sr_irq, and it's working as expected now. Thank you guys. Now I just need to add in debounce code, and try to add in some scripted light flashing. hmm.

So whats the code look like now ?

 

I'm hangin' in suspense  ;-)

 

Mainly, I'm interested because early one when i was experimenting with LPM code, I had a similar issue with LPM4. I've since had LMP0 working, but haven't used LPM4.

Share this post


Link to post
Share on other sites

So whats the code look like now ?

 

I'm hangin' in suspense  ;-)

 

Mainly, I'm interested because early one when i was experimenting with LPM code, I had a similar issue with LPM4. I've since had LMP0 working, but haven't used LPM4.

I literally only added a __no_operation(); before and after the _BIS and _BIC.

#pragma vector = PORT1_VECTOR
__interrupt void Port_1_ISR (void)
{
// Clear Low Power Mode and Disable Interrupts to Allow MSP to handle commands before returning to sleep
     __no_operation();
     _BIC_SR_IRQ(LPM4_bits + GIE);
     __no_operation();

     lm4970_state++;     // Increase LM4970 state by 1
     if (lm4970_state > 9) lm4970_state = 0;
     P1IFG &= ~PB;       // P1.3 IFG cleared
}

 

and

int main(void)
{
     cpu_init();
     i2c_init();
     pushbutton_init();
     lm4970_init();

     // Setup Complete, now check state, run function, then go to sleep with interrupts
     while(1)
     {
          switch ( lm4970_state )
          {
          case 0: // Off
               lm4970_shutdown();
               break;
          *snipped*
          case 9: // White
               lm4970_led_color(lm4970_state - 2);
               break;
          default: // Reset State, Turn Off
               lm4970_state = 0;
               lm4970_shutdown();
               break;
          }          

          __no_operation();
          _BIS_SR(LPM4_bits + GIE); // Low Power Mode x, Enable Interrupts
          __no_operation();
     }
}

Share this post


Link to post
Share on other sites

You only need one after _BIS_SR(), as I tried to explain, when you switch to LPM, the next instruction is partially executed already. If this next instruction does an an atering on a variable, it might have been read, but not yet written. When you wake from low power, you might expect it to read the variable, alter it and write it, but the reading has already been done for you.

This is also the case with SR, so doing _BIS_SR(LPM4_bits + GIE); and then _BIS_SR(GIE); will actually read the SR with the low power mode bits set in the _BIS_SR(GIE); instruction, so when waking from low power will then write the low power bits back to the SR again, although that is not your intention. To avoid this, add the __no_operation() after each point where you'll end up in low power mode. This will avoid reading the SR prematurely, but instead fill the pipline stage with somthing that ends up doinbg about nothing (like adding 0 to 0).

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...