Low Power/Sleep Library?

bdbell
Mon Jun 08, 2015 12:50 pm
I have been lurking in this forum and the original thread for a while – first off want to thank Roger and everyone else here for this great work on getting the STM32 working in the latest arduino environment!

Has anyone here done anything with Low-Power or sleep modes with the Maple Mini or similar clones? I am designing a device using STMF103C8T6 and a 2.4″ ILI9341 TFT. I want to turn off the display and put the device to sleep after a certain time of non-use. I have it working to turn off the display, and turning on using an interrupt on a button – but not having much luck finding any information on using Low Power modes. There are some examples on Leaflabs site using the same Sleep library as Arduino – but I have had no luck getting anything to work. Not so good with the low-level programming or library coding – more of a hardware guy…

The only thing I have found that does seem to work, at least reduces the CPU speed and power draw;

rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_256); //slows down cpu and reduces power

rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_1); /normal cpu speed

but when I use this occasionally the button interrupt to wake the device does not work consistently. This may be the way I have set up the interrupts…

Any help would certainly be appreciated!

Brent


mrburnette
Mon Jun 08, 2015 12:58 pm
I have personally not looked into low-power, but the original Leaflabs forum has some relevant posts. As we inherited and evolved from the Leaflabs’ effort, most of the structure should still be in place:

http://forums.leaflabs.com/topic.php?id=1437

http://forums.leaflabs.com/topic.php?id=922

As you explore the possibilities, post back your experience.

ray


victor_pv
Tue Jun 09, 2015 2:18 pm
Thanks for sharing how to change the clock divider to save some power. Have you done any measurement on power usage by any chance?

I am making something that will run on batteries and definitely reducing speed will help me, as I probably don’t need half the CPU power in this MCU.

EDIT: Those posts by Ray look promising.
I think you could use the systick to trip an interrupt every ms, check if there is something to do (like any input to process) and if not, go back to sleep.
The systick handler in libmaple has a function to set a callback function in your code, so once the interrupt trips besides incrementing the systick counter it will call your function to do whatever needed. You can also implement your own systick handler altogether.

If you have a look at the CoOS or FreeRTOS files, and search for systick, you will see how those setup the systick and systick callback.

I will try later to change CoOS and set the idle task to put the CPU to sleep when there is nothing to do and see if it works.

Anyone can suggest a measurable way to see if the CPU is sleeping much?


ahull
Tue Jun 09, 2015 2:37 pm
victor_pv wrote:
Anyone can suggest a measurable way to see if the CPU is sleeping much?

mrburnette
Tue Jun 09, 2015 3:35 pm
Anyone can suggest a measurable way to see if the CPU is sleeping much?

A small 1 Ohm resistor in series with the STM32 and use a scope to visualize the On/Sleep cycles by measurement of the Vd across the resistor.

On my Maple Mini, at full speed, the current is around 50mA, which would be a Vd of 165mV so a 1x probe should provide a decent indication while leaving 3.3 – 0.165 or 3.135V for the uC. If you are going to simulate a battery project, then I would recommend using the actual (fresh) batteries you intend on using, LiFePO4, Alkaline, etc. Chemistry will have an impact although I have not documented (anywhere I can find easily) what that will be. From a current measurement, you are not really overly concerned since you are looking for the deltas in current across the 1 Ohm resistor.

I wonder if the trick of reading the Arduino AVR battery voltage using the uC would work with the STM32… if so, once a critical voltage was reached you could do something: blink Power LED, display icon on 5110, turn off backlight, drop the uC speed further!

Ray


victor_pv
Tue Jun 09, 2015 4:22 pm
I found code by someone to measure the CPU usage in the CoOS idle task, but happens that just measures time wasted there, so even if the MCU is sleeping the time is passing, so that is not valid.

