Jump to content
43oh

Design consideration: LPM pitfall possible?


Recommended Posts

Hi,

First of all, hi! :smile: Second, that I'm new to embedded programming and it seems I have to learn the basics. What's the problem: I'm doing a couple of different sensor measurement in my code. I have 2 sensors Timer-based (pseudo 1-wire protocol that I need to parse by verifying logic level intervals) and 1 I2C-based (BMP085).

init();
get_temp_data();
get_humidity1_data();
get_humidity2_data();

Now, while the Timer-based readings (humidity1 and humidity2) are sequential (each with while() loop finished when a complete set of reading is done), that means humidity2 cannot start before humidity1 is completed, the temp and humidity1 can be run concurrently - temp is I2C-interrupt-triggered, humidity is Timer-interrupt-triggered. Of course, I could make humidity1 start after temp completes, but I guess this is not good fashion ;-)

 

Now as for the LPM. Suppose the init() sets the appropriate interrupt setup and when did it calls the LPMx.  The I2C ISR is triggered and thus the get_temp_data() is served. But inside, the get_temp_data() has 

_bis_SR_register(CPUOFF + GIE)

 to enter LPM because we are waiting for further interrupts.

 

What will happen if the I2C and Timer interrupts are caught at the same time? They are prioritized, but in fact queued. Let's say the I2C will go first, enter the get_temp_data() and hit the 

_bis_SR_register(CPUOFF + GIE)

 which will cause the CPU go to sleep. Then the Timer ISR would get lost.

 

Is this case protected? Will the CPU check for pending interrupts before going to LPM? And in general, is the design presented above ok?

 

Best Regards,

tml

Link to post
Share on other sites

Typically you don't use __bis_SR_register inside an ISR, what happens is you let the ISR exit and when it does, it pops the previous value of SR off the stack, which includes CPUOFF+GIE. So the CPU goes back into LPM after exit--unless another IRQ is in the queue, at which point the CPU will address that instead.

 

How you modify the runtime state inside an ISR is to use __bic_SR_register_on_exit() to clear the LPM bits so it resumes active mode execution of the main program upon exit. (There is a __bis_SR_register_on_exit() in case you want the ISR to push the CPU into an even deeper level of sleep after it exits)

 

The way I handle concurrency like that is to make both ISRs wake the CPU on exit and then have checking code in the main loop that will re-enter LPM or not based on whether everything is done.

 

Sent from my Galaxy Note II with Tapatalk

 

 

Link to post
Share on other sites

