Skip to content

Commit 49e0f01

Browse files
author
Amanda Butler
authored
Create power_mange.md
Apply changes from PR #1095 to v5.13.
1 parent 0807487 commit 49e0f01

File tree

1 file changed

+268
-2
lines changed

1 file changed

+268
-2
lines changed
Lines changed: 268 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,269 @@
1-
# Power Optimization
1+
# Power optimization
22

3-
[A document about power management]
3+
IoT devices often claim they can run ten years on one battery, but building low-powered nodes is challenging. Complex products use multiple threads, a variety of active timers and sometimes a second core handling network connectivity. Sensors and other peripherals also use power. To help you manage these complexities while using the least amount of power, Mbed OS contains a number of low-power features, including low power tickers, tickless mode and the sleep manager. This tutorial explains how to use these features.
4+
5+
## Power modes
6+
7+
Mbed OS contains three [power modes](../apis/power-management-sleep.html):
8+
9+
- Active - The MCU and all clocks are running.
10+
- Sleep - The core system clock is disabled. This eliminates dynamic power that the processor, memory systems and buses use.
11+
- Deep sleep - In addition to the core system clock, all high-frequency clocks are disabled, and the [SysTick](../apis/rtos.html) is disabled.
12+
13+
Switching between these power modes occurs automatically. When all threads in the system are idle, Mbed OS yields control to the [idle thread](../apis/idle-loop.html). The idle thread then invokes the sleep manager, which brings the system to sleep or deep sleep mode. The idle thread also sets a timer to wake up the system again, but you can also wake up the system through an external interrupt or the low power ticker.
14+
15+
For example, this application automatically brings the system to sleep mode between blinking the LED:
16+
17+
```cpp NOCI
18+
#include "mbed.h"
19+
20+
DigitalOut led(LED1);
21+
22+
int main() {
23+
while (1) {
24+
// blink the LED
25+
led = !led;
26+
// sleep for two seconds
27+
wait_ms(2000);
28+
}
29+
}
30+
```
31+
32+
The same principles also apply when using multiple threads or while using the [EventQueue](../apis/eventqueue.html). When all threads are idle, the system goes to sleep.
33+
34+
This leads to significant energy savings without any modification from you. For example, these images show the program mentioned above running first without sleeping, then with sleep mode enabled and finally with deep sleep mode enabled:
35+
36+
<span class="images">![No sleep](../../../images/idle-3.png)<span>Without sleeping, the MCU always consumes at least 43 mA.</span></span>
37+
38+
<span class="images">![Sleep](../../../images/sleep.png)<span>By enabling sleep, the idle current goes down to 15 mA with clear peaks whenever you run some code.</span></span>
39+
40+
<span class="images">![Deep sleep](../../../images/deepsleep1.png)<span>In deep sleep mode, the idle current goes down to 358 uA. The jitter is noise from the energy profiler.</span></span>
41+
42+
<span class="notes">**Note:** The current consumption differs wildly between devices, even when comparing between MCUs from the same vendor. Look at the data sheet for your MCU to get an indication of power consumption in sleep and deep sleep mode.</span>
43+
44+
### Sleep and deep sleep
45+
46+
Whether the sleep manager puts the MCU in deep sleep instead of sleep depends on:
47+
48+
- Whether the device has low-power tickers available. These are required to wake up the MCU when high-frequency tickers are disabled. If low power tickers are available, the `DEVICE_LPTICKER` macro is set.
49+
- Whether [tickless mode](../porting/tickless.html) is enabled. In tickless mode, the system can function without SysTick running. This is either enabled by the device, or by setting the `MBED_TICKLESS=1` macro.
50+
- If any bus or driver is active that relies on the high-frequency clock, such as the SPI bus when doing asynchronous operations or a high-frequency [Timer](../apis/timer.html).
51+
- The time until waking up, as waking up from deep sleep can take up to 10 ms.
52+
53+
To help you understand how much time the MCU spends in active, sleep and deep sleep modes and to determine what might be blocking deep sleep, you can enable CPU statistics and the sleep tracer.
54+
55+
### CPU statistics
56+
57+
To enable CPU statistics, which show you how much time is spent in various modes, add the following line to the `target_overrides` section of your `mbed_app.json` file:
58+
59+
```
60+
"platform.cpu-stats-enabled": 1
61+
```
62+
63+
You can now call the `mbed_stats_cpu_get()` function to retrieve information on sleep behavior. For example:
64+
65+
```cpp NOCI
66+
#include "mbed.h"
67+
#include "mbed_stats.h"
68+
69+
static DigitalOut led(LED1);
70+
71+
int main() {
72+
while (1) {
73+
led = !led;
74+
wait_ms(2000);
75+
76+
mbed_stats_cpu_t stats;
77+
mbed_stats_cpu_get(&stats);
78+
printf("Uptime: %llu ", stats.uptime / 1000);
79+
printf("Sleep time: %llu ", stats.sleep_time / 1000);
80+
printf("Deep Sleep: %llu\n", stats.deep_sleep_time / 1000);
81+
}
82+
}
83+
```
84+
85+
When your device supports low-power tickers and tickless mode, you see something like:
86+
87+
```
88+
Uptime: 2099 Sleep time: 0 Deep Sleep: 2098
89+
Uptime: 4202 Sleep time: 0 Deep Sleep: 4197
90+
Uptime: 6305 Sleep time: 0 Deep Sleep: 6296
91+
Uptime: 8407 Sleep time: 0 Deep Sleep: 8394
92+
Uptime: 10510 Sleep time: 1 Deep Sleep: 10493
93+
Uptime: 12613 Sleep time: 1 Deep Sleep: 12591
94+
```
95+
96+
(The uptime does not go up by exactly 2000 ms because you also spend time writing the data out over serial).
97+
98+
### Blocking deep sleep
99+
100+
As stated before, some drivers and components can block deep sleep, either because they need access to the high-frequency timers (such as the `Timer` object), or because they cannot handle the latency that waking up from deep sleep introduces. This happens for example when attaching a receive interrupt on a UART. By the time the interrupt fires, the data that was written to the UART could no longer be there.
101+
102+
#### Acquiring a sleep lock
103+
104+
If your code requires blocking deep sleep, you can acquire a sleep lock. While the lock is active, Mbed OS will not bring the MCU into deep sleep mode. You can do this either by calling:
105+
106+
```cpp NOCI
107+
sleep_manager_lock_deep_sleep();
108+
109+
// ... do your operation
110+
111+
sleep_manager_unlock_deep_sleep();
112+
```
113+
114+
Or through the [DeepSleepLock](../mbed-os-api-doxy/_deep_sleep_lock_8h_source.html) RAII object. While the object is in scope, deep sleep is locked.
115+
116+
```
117+
// some code here
118+
{
119+
DeepSleepLock lock;
120+
// Code in this block runs with the deep sleep mode locked
121+
}
122+
// deep sleep mode is restored to the previous state
123+
```
124+
125+
In addition, the `DeepSleepLock` object also has `lock` and `unlock` functions, which are useful for asynchronous operations.
126+
127+
#### Seeing active sleep locks
128+
129+
The sleep manager maintains a list of all active deep sleep locks and can log these whenever the device goes into sleep mode. This helps you determine what is blocking sleep. To enable these log messages, add the following macro to the `macros` section of your `mbed_app.json` file:
130+
131+
```
132+
"MBED_SLEEP_TRACING_ENABLED"
133+
```
134+
135+
For example, you can create a `Ticker` object that blinks the LED. The `Ticker` requires the high-frequency timers to be active:
136+
137+
``` NOCI
138+
#include "mbed.h"
139+
140+
static DigitalOut led(LED1);
141+
static Ticker ticker;
142+
143+
void blink() {
144+
led = !led;
145+
}
146+
147+
int main() {
148+
ticker.attach(&blink, 2.0f);
149+
}
150+
```
151+
152+
When you run this code with tickless enabled, this prints:
153+
154+
```
155+
# at the beginning of the program
156+
LOCK: Ticker.cpp, ln: 61, lock count: 1
157+
158+
# every time the device goes to sleep
159+
Sleep locks held:
160+
[id: Ticker.cpp, count: 1]
161+
Sleep locks held:
162+
[id: Ticker.cpp, count: 1]
163+
```
164+
165+
Every time the device goes to sleep, this tells you a sleep lock is active. It also tells you where the lock was declared. To bring this device into deep sleep, you therefore need to get rid of the lock. You can either do this by using a low-power variant of the same class (`LowPowerTicker` in this case), or by only keeping the object that locks active when you actually need it. For example, a Wi-Fi module that uses a UART interrupt blocks deep sleep, so only keep the interrupt active when you expect a response from the module. For PWM, free the interface whenever you're not using it as to not block deep sleep.
166+
167+
**Tip:** Too much output from the sleep tracer? For a cleaner log, you can disable the code that logs all active locks when going to sleep in [mbed_sleep_manager.c](https://github.com/ARMmbed/mbed-os/blob/8e819de43e88a11428f3f7d21db7f6e7a534058a/platform/mbed_sleep_manager.c).
168+
169+
#### Mbed OS drivers that block deep sleep
170+
171+
This is a list of core Mbed OS drivers that block deep sleep:
172+
173+
- [Ticker](../apis/ticker.html) - if you don't need the precision of the high-frequency timer, you can use [LowPowerTicker](../apis/lowpowerticker.html) instead.
174+
- [Timeout](../apis/timeout.html) - if you don't need the precision of the high-frequency timer, you can use [LowPowerTimeout](../apis/lowpowertimeout.html) instead.
175+
- [Timer](../apis/timer.html) - if you don't need the precision of the high-frequency timer, you can use [LowPowerTimer](../apis/lowpowertimer.html) instead.
176+
- [SPI](../apis/spi.html), when using the asynchronous APIs.
177+
- [I2C](../apis/i2c.html), when using the asynchronous APIs.
178+
- [CAN](../apis/can.html), if there is an interrupt attached.
179+
- [PWM](../apis/pwm.html), after writing a value to a pin.
180+
- Every class that inherits from `SerialBase`, such as [Serial](../apis/serial.html), if it has a receive interrupt attached. Additionally, deep sleep is blocked temporarily while using the asynchronous APIs for reading and writing.
181+
182+
Device-specific drivers (such as USB stacks) and networking drivers might also block deep sleep.
183+
184+
## Advanced topics
185+
186+
### Inner workings
187+
188+
When all threads are paused, the system [idle hook](../apis/idle-loop.html) is invoked. By default (though you can override this behavior), this yields control to the sleep manager. The sleep manager then either calls `hal_sleep()` or `hal_deepsleep()`, depending on whether deep is locked or permitted. The device implements these HAL functions, according to the following specifications:
189+
190+
- `hal_sleep()`:
191+
- Wake-up time should be less than 10 us.
192+
- The MCU should be able to wake up from any internal peripheral interrupt or from an external pin interrupt.
193+
- All peripherals need to operate the same way they do in active mode.
194+
- `hal_deepsleep()`:
195+
- Wake-up time should be less than 10 ms.
196+
- The MCU should be able to wake up from the low-power ticker, an external interrupt and the watchdog timer.
197+
- High-speed clocks should be turned off.
198+
- RTC is running and keeps time.
199+
200+
Both HAL sleep functions work like an Arm Wait For Interrupt (WFI) instruction, where the function returns when there is a pending interrupt. To achieve this, the sleep manager calls these functions from a [critical section](../apis/criticalsectionlock.html). Often (though this is device specific) `hal_sleep` is implemented as just a `__WFI()` call, and deep sleep is the same call but surrounded by power control settings that limit the wake-up sources and functioning peripherals.
201+
202+
This is also why the MCU wakes up from sleep every millisecond when tickless is not enabled. In nontickless mode, SysTick needs to fire every millisecond and does this by setting an interrupt on the usticker. Right after the SysTick, the sleep manager puts the MCU back to sleep. However, this also means that in nontickless mode, you can't put the MCU in deep sleep because the wake-up latency is bigger than the SysTick interval.
203+
204+
For more information on the design of tickless and the sleep manager, please see the [office hours video with Bartek Szatkowski](https://www.youtube.com/watch?v=OFfOlBaegdg).
205+
206+
### Hibernate mode without RAM retention
207+
208+
All sleep modes in Mbed OS are implemented with RAM retention, but some MCUs have even lower power modes that completely stop the MCU and won't retain any information. After waking up, the MCU starts execution from the beginning of the program. Typically the only way to wake up from this mode is through an interrupt on a wake-up pin or from the low power ticker.
209+
210+
### Measuring power consumption
211+
212+
Accurately measuring the power consumption of deep sleep is challenging because of the huge dynamic range required, with current ranging from 1 uA to 100 mA. Also, the MCU is often awake for a very short amount of time, so measuring by hand is not practical. In addition, almost all Mbed Enabled development boards have a debug circuit that draws power (often much more than the MCU itself). Therefore, for accurate power measurement, you need:
213+
214+
- A current measurement probe. Some options that we have had good experiences with:
215+
- [QOITECH Otii](https://www.qoitech.com/) - acts as both a current and voltage measurement device and as a flexible power source for your device. This is probably your best option if you can spare $589.
216+
- [ULINKplus](http://www2.keil.com/mdk5/ulink/ulinkplus) - debugging probe that can be used together with Keil MDK that also provides current measurement. This is a good option if you already have an MDK license and is available for $690.
217+
- [SiLabs Giant Gecko](https://os.mbed.com/platforms/EFM32-Giant-Gecko/) - development board with a circuit that can also measure current [of an external MCU](https://www.silabs.com/community/mcu/32-bit/forum.topic.html/how_to_use_an_stkto-jLpQ). At $29, this is a much less expensive alternative to the dedicated probes, but it can only output 3.3V, which can be a problem when powering 5V peripherals.
218+
- [Nordic Power Profiler kit](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/Power-Profiler-Kit) - originally made for Nordic development boards, you can also use this kit to measure current of any other MCU. It is available for $80 but can also only output a maximum of 3.3V.
219+
- A way of powering the MCU (and preferably its peripherals) without powering the debug circuit. This is very specific to the development board.
220+
221+
1. Disable any peripherals that draw power but are not part of your application (such as a power LED). This might require you to physically remove components.
222+
223+
<span class="images">![Current measurement setup for this article](../../../images/sleep-setup-jan.JPG)<span>Current measurement setup for this article</span></span>
224+
225+
This is the current measurement setup for the images earlier in this article. The STLink debug circuit is physically disconnected from the development board to avoid powering the debug circuit during measurement. The jumper wires from the STLink to the development board are there to reprogram the device. On the NUCLEO-F446RE board resistor R32 is removed to disable the power LED. The EFM32 Giant Gecko acts as the power source for the board, connecting VMCU on the Giant Gecko to 3.3 V on the NUCLEO-F446RE (and GND to GND). You can use Simplicity Studio to see the current measurement.
226+
227+
Unfortunately there is no generic way of doing this. There are probably hints in the help guide for your development board.
228+
229+
### Choosing and shutting down peripherals
230+
231+
It might seem like an open door, but putting the MCU to sleep is only part of a low power design: Peripherals can draw much more power than the MCU. The LED in the beginning of the article is drawing ~2.8 mA, much more than the rest of the circuit in deep sleep. Therefore, make sure to choose components that fit your power budget and shut peripherals down when you don't use them. Radios often have sleep modes you can invoke, so make sure your drivers use these.
232+
233+
Another option to consider is using a lower voltage design. Many MCUs can run at 1.8 V instead of 3.3 V, and choosing peripherals that can run on the same voltage drastically reduces your overall power consumption.
234+
235+
## Troubleshooting
236+
237+
### Stack overflow when enabling sleep tracing or CPU statistics
238+
239+
When enabling sleep tracing or CPU stats, the idle thread has to allocate more data. On some devices, this leads to stack overflows on the idle thread. If you encounter this, you can increase the stack size of the idle thread by adding the following section under `target_overrides` in `mbed_app.json`:
240+
241+
```
242+
"rtos.idle-thread-stack-size": 1024
243+
```
244+
245+
### Device not entering deep sleep even though tickless is enabled
246+
247+
On some devices, the interrupt latency when running on the low-power tickers causes the device to drop bytes when running the serial at higher baud rates (such as 115,200). To mitigate this, these devices run tickless from the microsecond ticker instead of the low-power ticker, and this blocks deep sleep.
248+
249+
If your application does not require high baud rates, you can set the following macro to re-enable tickless from the low-power ticker:
250+
251+
```
252+
MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER=0
253+
```
254+
255+
### Device wakes up from deep sleep every second (or other period)
256+
257+
Some devices wake from deep sleep for a small period every second, even when the device is instructed to wake up much later.
258+
259+
<span class="images">![Blinky with 10 second interval on DISCO-L475VG-IOT01A1](../../../images/deepsleep-wakeup.png)<span>Blinky with 10 second interval on DISCO-L475VG-IOT01A1</span></span>
260+
261+
This is related to the maximum timeout of the hardware low power ticker. It can only be asleep accurately for one second, so the device wakes up every second, and Mbed OS brings the device back into deep sleep afterward. You often can override this behavior by choosing a different clock source, but this is device specific. Look at the data sheet for your MCU. On the DISCO-L475VG-IOT01A1, you can override this by adding the following line to the `target_overrides` section of your `mbed_app.json`:
262+
263+
```json
264+
"target.lpticker_lptim_clock": 4
265+
```
266+
267+
### Device does not sleep in bare-metal mode
268+
269+
The sleep manager does not load when running Mbed OS in bare-metal mode. We may add this capability in a future release. If you are developing in bare metal mode, call the `sleep()` function manually, and make sure you set up the wake up source.

0 commit comments

Comments
 (0)