An F103 sleeping with a current consumption of 3 µA is very impressive. This translates to about 7 years of “run” time on a CR2032 coin cell (although not doing anything useful). That is three orders of magnitude lower than the ≈ 5 mA of standard run mode at 8 Mhz.

And it’s very much in line with the capabilities specified in the STM32F103 datasheet:

Our demo actually exceeds the specs given in the table above, which is down to 3.4 µA. Only the low-power “LSI” oscillator and the independent watchdog need to be running.

The low-power chase

The good news is that the results so far have been obtained with a very simple demo app and very little going on behind the scenes. But I assure you: we just got lucky this time.

Very often, the chase begins by reading through the various reference manuals, making sure that all the proper actions have been taken. Beware of the law of diminishing returns!

The other aspect requiring careful attention, is the schematic of the Nucleo-F103 board, which (luckily) is public and well-documented. Am I measuring the current consumption of the µC, and nothing else? Are there leakage paths or capacitve discharge effects?

Lastly, are my measurement tools really giving me the proper information? There are many possible pitfalls, burden voltages, signal loops, noise pickups, additional current leakage (or current injection!) paths.

At microamp levels, low-power consumption details can become extremely complex.

It gets even harder further down the road: a 3.3 MΩ resistance (a smudgy finger, leaving a residue on the PCB?) leaks 1 µA at 3,3V. What about meter accuracy / calibration errors?

Measuring current

Down to 100 µA or so, I use my simple but proven multimeter (the 4.5 digit UT61E). There are important instrument details such as its burden voltage which can have a great effect on such measurements, and even on the ability to use a certain meter in-circuit.

But the main issue is really with the dynamic range requirements: a µC switching between milliamps while (briefly) running, to microamps while in low-power sleep can tax even fairly advanced current meters, since this is a three order-of-magnitude current change. In the low-current range, it’ll overload (and the burden voltage may get in the way), and in a higher range the low-power values will get get lost in the readout and meter accuracy.

In a nutshell: if the dynamics won’t bite ya', the (lack of) resolution and accuracy will!

With the µCurrent

The µCurrent by Dave Jones is a very popular adapter to help measure current in the mA, µA, and even nA range. Its operation is very simple, and falls into 3 ranges: ± 0..1250 nA, ± 0..1250 µA, and ± 0..1250 nA, which all produce a corresponding output voltage in the range ± 0..1250 mV that is much easier to measure.

When hooked up into our circuit, I get 92 mV on the multimeter in the mid-range setting, which corresponds to 92 µA. Not quite the same as the 45 µA measured earlier, but these sorts of variations are to be expected until you really narrow down all the noise and leakage paths in the measurement setup. It’s still an “in the same ball-park” outcome.

The 6-decade range of the µCurrent can be both a blessing and a curse: given the high dynamic range we need for µC based wake/sleep current changes, it’s not easy to match it up with the options of the µCurrent. Luckily, we can simply let it overload a bit, i.e. using the µA range, it doesn’t really matter that the spikes will be several mA, i.e. many volts (well above the capabilities of the µCurrent). On the µA range, the µCurrent uses a 10 Ω shunt, so it’s ok for a few mA to flow, and drop a few tens of mV.

With an oscilloscope

Another option is to measure voltage across a known resistance “shunt” (this is what most current meters do internally anyway). A really useful approach is to use an oscilloscope instead of a multimeter, because a “scope” captures far more dynamic results. Overload is less of an issue: a scope can zoom in on the area of interest, even if the rest is off-scale.

The key is to dimension the shunt resistor appropriately. With a 10 Ω resistor, 1 µA will produce 10 µV and 10 mA will produce a 100 mV signal. The lower range will be nearly invisible on the scope, while the higher end will affect the supply voltage reaching the µC: dropping Vcc from 3.3V to 3.2V is probably acceptable, but not insignificant.

For starters, I’m going to try this with my oscilloscope’s built-in 50 Ω mode (intended for RF coax cable termination). This leads to the following signal levels and voltage drops:

Current Reading Vcc Comments
1 µA 0.05 mV 3.3V can’t see this on my scope
10 µA 0.5 mV 3.3V probably visible, with some noise
100 µA 5 mV 3.3V easily viewed
1 mA 50 mV 3.25V easily viewed
10 mA 500 mV 2.8V Vcc drop probably ok, may reduce accuracy
20 mA 1.0 V 2.3V Vcc drop is probably unacceptable

Here’s where things can become fascinating (also illustrating the amazing levels of insight an oscilloscope can offer), once you get used to tweaking all its little knobs:

What’s going on here?

Each horizontal division is 2 ms wide, so we must be looking at the 10 ms LED “ON” time. But what’s that 3 ms gradual ramp? And why that extra high 1 ms spike?

Likewise, each vertical division is 100 mV high. Measured across 50 Ω, that’s 2 mA per division. Looks like the LED is drawing about 3 mA. Buit what is this 7.5 mA spike?

What about those bumps, 1 ms apart? That must be the SysTick timer, firing at 1000 Hz. They don’t appear right away, maybe the first part is some internal start-up? If so, why?

What can also be seen is that in low-power standby mode, we can’t properly determine the current consumption on this capture. It’s far too close to zero.

Let’s crank up the sensitivity to the max, and ignore the overloaded middle section:

Note the reduced horizontal scale (10 ms/div), and the maximally increased vertical scale (1 mV/div, i.e. 20 µA/div). The missing bit in the middle is that big LED-ON hump, way out of scale on this image. Let’s average 8 captures to even out the noise for a cleaner signal:

This shows that the sleep mode current is about 3 divisions high (the zero baseline is the little yellow arrow on the left), it looks like standby mode draws about 60 µA (not 45 µA).

One final value, shown as f(Tr) on the bottom: that’s how often the scope is triggering. If we calculate the inverse, this confirms that the watchdog triggers once every 13 seconds. Note that the frequency shown is milli-, not mega Hertz. The 50 Hz hum from the previous capture is now averaged out, because its phase is not correlated to the wakeup triggers.

As I’ve said before, the oscilloscope really is the printf of the electronics world!

With both

The next step is to combine the oscilloscope readout with the µCurrent as input adapter:

This is in the mid-range “1 µA = 1 mV” setting. As you can see, we get the same shape, but there are also several differences:

The overload and ringing are due to the fact that the µCurrent uses an op-amp internally to provide the necessary amplification. It’s actually doing really well, and is based on a high-grade op-amp. But there simply are limits: the output voltage swing is limited by the internal µCurrent battery voltage, and the overload really disturbs the op-amp a bit.

Another small error to be aware of is op-amp bias. When shorted out, the µCurrent read-out shows a very acceptable 4 mV (it should be exactly 0 mV, in an ideal world).

For zooming in on the microamps, and later maybe also on the nanoamps, the µCurrent will probably be a very useful tool, especially combined with an oscilloscope. But for the full picture of high and low current levels, we’re always constrained by physics, resolution, accuracy, and instrument capabilities. Whether it’s a multimeter, µCurrent, or oscilloscope.

Update - I’ve adjusted the article to reflect the proper results (3 µA, not my earlier 45 µA).