Not really correct. If you are in LPM, then enter an interrupt and inside this interrupt you enter LPM again (as spirilis says: don't), you set the GIE bit again too. The second ("queued") interrupt will then be executed while the first ISR is still on hold. Then hopefully your second ISR will leave LPM on exit, so the first interrupt gets processed further. If you don't, the first interrupt will at some point be fired again, which will again go on hold. This keeps happening until your stack runs out and you get undefined behaviour (because you're writing in RAM where you did not expect or intend to write).

 

Generally speaking, you never want to stall during an ISR, not using a while(!hardware_flag) nor using LPM. ISRs must (as a rule of thumb) be handled as quickly as possible. If you ever need to postpone a certain part for later processing, shift that out to the main loop.

/* Do not use this design */
void main()
{
  init();
  LPMx;
}

__interrupt void ISR_A()
{
  do_part_1();
  LPMx;
  do_part_2();
}

__interrupt void ISR_B()
{
  wake_from_LPM();
}

/* Use this design */
bool flag = false;

void main()
{
  init();
  while(1) // infinite loop
  {
    LPMx;
    if (flag)
    {
      do_part_2();
      flag = false;
    }
  }
}

__interrupt void ISR_A()
{
  do_part_1();
}

__interrupt void ISR_B()
{
  flag = true;
  wake_from_LPM();
}
Link to post
Share on other sites

Thank you @sprilis and @@roadrunner84! Although this is not exactly what was my concern your instructions were still very valuable!

 

When I wrote

The I2C ISR is triggered and thus the get_temp_data() is served. But inside, the get_temp_data() has _bis_SR_register(CPUOFF + GIE)

 

it might have indicated that from the ISR I have directly called get_temp_data(), but in fact in the ISR I set the flag allowing get_temp_data() to process the while() loop, just as @@roadrunner84 suggested. I should be giving code snippets in the future...

 

My concern was what will happen if the next ISR will come right after first one came and right before the get_temp_data() got the flag to run, and then go to sleep...

But now I realize that the CPU won't go to the LPMx if it has any ISR in the queue.

Thanks again!

 

Best Regards,

tml

Link to post
Share on other sites

Keep in mind that the MSP430 is a bit unusual in the way that you can stick it into LPM just once in the main code, and then have all of your functionality implemented in ISRs.  In order for an ISR to cause the main process to resume, it needs to explicitly wake the MSP430.

Link to post
Share on other sites

Keep in mind that the MSP430 is a bit unusual in the way that you can stick it into LPM just once in the main code, and then have all of your functionality implemented in ISRs.  In order for an ISR to cause the main process to resume, it needs to explicitly wake the MSP430.

 

It's also important to note that if you want your MSP430 to be power efficient, you *have* to implement it this way.  The MSP430 isn't that power efficient when running at full speed...the PIC controllers use less power at full speed IIRC.  But it sleeps well and wakes up/responds to interrupts quickly and that is its main strength...

Link to post
Share on other sites

The MSP430 isn't that power efficient when running at full speed...the PIC controllers use less power at full speed IIRC.

PICs are very inefficient in terms of IPC and also in terms of the number of instructions you need to run in order to get an equivalent block of C executed.  So MSP430 is typically a lot more efficient in runtime than PIC is.  STM32L and other efficient ARM cores are more efficient than MSP430 is in runtime, though, sometimes markedly so.

Link to post
Share on other sites

It's also important to note that if you want your MSP430 to be power efficient, you *have* to implement it this way.  The MSP430 isn't that power efficient when running at full speed...the PIC controllers use less power at full speed IIRC.  But it sleeps well and wakes up/responds to interrupts quickly and that is its main strength...

 

 

Keep in mind that the MSP430 is a bit unusual in the way that you can stick it into LPM just once in the main code, and then have all of your functionality implemented in ISRs.  In order for an ISR to cause the main process to resume, it needs to explicitly wake the MSP430.

 

That is really interesting. So you say that on MSP430 it's better to enter LPMx in main loop and implement/call functions inside the ISR rather than just toggling a flag inside the ISR and then letting the main loop to continue? Is it because the uC runs at a lower frequency when the ISR is processed than it does in the main loop? My understanding is that upon entering ISR the uC wakes up just as it would when cleared LPM in the SR and then either goes into LPMx again (no __bic_SR_register_on_exit()) or stays woken up (__bic_SR_register_on_exit()).

Link to post
Share on other sites

You can implement all functionality in the ISRs, but bear in mind that you should never block then. Blocking code (while(!hardware_flag), write a serial string, etc) should only occur in the main loop.

You can also just leave LPM and process in the main loop, but that adds a little overhead, for small and fast processes, just keep it in the ISRs.

Link to post
Share on other sites

As others have said, you can implement MSP430 applications by entering LPM in main() and never returning.  Once interaction between subsystems becomes at all complex, though, the code is easier to maintain if the ISRs simply record fact-of-event and let an event handler in main dispatch things.  You also need to do this if the priority of interrupts provided by the MSP430 peripherals doesn't match the priority of the events that are captured.
 
For 5xx/6xx-based devices be aware of a couple things:  various UCS errata cause DCO instability when extremely short wake-ups prevent the FLL from stabilizing properly; there's a TI app note on the issue.  Also the same family has a supply voltage supervisor/monitor peripheral that, if left in its power-up configuration, hits you with a 150us penalty on wake-ups from LPM3 or LPM4.

Link to post
Share on other sites

That is really interesting. So you say that on MSP430 it's better to enter LPMx in main loop and implement/call functions inside the ISR rather than just toggling a flag inside the ISR and then letting the main loop to continue?

It depends on what you need to do.  For simple things, yes, this can suffice.  If you are doing a lot of concurrent I/O, you will want an RTOS or at least an extensive state machine to run in the main context, to manage all the interrupt sources (drivers).  If you have only one primary source of non-blocking I/O, then you can get-away with just doing it in the ISRs.

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...