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!