With a real-time operating system, going into low-power mode is easy. Continuing the recent ChibiOS example, here is a powerUse.ino sketch which illustrates the mechanism:
#include <ChibiOS_AVR.h>
#include <JeeLib.h>
const bool LOWPOWER = true; // set to true to enable low-power sleeping
// must be defined in case we're using the watchdog for low-power waiting
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
static WORKING_AREA(waThread1, 50);
void Thread1 () {
while (true)
chThdSleepMilliseconds(1000);
}
void setup () {
rf12_initialize(1, RF12_868MHZ);
rf12_sleep(RF12_SLEEP);
chBegin(mainThread);
}
void mainThread () {
chThdCreateStatic(waThread1, sizeof (waThread1),
NORMALPRIO + 2, (tfunc_t) Thread1, 0);
while (true)
loop();
}
void loop () {
if (LOWPOWER)
Sleepy::loseSomeTime(16); // minimum watchdog granularity is 16 ms
else
delay(16);
}
There’s a separate thread which runs at slightly higher priority than the main thread (NORMALPRIO + 2), but is idle most of the time, and there’s the main thread, which in this case takes the role of the idling thread.
When LOWPOWER is set to false, this sketch runs at full power all the time, drawing about 9 mA. With LOWPOWER set to true, the power consumption drops dramatically, with just an occasional short blip – as seen in this current-consumption scope capture:
Once every 16..17 ms, the watchdog wakes the ATmega out of its power-down mode, and a brief amount of activity takes place. As you can see, most of these “blips” take just 18 µs, with a few excursions to 24 and 30 µs. I’ve left the setup running for over 15 minutes with the scope background persistence turned on, and there are no other glitches – ever. Those 6 µs extensions are probably the milliseconds clock timer.
For real-world uses, the idea is that you put all your own code in threads, such as Thread1() above, and call chThdSleepMilliseconds() to wait and re-schedule as needed. There can be a number of these threads, each with their own timing. The lowest-priority thread (the main thread in the example above) then goes into a low-power sleep mode – briefly and repeatedly, thus “soaking” up all unused µC processor cycles in the most energy-efficient manner, yet able to re-activate pending threads quickly.
What I don’t quite understand yet in the above scope capture is the repetition frequency of these pulses. Many pulses are 17 µs apart, i.e. the time Sleepy::loseSomeTime() goes to sleep, but there are also more frequent pulses, spread only 4..9 ms apart at times. I can only guess that this has something to do with the ChibiOS scheduler. That’s the thing with an RTOS: reasoning about the repetitive behavior of such code becomes a lot trickier.
Still… not bad: just a little code on idle and we get low-power behaviour almost for free!