Seems like the current measurement may be the best. This can be a project to put the pig-o-scope to some practical usage too :)
I have a bunch of really low value 1/2W resistor that could eat 50mA for breakfast.
with 0.5ohm I calculate 25mV accross the resistor.
Andy, just have tested the pigoscope more, you think we have good sensitivity for 25mV?
I think I have some multimeter than is sensitive enough too.

I am planning on running something CPU intensive in the awake periods, then let it idle for a while and then repeat again.
Probably periods of several seconds will work the best.
Something like:
-CoOS
-Set idle taks to put CPU to sleep, which should be waken up in the next systick (1ms)
-1 Task only doing CPU math for 5 seconds.
-Then a delay of 5 seconds before running again. during that time the CPU should sleep for almost 1ms, wake up for very short, check that there is no other task to run, and go back to sleep.

Does something like that make sense as a measurable test?

P.S. I was thinking on doing IO to a display, but I think it would be better to measure just the CPU by itself, as an LCD can be put to sleep in other ways.

EDIT:
Ray, I am not sure how you are calculating 165mV with a 1Ohm resistor with 50mA across. I am missing something? that would give me 50mV accross the resistor. :?


mrburnette
Tue Jun 09, 2015 4:44 pm
Does something like that make sense as a measurable test?

For uC power down testing, the proof is in the actual code. Simulation will get you into the ballpark.

EDIT:
Ray, I am not sure how you are calculating 165mV with a 1Ohm resistor with 50mA across. I am missing something? that would give me 50mV accross the resistor. :?

I’m an idiot :o
My staff would never give me a calculator… especially RPN! 1-2-3 and Excel were all I had.

Ray

True story: A friend working for Tek long ago had a boss that ‘stole’ the desktop HP programmable calc, the one with the template overlays… He wrote templates and then marked-up with pencil the overlay for easy entry. The calculator sensed the particular template and knew which program to run. So, friend and colleges made an exact duplicate template carefully copying the boss’s handwriting – exact except that Pi was slightly skewed from the exact value of 3.141592654. When the boss would check a junior engineer’s work, the answer would be different. The boss would go out in the cube area to get the engineer and inevitably they would go back to the boss’s office. During this absence, one of the senior engineers would sneak in and swap the template on the calculator with the original template which of course gave the correct answer! They never got caught but had to stop the prank because the boss was getting very agitated that all the new engineers thought he was bonkers!


strawberrymaker
Tue Jun 09, 2015 8:19 pm
Isnt it already pretty much low power when you use less peripherals?
Image
Are all peripherals initialized even if there arent used?
I’d guess using npn transistors for led & vcc for the screen and a ldo with low quiescent current would be good enough. you would only need to send the starting commands of the screen again.

If im wrong ,please tell me :)

You may want to read the datasheet. it got many infos about sleep and wakeup(isnt a0 even described as wakeup?)

~Straw


ahull
Tue Jun 09, 2015 9:32 pm
@Straw, you are correct to the extent that when active, the peripherals consume significant amounts of current (well significant in the case of a battery powered project), however to get really low currents, we need to sleep too as even when idling, the processor and memory are likely to be major factors in power consumptions. To use the least amount of power, we need to put as much of the silicon to sleep as we can. The other thing that affects power is clock speed, but often this is not as simple a factor as reduce speed, save power, since getting tasks done quickly may result in longer sleep periods.

EDIT: This library (AVR specific) and the associated documentation should provide some useful insights. I have used it in the past for battery powered projects.


ahull
Tue Jun 09, 2015 10:36 pm
Another current hog is likely to be the voltage regulator. Not a worry if you are powering *directly* from batteries, since there wont be a regulator, but if you are serious about low power and your power supply is anything other than a 3.6V LiPo and a diode, or a couple of AA cells then the regulator is a serious design consideration. Here is an example of what I am talking about.

