Jump to content
energia

Building low power into Energia

Recommended Posts

Energia in it's current implementation takes advantage of low power in some places but we can improve on this quite a bit.

To make sure that the implementation of low power modes is inline with what you expect, I want to ask you: "How would you like to see low power work"

 

I have some ideas but do not want to bias anybody by listing them. Anything goes!

 

Thanks!

 

Robert

Share this post


Link to post
Share on other sites

@@energia

 

I see two kinds of usage:

  • place the MSP430 on deep sleep mode and wake it up based on a timer, e.g. every minute or every hour
  • place the MSP430 on deep sleep mode and wake it up based on a pin interrupt, for example when receiving a message from the Anaren CC110L BoosterPack or other low-energy RF.

Share this post


Link to post
Share on other sites

I guess the challenge with LPM's is to be able to do it in a way that doesn't break Arduino compatibility.  With that in mind I would suggest the following:

 

The first thing is very straightforward, and is as simple as adding a call to _BIC_SR_IRQ(LPM4bits) into the pin-interrupt handler. That way you can put the processor into LPM4 inside loop() (or setup if you feel so inclined), then when a pin interrupt occurs, the interrupt handler will run, then the loop() will continue to run from the next instruction after the LPM4.  Of course if you want to do all your work in the interrupt, all you have to do is just have LPM4 in your loop and it will resume to LPM4 very soon after the interrupt has run.

 

For a bit more advanced low power control, I'd like to see a mechanism of selecting the 32.768 crystal as the source of the WDT timer. I do this in my RTC library.  The drawback is that due to hardware limitations within the WDT module, the maximum resolution is only 1/512 seconds (approx 2 mSec). The big plus though, is that because the crystal runs in LPM3 mode it allows delays to be done in LPM3 mode. In the RTC library I create an "LPM3delay" function that does that. Of course it still comes briefly out of LPM3 512 times per second, but it offers substantial power saving over the traditional LPM0 delay function.

 

The difficulty here is that the default setting still needs to be that the WDT is sourced from SMCLK to allow for maximum Arduino compatibility and to cater for the many launchpads that don't have the crystal attached. I'm not sure what the best way to provide a crystal option would be - options would include:

  1. A runtime function (e.g. create a function setTimerSourceXtal() or similar) which reconfigures the clocks. The code for the interrupt, mills & delay would have to have some mechanism of determining at runtime what the interrupt frequency is so it can keep the timers accurate. This is currently done at compile-time, so doing it at runtime would add extra code & ram overhead. One advantage of the run-time option though is that it could even be extended to control the frequency between 512/64/4/1 Hz. The lower frequencies would dramatically reduce the resolution of delay() & millis(), (which could break some libraries) but depending on the application that may not be an issue, and the lower frequencies would allow a lower power consumption.
  2. Linking in an alternative to wiring.c as part of a library - similar to the way the RTC library does it now. When the linker finds a replacement for core code in either the main .ino or in a library it replaces the core code with the new version. The only drawback I can see is that there may be an issue keeping both lots of code in synch. For example my RTC library uses a method of calculating millis() based on the wiring.c included with Energia 9. Energia 10 has a much improved millis() algorithm which isn't in the RTC library.
  3. A compile time option that allows you to select the crystal as a board type - e.g. in drop-down where you select the 2452, 2553 etc, have something like "2553 with 32kHz Crystal". I'm not sure if this method of board selection is possible but if it is it would then presumably be a matter of using a #ifdef to determine if the alternate code should be compiled.

Share this post


Link to post
Share on other sites

I personally think that managing LPM at a high level from energia will be a difficult task. My CC430's are now waking from LPM3 based on RTC counter events but I'd also like the MCU to wake up on given date/times, pin events or even RF receptions. Many different possibilities with many different MCU's. In any case, entering LPM is quite simple so I'd avoid adding more complexities to Energia.

 

On the other hand, I'd expect a way to restart the WDT after exiting from LPM. This has recently caused me some nightmares since I tried to do a delay() when the WDT was previously disabled before entering LPM. Rick has suggested me to give enableWatchDog() a try and I'll do so.

Share this post


Link to post
Share on other sites

Sorry for the short answer I gave before, I was working from my phone.

 

Now, for low power mode and its challenges.

First off, the MSP430 does offer a lot of low power features that are not available on the AVR32 microcontrollers. As a result, implementing low power and not breaking with Arduino is a conflict in focus. Personally I'd rather have a non-Arduino but low power enabled version.

 

As mentioned before, without the option to exploit the crystal clock source you're basicaly limited to using either LPM0 or LPM4. Which basically means low-power-with-timers or low-power-without-timers. The much loved LPM3 would enable usage of timers while still going much lower power.

 

 

