The exploration into low-power sleep modes continues. An STM32F103 draws just 3 µA in standby mode, but that’s not the end of the story. The STM32L0xx µCs are more modern and even lower-power. So I just had to try the same thing again with an STM32L031:

This is a small, but still hand-solderable, TSSOP-20 package. There’s even a TSSOP-14, if you’re willing to switch to L011 (2K ram instead of 8K). It takes very little to hook up this chip, just the µCurrent to measure µA’s and an ST-Link to power it and program it.

Here’s my test code, using the same approach as before: start up, blink the LED once, enable the watchdog, and put the µC into standby mode. This turns just about everything on the chip off. Except that independent watchdog, of course:

#include <jee.h>

static void delay () {
    for (int i = 0; i < 1000; ++i) __asm("");

int main() {
    delay(); // led is off

    PinA<1> led;
    delay(); // led is on

    delay(); // led is off

    Iwdg dog (6);  // approx 26s with max reload count of 4095
    dog.reload(100); // shorten watchdog cycle to about 0.7 s


And sure enough, the resulting low-power mode consumes just 0.5 µA - very impressive!

There’s a nasty catch, though: the startup from standby (which does an internal reset) takes about 1.6 milliseconds before the first line of code in main() gets control, and the chip draws about 300 µA during this power-up phase:

That’s a lot of power, relatively speaking. The red line in the scope capture above is the integral of the current, i.e. the total energy consumed, which turns out to be a “whopping” 500 µCoulombs. When considered in relation to the 0.5 µA sleep current, that’s fairly substantial: the equivalent of sleeping 1000 seconds is “thrown away” in just 1.6 ms!

Update - The comments above were way off (the red scale on the scope is wrong). The proper value is 0.5 µC, i.e. 1 sec of energy “thrown away” in 1.6 ms, not in any way as dramatic as what I had originally written down.

By the way, the spikes you see appear to be from the 38 kHz ultra low-power internal clock, needed for the watchdog’s countdown. They are brief and use very little energy.

But the real reason for stressing this, is that those 1.6 ms don’t match the specifications in the datasheet at all. The datasheet suggests that the µC can wake up in 65 µS (typical).

I’ve been pulling my hair out over this issue (figuratively … no worries!), but can’t figure out why the delay is there. It appears to be due to a wait for Vrefint to stabilise, but the FWU (fast wake-up) bit should have disabled this! I’ve even explicitly disabled the brown-out reset (BOR) circuitry in the option bytes, although it’s off by default, as far as I can tell.

Here’s my test code, now reduced to its bare essentials, yet still with 1.6 ms startup delay:

#include <jee.h>

int main() {
    MMIO32(Periph::iwdg+0x00) = 0x5555; // unlock PR
    MMIO32(Periph::iwdg+0x04) = 1;      // max timeout is 800ms
    MMIO32(Periph::iwdg+0x00) = 0xCCCC; // start watchdog

    constexpr uint32_t scr = 0xE000ED10;
    MMIO32(scr) |= (1<<2); // set SLEEPDEEP

    MMIO32(Periph::rcc+0x38) |= (1<<28); // PWREN
    MMIO32(Periph::pwr) |= (1<<10) | (1<<9) | (1<<1); // FWU, ULP, PDDS


My conclusion so far: the L031’s standby current is a fantastically low 0.5 µA, but the subsequent startup energy loss really undoes a lot of its potential energy savings.

Suggestions are very welcome, if you have any tips on how to chase and resolve this!


After some further sleuthing and discussion, it turns out that the above 1.6 ms delay can be reduced further. All the way down to 85 µs, in fact. See comments below for details. Here is the final scope capture with this – much improved – result:

I’m now using an L011 instead of an L031 µC, but the differences between them are minor (less RAM and flash memory in the L011). The red line is cumulative energy consumption, as before, and comes to a phenomenally low 0.04 µCoulombs before a LED is turned on.