It may sound strange to think of 5mA in any context as hogging power, but where battery powered designs are concerned, it can mean the difference between days of standby battery power and weeks, months or even years. If you can reduce current from 5mA to 500uA then obviously your battery lasts 10 times as long on standby, so your little 120mAh lipo that lasts 1 day between charges becomes a more useful device that goes 10 days before you need to recover it and recharge. A 2000mAh lipo goes from 16 1/2 days to 167 days of standby, and my “Sticks O Dynamite” battery pack lasts forever or thereabouts ;) (theoretically, assuming the average lifespan of a modern commercial product) . Hence my comment about removing the power LED.


victor_pv
Tue Jun 09, 2015 11:38 pm
Initial report:
The example in the first link from Ray’s post compiles and toggles the led with a few modifications, since we got rid of toggleLed and a few more things. I think we should not have removed those, now to use all examples you need to go adding them to the sketch :(
Anyway, if you press the button twice, it turns the led off, another 2 times turn it on. I still have to power it thru a resistor and calculate comsumption:

The notes about serialusb and the rest were there already. I have not verify if it works or not yet besides toggling the pin.

// Engages stop mode on the cpu, wakes up when the onboard button is briefly pressed
// Toggles pin 3 and pin 13 every time the button is pressed/released
// Pin 3 is used to indicate that the main loop has run. It is toggled every time loop() is called.
// Normally this would be constantly flashing on/off so fast it would appear on all the time
// In this case, the loop is only finished when the button has been pressed/released.

// PROBLEMS so far : works as expected,
// : note that because it's in stop mode, you may have to
// : reset to reprogram.

// : SerialUSB.print : NOT WORKING.

#include <stdint.h>
#include <libmaple/pwr.h>
#include <libmaple/scb.h>

#define BOARD_LED_PIN 33
#define BOARD_BUTTON_PIN 32
#define BUTTON_DEBOUNCE_DELAY 1

// These are possibly defined somewhere but I couldn't find them. System Control Register
#define SCB_SCR_SLEEPDEEP 4 // Controls deepsleep(1) or sleep(0)
#define SCB_SCR_SLEEPONEXIT 2 // Controls sleeponexit (not used here)

volatile bool ledState = LOW; // Used in ISR blinkState()

void togglePin(uint8 pin) {
if (pin >= BOARD_NR_GPIO_PINS) {
return;
}
gpio_toggle_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit);
}

void toggleLED() {
togglePin(BOARD_LED_PIN);
}

uint8 isButtonPressed(uint8 pin=BOARD_BUTTON_PIN, uint32 pressedLevel=HIGH) {
if (digitalRead(pin) == pressedLevel) {
delay(BUTTON_DEBOUNCE_DELAY);
while (digitalRead(pin) == pressedLevel)
;
return true;
}
return false;
}

uint8 waitForButtonPress(uint32 timeout=0) {
uint32 start = millis();
uint32 time;
if (timeout == 0) {
while (!isButtonPressed())
;
return true;
}
do {
time = millis();
/* properly handle wrap-around */
if ((start > time && time + (0xffffffffU - start) > timeout) ||
time - start > timeout) {
return false;
}
} while (!isButtonPressed());
return true;
}

void setup()
{
pinMode(BOARD_LED_PIN, OUTPUT);
pinMode(3, OUTPUT);
pinMode(BOARD_BUTTON_PIN, INPUT);

// Just to show that the board has reset (wdt or possibly standby mode(not used here))
for (int i = 0; i < 20; ++i)
{
delay(200);
toggleLED();
}

togglePin(3);

// Wake up cpu on button press.
attachInterrupt(BOARD_BUTTON_PIN, blinkState, FALLING);

}

void loop()
{
//SerialUSB.println("HI!"); delay(100); // Only works on first iteration, before stop mode

// Clear PDDS and LPDS bits
PWR_BASE->CR &= PWR_CR_LPDS | PWR_CR_PDDS;

// set sleepdeep in the system control register
SCB_BASE->SCR |= SCB_SCR_SLEEPDEEP;

// Now go into stop mode, wake up on interrupt
asm(" wfi");

digitalWrite(BOARD_LED_PIN, ledState);
togglePin(3);

}