Currently there is only the option to attach interrupts to the buttons, but not to anything else (UART, timers, etc), which poses the question of how to handle interrupts (the prime enabler of low power).

 

One solution is to let users define their own ISRs, but this breaks the nice simplificatios that Energia provides.

A second solution is to expand attachInterrupt to allow other interrupt sources.

A third solution would be to let every and any interrupt kick the MSP430 out of low power, and then handle the interrupt inside the loop() using flags (one flag per interrupt source).

 

Then to go to low power, I think it would be most elegant (though somewhat more complex for library builders) to make global volatile "counting semaphores" that keep track of the currently active peripherals that require certain low power modes to be available.

For example, when I am sending a byte over UART, the MSP430 may not enter LPM3 after the loop(), because then the UART will stop working. This is because the UART needs SMCLK to be available. So when I start sending a byte, the UART send function incrrements te SMCLK semaphore by 1, when it's done sending it decrements the semaphore by 1 again.

 

As I said before the loop() will need to be low power enabled, for example by using a function like setLoopFrequency(1000) to let loop() be called 1000 times per second (or something alike). As a result, the framework will by default call loop() immediately, but when you have set the frequency, the framework will increment the ACLK semaphore by 1 to prevent the loop-timer from freezing, and then decrement it by 1 just before calling loop() again.

 

 

As a major braintwist: ideally I'd like to use the MSP430 with an API that mimics asynchronous behaviour. So for example I'd like to call a UART send function in an asynchronous fashion like

uartSendAsync(str, len, &done);

where str is a pointer to a string, len is the number of bytes to transit and done is a function that is called upon finishing (or failing to finish) the transmission. This style is similar to the Promise pattern as used in JavaScript.

The big advantage is that peripherals are approached basically is dedicated coprocessors, allowing a user to write a program as if he has multiple threads/processes/processors to his disposal.

The big drawback is that it's much easier to screw things up when you do not know what you're doing unless you build a heavy framework in Energia to protect the user from himself.

const int LED1 = 1;
const int LED2 = 2;

void toggleLED()
{
  digitalWrite(LED1, !digitalRead(LED1)); // toggle LED1
}

void sendDone(bool error) //error is true if an error occurred, could be an integer as well?
{
  digitalWrite(LED2, LOW); // LED2 low when done sending string
  //after the string is sent, SMCLK is no longer required, SMCLK may be disabled, so the MSP430 can go to LPM3
  //because there are still timers required for the intervals set, LPM4 cannot be used; ACLK is still prevented from being disabled
}

void sendString()
{
  static char str = "hello world";
  digitalWrite(LED2, HIGH); // LED2 high when starting string send
  uartSendAsync(str, sizeof(str), sendDone); // send a string, then call sendDone
  //until the string is sent, Energia will prevent the MSP430 from disables SMCLK, so the lowest power mode is mode 0
}

void setup()
{
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  setInterval(200, toggleLED); // toggle the LED every 200ms
  setInterval(2000, sendString); // send a string every 2 seconds
}

Share this post


Link to post
Share on other sites

My thought is to have an additional class of functions, e.g. LowPower.<method> with specialized delays that allow one to make use of LPM3 and perhaps LPM4 as needed.  The goal I had in mind is to boil LPM down to basic primitive modes that are available on many chips--as most chips do have some sort of "sleep" vs "deep-sleep/hibernate with a periodic tick to wake up" sort of feature.  With a high-level API for this, a low-power application could be ported seamlessly from MSP430 to Tiva.

Share this post


Link to post
Share on other sites

Thank you all for the excellent feedback! This is exactly what I was looking for. So keep them coming.

I will sort through them in the next week or so and report back with a proposal for us to discuss.

 

Thanks again!

Share this post


Link to post
Share on other sites

I've been thinking a bit more about the low power stuff. I came to the conclusion that the loop() is conflicting with the idea of low power. Instead of a loop() that is called contiguously, an interval handler should be defined. Essentially this is the same as stating that the interval of loop() should be configurable, but this puts loop() on a pedestal. Instead of having a main loop, you'd have a timer bound event, in Arduino compatible mode the interval is set to 0, which is why loop() seems like the place for the timer handler.

But it is perfectly fine to have multiple timer event handlers, for example one every second and one every minute, either updating the kept time. Instead of having a single handler which counts to 60 and then increments the minute value, why not just have a second handler set at a minute interval?

 

Effectively my suggestion is to implement an abstraction layer on top of interrupts that implements an actor-like model. I say actor-like because there are properties of actors that you'd not want to implement on a microcontroller, for example you don't want to be able to send an infinite number of strings into your UART actor, since this would consume an unacceptable amount of RAM. Instead such a UART abstraction would only be able to handle a single message at once. A second message would result in the immediate calling of the promise (on-finished callback) with an error value. This calls for the need of a isBusy() method for such actors.

 

