Computing stuff tied to the physical world

RF12 power optimization

In AVR, Hardware, Software on Dec 1, 2011 at 00:01

Here’s a small evolutionary change to the RF12 driver, to squeeze one more drop of power consumption out of it.

This is the code for which I’m trying to optimize power consumption:

Screen Shot 2011 11 19 at 11 31 16

The Sleepy::loseSomeTime() call is the big power saver. It puts the ATmega into a total power down mode, except for the watchdog timer, which is used to get it back out of this comatose state. So we’re sleeping for 10 seconds.

Around it are the rf_sleep() calls needed to put the RFM12B into low-power mode as well.

And lastly, the rf12_sendWait(3) call does something pretty nifty: it puts the ATmega into full power down mode between each byte sent to the RFM12B while transmitting. This requires a non-standard fuse setting in the ATmega – it only works with a ceramic resonator or the internal clock oscillator, not with a crystal: wake up out of power down within a few clock cycles.

The most efficient mode turns out to be with the ATmega running at 8 MHz off the internal RC oscillator (which starts up really fast). With default Arduino’ish settings, you have to use mode 2, i.e. a less extreme power down mode so it can wake up fast enough.

Here’s one complete transmission of a 8-byte payload (scope details to follow tomorrow):

SCR33

Each vertical division is 5 mA current draw (the voltage drop across a 10 Ω series resistor). You can see the ATmega turn on, drawing 5 .. 9 mA, and the RFM12B in transmit mode consuming about 23 mA.

The red line is pretty advanced stuff: it integrates the current over time – which is equivalent to the amount of charge consumed. At the end of the trace, this leads to a result of 7.22 microcoulombs per packet sent. One way to interpret this (thanks, Jörg – see comments), is that you could send one packet per second on an average current of less than 8 µA (hm, I think that should be 80 µA).

The “blips” are when the ATmega wakes up and feeds another byte to the RFM12B. In detail (edited image):

SCR16

These blips take about 32 µS @ 5 mA, which is what it takes to communicate with the RFM12B on the SPI bus at 2 MHz. The reason is that the RFM12B supports a 2.5 MHz maximum SPI rate (it turns out that this limitation only applies for data read from the RFM12B module).

The blips repeat 18 times, every 162 µS. Why 18? Well, an RF12 data transmission looks as follows:

RF12 packets

That’s 9 bytes of overhead, plus the 8 payload bytes, plus a trailing interrupt to shut down the RFM12B once the transmission is over.

For completeness – in case you think I can’t count blips: there’s one more activity peak at the start. That’s the call to rf12_canSend(), to check that the RFM12B is ready to start a transmission (which then takes 250 µs to start).

This is probably the limit of what you can push out of power savings with an ATmega (or ATtiny) and an RFM12B. Well, apart from: 1) sending less data, 2) increasing the transmit data rate, or 3) decreasing transmitter power.

When re-using the same code with “rf12_sendWait(2)” and running with normal fuse settings at 16 MHz using a ceramic resonator, it uses only slightly more charge – 7.70 µC, i.e. 7% more. So while this was a nice exercise, it’s not really a major improvement.

All in the name of nanowatt yak power s(h)aving…

  1. Forget the Yak – is that a Hameg in view? Ace !

    Has productivity leapt or hit a temporary dip while you figure out all those great options ? ;-)

    • Heh, you noticed? … yes, there’s a learning curve, but the Rigol was a truly excellent intro.

      Not quite the sticker shock of this thing, but hey, at least I get to keep the house.

  2. You finally got your new toy! Wow, already the screen shot looks fantastic. Can you tell us the exact model?

    With ~8uAs per transmission, this means that a 10uA supply current should be roughly enough to make one transmission per second. This is a very good result. As most of the juice is taken by the RFM12, this result should not change much when using other ‘lower power’ controllers.

    • Hm, wait – 23 mA every 3 ms out of 1000 ms, gives me a 70 µA average. I think the above calculations are off by a factor 10 – weird. Scope details coming in tomorrow’s post, BTW.

    • OK, so my stomach feeling was right that there is something wrong with this value …… but it did not reach up to the brain, otherwise I could have made the same rough calculations that you did now :-)

  3. Hi,

    Is there any disadvantage to doing the power down’s?

    i.e should you use code like this even when using mains power?

    Thanks,

    Ian

    • Only drawback I can see is the need to set fuses differently for the rf12_sendWait(3) trick.

  4. Nice work JC! I am really interested in this power saving stuff. One more thing I have been thinking about is changing the prescaler at runtime (i.e. change the ATmega/ATtiny to 1Mhz and then do the power down). Do you think this will save any more power, or is this completely marginal? It seems you got a nice test setup going now, could you please test this?

    • One of the big power savings in low power mode comes from switching off the clock generator. So it does not matter to which frequency it is set.

    • There was a post about pre-scaling (drat, I can’t find it back!) – the main outcome was that although the power consumption will be lower, the same code also ends up taking more time. In the end, I think my conclusion was that you’re as well off just running full speed and going back to power down ASAP.

      It does help when running at low voltages, since max clock rates depend on VCC. That’s probably one reason why the ATmega/ATtiny can start up with a ÷8 pre-scaler (which you can then change in software once running). With the pre-scaler, the MPU will always work, regardless how close it is to its low VCC limit.

  5. If you don’t want to set fuses though, you should use the above code with rf12_sendWait(2), even on mains?

    • Sure, why not? If you want to wait for the end, might as well do it with less energy…

  6. about the factor 10 : sounds like the x10 switch on the scope probe, maybe ?

    • Nope – this scope comes with probes which automatically pass the setting on through an extra pin (could be a 1-wire bus or just a simple resistor). I also use a simpler probe, but that’s the first thing I checked. It’s probably a math mistake, I think I still need to add the time scale in there (somehow). Either that or just make it multiply by a constant factor. Still learning the ropes… (or is that called “fighting technology”, perhaps?)

    • It gets weirder. Using a resistor to draw a constant current, I can check the coulombs integration result. At first I thought that maybe I’d have to multiply by the number of horizontal divisions (which is 12). But now comes the crazy bit: the proper result seems to come out if I multiply the integration result by … 11 -huh?

  7. I’m trying the above, but with sendWait(2) – it’s transmitting the values over wireless perfectly, but it seems to be corrupting the serial output and outputting funny characters.

    My code is here: https://github.com/ichilton/sketchbook/blob/master/test_sender/test_sender.pde

    If I comment out the sendWait and the loseSomeTime calls, it stops doing it and looks fine on serial. If I put either of them back though, serial gets corrupt again.

    Any ideas?

    • Yes – please report this on the forum and I’ll reply. The explanation will be of use to more people, I suspect.

  8. Hmm – if your “constant” current source is R in series with the 27v bench supply, the feed sags ~10% between 0 and 3v back emf ?

Comments are closed.