// Interrupt Service Routine (ISR)
void blinkState()
{
ledState = !ledState;
waitForButtonPress(); // Dangerous in ISR, I know, but quick demo only so blah.
}


bdbell
Fri Jun 12, 2015 6:04 pm
Want to thank everyone for their feedback on this, I got slammed with work so have not had time to get back to it till now. I have set up a couple of test sketches for my device – one uses the CPU scaling method I mentioned in the OP, and another using the sketch victor_pv provided (thanks!). Here are the results;

Sleep mode – 2.892mA
CPU scaled 256 – 15.954mA
No Sleep or Scale, TFT off – 48.26mA
TFT on – 89.85mA

The device is running off a single 2000mah Lion battery. I am using a Buy-Display.com 2.4″ ILI9341 TFT (without shield), my board has both 2.8 and 3.3v regulators (LP3981), a MCP73831 battery charger, DS3231M RTC, EEprom ic, TCS2046 touch controller, BMP180, SHT21, mosfet circuit for checking battery voltage, piezo buzzer and a BT module – I have disabled the buzzer, mosfet circuit, and BT module for the tests, but nothing else so far.

Will do more tests and update post when I have time.


strawberrymaker
Fri Jun 12, 2015 6:54 pm
Well, that already sounds nice :) Im interested into further tests ^^

~Straw


ahull
Fri Jun 12, 2015 10:18 pm
I’m pretty impressed so far. The voltage regulator will be a significant drain. Waking from sleep can, so far as I understand it be achieved by setting an interrupt from the RTC, as well as a button push and I suspect using the RTC might be one of the most useful method of waking up for most sleeps of significant duration. (i.e. greater than a second).

According to the datasheet it should be possible to get the processors power down to around 6uA with a further 3uA or less for the RTC and battery backed registers. This is with absolutely everything shut down, but presumably this case the processor would have to backup and restore all clocks etc.

As to creating a useful set of library functions for this, I’m open to suggestions. Sleep till a certain time, sleep for n seconds, sleep till interrupt from (event/pin transition) all seem worth while goals.


victor_pv
Sat Jun 13, 2015 2:44 pm
ahull wrote:I’m pretty impressed so far. The voltage regulator will be a significant drain. Waking from sleep can, so far as I understand it be achieved by setting an interrupt from the RTC, as well as a button push and I suspect using the RTC might be one of the most useful method of waking up for most sleeps of significant duration. (i.e. greater than a second).

According to the datasheet it should be possible to get the processors power down to around 6uA with a further 3uA or less for the RTC and battery backed registers. This is with absolutely everything shut down, but presumably this case the processor would have to backup and restore all clocks etc.

As to creating a useful set of library functions for this, I’m open to suggestions. Sleep till a certain time, sleep for n seconds, sleep till interrupt from (event/pin transition) all seem worth while goals.


ahull
Sat Jun 13, 2015 7:50 pm
victor_pv wrote:
One concern is that the deepest sleep mode does not keep the RAM. So to achieve 9uA we may need to use an external memory eeprom or flash to save the state. I just recently discovered FRAM memories, which don’t need power to keep data, but are completely random read and write like SRAM, with much lower power requirements to write as it doesn’t need to erase whole pages at once.
They are available in parallel, i2c and SPI interfaces. Seems like a lower power option for permanent storage. Save all the important information there, go to sleep, when waking up check if there is a state to restore, and then run from that state.

victor_pv
Sat Jun 13, 2015 8:13 pm
ahull wrote:

My understanding is that the power domain for the RTC/Ram is separate from the processor, so you can elect to shut it down separately, or not depending on your application (hence the separate 3uA figure for the RTC/Ram), but I’ll re-read that part of the datasheet to make sure I am not barking up the wrong tree.

