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!