An actor would be able to handle this:

void onDone(bool error)
{
  // nothing to do
}

{
  sendUart("1", onDone); sendUart("2", onDone); sendUart("3", onDone); sendUart("4", onDone); sendUart("5", onDone);
  sendUart("6", onDone); sendUart("7", onDone); sendUart("8", onDone); sendUart("9", onDone); sendUart("10", onDone);
}

The MSP430 actor-like abstractions would need something like this:

int count = 1;
char str = "1";

void onDone(bool error)
{
  if (count < 10)
  {
    count++;
    str[0] = count + '0';
    sendUart(str, onDone);
  }
  else
  if (count == 10)
  {
    count++;
    sendUart("10", onDone);
  }
  else
  {
    //nothing to do
  }
}

{
  sendUart("1", onDone);
}

Share this post


Link to post
Share on other sites

Roadrunner, I think that loop() and LPM are perfectly compatible. For example, our panStamps are put into LPM from loop() as follows:

void loop()
{
  // Do something like sending wireless packets...
  
  panstamp.rtc.sleep(10);    // Enter LPM3 (2uA) and stay there for 10 seconds
}

Of course, we could enter LPM4 instead and wait for any binary input event, etc.

Share this post


Link to post
Share on other sites

For all I know this is already implemented, but why not put the system to sleep during the delay() function and use a timer to wake it up? 

 

Baby steps

Share this post


Link to post
Share on other sites

For all I know this is already implemented, but why not put the system to sleep during the delay() function and use a timer to wake it up?

 

At the moment, delay() goes into LPM0. Because SMCLK by default is derived from The DCO, and SMCLK is the timer used, LPM0 is the lowest power mode that can currently be used without issues. When using the DCO, LPM1 is effectively the same as LPM0, and LPM2 or lower will stop the clock, so we would never wake.

 

To use lower power modes we have to use a timer derived from VLOCLOCK or the 32.768kHz crystal, as these clocks continue to run down to LPM3. Both have drawbacks, although as per my prior post I think we should have the option of selecting the crystal.

 

The drawbacks are:

 

Firstly with the crystal, not every launchpad has it soldered on.

 

VLOCLOCK is terribly inaccurate. From my experiments, it's frequency varies between about 10kHz and 15kHz depending on temperature and chip to chip. The frequency can vary dramatically within the space of a few minutes, so calibration isn't really an option.

 

If the Watchdog Timer is used as the timer base, as it currently is, the minimum resolution that can be derived from the crystal is approx 1.9mS, and from VLOCLOCK it is approx 6mS. This affects the resolution of anything that uses delay() or millis(), and could break libraries. The lower resolution from the crystal is IMO acceptable, but only if it is presented as an option to choose, either at runtime or compile time.

 

If instead of the WDT we use the TIMER_A to measure the time for the delay, we have a bit more flexibility and can maintain good resolution with VLOCLOCK or the crystal. But TIMER_A is currently used for a host of other functions, and the 2231 & 2452 only have 1 Timer. So if we use TIMER_A the 2231 & 2452 would completely lose serial, analogWrite, & tone. Even on the 2553 analogWrite would be limited to 2 pins.

 

Unfortunately because of the hardware limitations, using the lower power modes with the option of timer wake up is not trivial. LPM4 is trivial, but it will only wake with a pin interrupt (no timeout option). Bear in mind that in LPM4 the timer for millis() stops so there is no way to determine how long you were in sleep mode unless you use an external time base such as an RTC ic.

 

As in my previous post, I think the best options are:

At the end of the pin interrupt handler in the core, clear the LPM4 bits so it will wake up reliably from LPM4

Provide an option to select the crystal as the time base for the WDT, (either at runtime or compile time) so LPM3 can be used.

 

 

Sent from my iPad using Tapatalk

Share this post


Link to post
Share on other sites
Asynchronous programing models with events and multiple unconnected execution paths will be hard for beginners to grasp.

I think this is essentially not true. Event driven programming is a very natural way to build applications. It starts to become hard to grasp only after you've been drenched in the linear programing paradigm.

 

But I think you're right that Energia may not be the right place to fit an event driven model in.

 

I have personally never used LPM1 or 2. When I want to just stop the CPU I enter LPM0, but far more often I drop to LPM3 and use the ACLK (either VLO or LFXT) to wake up again. I drop to LPM4 quite often as well, most times to come back on a reset only.

Not having an external crystal basically strips away LPM3 if you desire any time precision (which is not really that essential a lot of the time), so you're stuck with quick-nap LPM0 or deep-freezer LPM4, which is kind of a shame.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×