ahull
Sat Jun 13, 2015 8:40 pm
victor_pv wrote:
I was having a good look at the datasheet, and seems like putting the MCU in deepsleep (clocks are lost, all peripherals shutdown, but RAM is kept) can bring the current down to 14uA to a max of 340uA. The next mode would be standby, that loses RAM.

strawberrymaker
Sat Jun 13, 2015 9:05 pm
ahull wrote:victor_pv wrote:


If we can make a library that saves the state of the peripherals and the clocks in RAM, and then put the chip in deepsleep mode, and upon wake up restores the clocks an peripherals, I think it could achieve huge savings for any sketch that needs to keep RAM.

I agree, the scenario you describe would probably be the most useful mode. Deepsleep would only be required for extreme cases (very long sleeps, or relatively small backup batteries).


victor_pv
Sun Jun 14, 2015 2:26 am
[quote=”strawberrymakerWouldnt it be a bit unnecessary to save the clock and then stopping it?

~Straw[/quote] The idea is saving the state of all the peripheral clocks so after the sleep mode stops them all, on wake up you can restore the same settings.
Of course you can just go and run the initial setup of clocks as if the system was just booting from reset, but what happens then if your sketch has been purposely stopping some clocks or reducing the speed on others?
I understand the deep sleep mode does not preserve the status of the main clock at the very least:
“When exiting Stop mode by issuing an interrupt or a wakeup event, the HSI RC oscillator is
selected as system clock. “


victor_pv
Mon Jun 15, 2015 5:14 am
I did a couple of tests:
-Using sleep mode (not deepsleep/stop), I can keep using serialusb, etc when the MCU wakes up right away. The clock is still HSE on wake up. Current reduction is negligible, around 0.5mA less than running only.
-Using deepsleep I can setup HSE, PLL, etc when the MCU wakes up, and go back to using the HSE. I did not try the peripherals, but I suspect I may just need to re-enumerate USB. I would expect the rest to work. The datasheet only says the clock source goes to HSI but does not say anything about losing peripheral clock settings. Systick can not be used in this mode.
-Using the frequency division I can go down to DIV16 and still have the MCU respond to a button, but any lower than that it seems like I lose a lot of button pushes. May work better for other interrupts sources like the RTC. The current reduction is significant, I forgot to write down the numbers, but I think at div16 I was using around 1/10 the mA compared to full speed.

Conclusions for me:
-Deepsleep is the best if you need to seriously save energy, but we need to set the MCU back to using the Xtal, and some peripherals may need to be reset to.
-Sleep by itself seems to have very little effect, but causes no impact to anything. It may be helpful combined with shutting down clock to several peripherals.
-Setting a prescaler to up to DIV8 seems like a good way to save energy if your sketch doesn’t need the extra speed. The speed grows faster than the current usage (or reduces faster than the reduction in current), so as Andy mentioned, it may be better to complete a task quickly and then go to deepsleep. If you task is waiting for anything like a button or processing some slow input, then a reduction in speed may be a pretty good way to save power.
-I belive the most saving in deepsleep mode comes from shutting down peripheral clocks. For anything running on batteries we should shutdown all unused peripherals right away, as libmaple initializes the clock to pretty much everything on start.

So the tactic for me will be:
-Shutdown clock to anything not needed
-Set the prescaler division to the one that completes the tast the fastest without being so fast that I need to wait on something else.
-Deepsleep as soon as possible.

I copied the code to set the HSE and PLL and select HSE as clock from the libmaple init routines and worked fine. Whenever I have more time I will see if I can test some peripherals after deep sleep, like SPI, serial.usb(after re-enumeration) and see if they need some other reset or not.


Timoteo
Fri Dec 21, 2018 5:35 pm
Hello Victor_pv,

Did you had the time to perform some other tests?
As my approach to reduce power is to deactivate unused peripheral would like to know about your results and maybe some direction to follow.

All the best,
Tim


Leave a Reply

Your email address will not be published. Required fields are marked *