This continues where yesterday left off, trying to wait less than 16 milliseconds using as little power as possible.
First off, I’m seeing a lot of variation, which I can’t explain yet. I decided to use a standard JeeNode SMD, including regulator and RFM12B radio, since that will be closer to the most common configuration anyway.
Strangely enough, this sketch now generates a 704 µS toggle time instead of 224 µs, i.e. 44 processor cycles per loop() iteration. I don’t know what changed since yesterday, and that alone is a bit worrying…
The other surprise is that power consumption varies quite a bit between different units. On one JN SMD, I see 1.35 mA, on another it’s only 0.86 mA. Again: I have no idea (yet) what causes this fairly substantial variation.
How do we reduce power consumption further? The watchdog timer is not an option for sleep times under 16 ms.
The key point is to find some suitable interrupt source, and then go into a low-power mode with all the clock/timing circuitry turned off (in CMOS chips, most of the energy is consumed during signal transitions!).
Couple of options:
- run the ATmega off its internal 8 MHz RC clock and add a 32 KHz crystal
- add extra circuitry to generate a low-frequency pulse and use pin-change interrupts
- connect the RFM12B’s clock pin to the IRQ pin and use Timer 2 as divider
- add a simple RC to an I/O pins and interrupt on it’s charge cycle
- use the RFM12B’s built-in wake-up timer – to be explored in a separate weblog post
Option 1) has as drawback that you can’t run with standard fuse settings anymore: the clock will have to be the not-so-accurate 8 MHz RC clock and the crystal oscillator needs to be set appropriately. It does seem like this would allow short-duration low-power waiting with a granularity of ≈ 30 µs.
Option 2) needs some external components, such as perhaps a low-power 7555 CMOS timer. This would probably still consume about 0.1 mA – pretty low, but not super-low. Or maybe a 74HC4060, for perhaps 0.01 mA (10 µA) power consumption.
Option 3) may sound like a good idea, since Timer 2 can run while the rest of the ATmega’s clock circuits are switch off. But now you have to keep the RFM12B’s 10 MHz crystal running, which draws 0.6 mA … not a great improvement.
Option 4) seems like an option worth trying. The idea is to connect these components to a spare I/O pin:
By pulling the I/O pin low as an output, the capacitor gets discharged. When turning the pin into a high-impedance input, the cap starts charging until it triggers a transition from a “0” to a “1” input, which could be used as pin-change interrupts. The resistor value R needs to be chosen such that the charge time is near to what we’re after for our sleep time, say 1 millisecond. Longer times can then be achieved by repeating the process.
It might seem odd to do all this for what is just one thousandths of a second, but keep in mind that during this time the ATmega can be placed in deep-sleep mode, consuming only a few µA’s. It will wake up quickly, and can then either restart another sleep cycle or resume its work. This is basically the same as a watchdog interrupt.
Let’s first try this using the internal pull-up resistor instead, and find out what sort of time delays we get:
(several typo’s: the “80 µs” comment in the above screen shot should be “15 µs” – see explanation below)
This code continuously discharges the 0.1 µF capacitor connected to DIO1, then waits until it charges up again:
With the internal pull-up we get a 3.4 ms cycle time. By adding an extra external resistor, this could be shortened. The benefit of using only the internal pull-up, is that it also allows us to completely switch off this circuit.
We can see that this ATmega switches to “1” when the voltage rises to 1.66V, and that its internal pull-up resistor turns out to be about 49 kΩ (I determined this the lazy way, by tweaking values on this RC calculator page).
Note that discharge also takes a certain amount of time, i.e. when the output pin is set to “0”, we have to keep it there a bit. Looks like discharge takes about 15 µs, so I adjusted the asm volatile (“nop”) loop to cycle 50 times:
In other words, this sketch is pulling the IO pin low for ≈ 15 µs, then releases it and waits until the internal pull-up resistor charges the 0.1 µF capacitor back to a “1” level. Then this cycle starts all over again. Easy Peasy!
So there you have it: a single 0.1 µF capacitor is all it takes to measure time in 3.4 µs ms increments, roughly. Current consumption should be somewhat under 67 µA, i.e. the current flowing through a 49 kΩ resistor @ 3.3V.
Tomorrow, I’ll rewrite the sketch to use pin-change interrupts and go into low-power mode… stay tuned!
If you’d prefer around 1ms granularity, could you use a 33nF cap to reduce the cycle without increasing charge current, or is there some problem with that?
Absolutely. Note that the timing will be approximate. You could calibrate it against the resonator, though.
The graphics show an interesting effect – contrast that classic exponential discharge curve in the lower ‘scope shot with the charging curve above. The much “flatter” shape (even allowing that it is aiming towards 3.3v) comes from using the internal pull-up “resistor”.
Real resistors take far too much chip space, this is simulated by a CMOS cell. The apparent resistance is voltage dependent – in fact it models closer to a constant current source than a pure resistor. A true constant current into a capacitor gives a linear ramp – so the trace shows a hybrid between exponential and linear.
This makes computing the expected time delay somewhat empirical as JCW shows, but proportionality should hold e.g. doubling the capacitor should ~ double the delay.
So you hold it low for 15us in order to wait for 3.4us?
Seems like you ate up all your power saving in the setup phase.. or did i miss something?
I bet it’s just a typo actually.. 15us then 3.4ms… nevermind :)
@tmk – well spotted! Corrected to 3.4ms.
Could you point to any reference on how to build a 10 uA 74HC4060 based timer (from Option 2)?
Eh, I admit that I don’t have one – I just looked at the datasheet, and thought it might be possible. Sorry – should have researched a bit more…
@zeph, the CMOS versions of the venerable 555 timer can get down to ~60uA. Or better, ~5uA possible with the scaler.
Alternatively, a low power RTC chip can get down below 50uA and you can even set the delay time through I2C !
You forgot option 5 ;-)
Use the wake-up timer integrated in the RF12b chip. This one allows to sleep from 1 to 255ms in 1ms resolution. From 2 to 510ms in 2ms resolution and so on… It’s not that exact (5% variation according to data sheet) but allows for the deepest sleep-mode (power off).
http://forum.jeelabs.net/node/1160 (sorry for posting my own forum entry here…)
Whoops – yes, I forgot to list it (fixed now). I’m sorry – it was in fact at the top of my list, but I wanted to dedicate a separate weblog post to it, in one of the coming days. And then I forgot to even mention it – silly me!
You could even use that technique for variable delay: just keep the low state for less time (so it discharges less the cap)… Not proportional (maybe a series resistor could help), but could be useful.