Stay busy, but also sleep a lot Mar 2017

One of the examples in the multi.fs code contains this little gem:

: sleep ( -- ) [ $BF30 h, ] inline ; \ WFI Opcode, enters sleep mode

task: lowpower-task

: lowpower& ( -- )
  lowpower-task activate
      eint? if \ Only enter sleep mode if interrupts have been enabled
        dint up-alone? if ( ."  Sleep " ) sleep then eint
    again ;

It’s a task which gets called periodically, like every task in the cooperative multi-tasker, and then checks if it’s the only enabled task, i.e. not set to idle. If so, it puts the ARM µC in “sleep” mode, a simple trick which halts the processor until the next interrupt. In other words, when there is nothing to do: pause until the next interrupt instead of frantically twiddling thumbs!

Sleep mode cuts power consumption roughly in half, so it’s not a huge win, but the interesting aspect is that it does not affect normal operation of the code: applications can take advantage of this without change. All they need to do is put tasks into idle mode when… idling!

For ultra low-power nodes, we’ll need to take this a lot further. We need to put the µC into “stop” (or even “standby”) mode, where all the main clocks are stopped. This has far more impact on the application: when clocks are stopped, the application loses all sense of time - not so great when you want to do a few things periodically.

Can we have an architecture whereby the application continues to think in terms of timers, periodic actions, and callbacks, yet also make the µC go into these really low-power modes whenever there is nothing to do?

This is where the timer task presented in the previous article could come into play. What if we were to extend it a bit as follows:

The key benefit of managing all timers in a single task, is that it becomes the sole place in the application which needs to track the passage of time. That means it could figure out exactly when the next callback needs to be triggered.

We’ll probably need to take care of some other details to make this work well:

Note that when the µC is in stop mode, some of its interrupt capabilities are in fact disabled. The UART for example, is likely to be comatose, so interrupts won’t even be generated. We could work around this by setting up a falling edge interrupt on the RX pin, to wake up on incoming data - even if that means losing the first character(s), as the µC springs back to life.

Here is an example, again from the multi.fs package, which illustrates how the command prompt task can easily be put to sleep:

0 variable seconds
task: timetask

: time& ( -- )
  timetask background
      key? if boot-task wake then
      1 seconds +!
      seconds @ . cr
    again ;

time& lowpower& tasks

: tick ( -- ) timetask wake ;

 ' tick irq-systick !
 16000000 $E000E014 ! \ How many ticks between interrupts ?
        7 $E000E010 ! \ Enable the systick interrupt.

stop \ Idle the boot task

It’s a fairly complex bit of code, but here’s the essence of what it does:

Two lines in this code are very special in this context:

  1. key? if boot-task wake then
  2. stop \ Idle the boot task

Looking at that last one: entering stop on the command line puts the command-processor in Mecrisp Forth to sleep. By doing this, we’ve disabled the prompt and we’ve lost control!

However, the other task still running is timetask, and it’s being woken up every second by the SysTick interruot handler. Since timetask checks for new input using key?, it can bring the command prompt back when fresh input needs attention. Now we’re back in business!

This is by no means the only way to deal with the command prompt in low-power scenarios, but it illustrates that there are ways to have our cake and eat it too: an application which can enter low-power modes, yet retain the ability to listen to the serial port and jump back into interactve mode when needed.

So far, these considerations are preliminary and not exhaustively tested. But hey, you gotta start somewhere when trying to come up with a foundation for ultra low-power nodes, eh?

Weblog © Jean-Claude Wippler. Generated by Hugo.