Using the new mbed power management API


Note

This is content from 2015. For the latest information on power management in Mbed OS 5, see power management in the documentation.

Today, ARM and Silicon Labs are releasing an extension to the ARM mbed SDK, enabling mbed users to more efficiently use system resources. We demo-ed the result of using these new low-power APIs before, and will provide guidance on how you can take advantage of them in this post.

What we are releasing today is a three-pronged approach to reducing power consumption in everyday mbed applications. Firstly, you can now take advantage of asynchronous I/O transactions on SPI, I2C and Serial peripherals. That will allow you to intelligently sleep, or do other calculations, while your potentially slow or long-running transaction is executing.

Secondly, the behavior of sleep() has been altered to support all sleep modes. On supporting platforms calling sleep() will automatically put your chip in the lowest power mode possible, depending on which transactions, timers or other operations are currently running. As of today, that functionality is present on Silicon Labs’ platforms, with other platforms hopefully following soon.

And lastly, the addition of low-power Ticker, Timer and Timeout APIs. The implementations for this will be dependent on which platform they’re running on, but the base idea is that you are sacrificing timing resolution for (much) lower power consumption. On Silicon Labs’ platforms, even though you’re still setting the timing value in microseconds, the resolution will be along the quarter millisecond line.

It is worth mentioning that no pre-existing APIs have been deprecated, so all code that uses the blocking APIs will still work, even on low-power enabled platforms. So now, let’s dive into the details! Asynchronous transaction calls

Asynchronous calls allow you to submit an I/O operation to the system for execution, and let the system handle the transaction in the background. When complete (or if it errors out), you get a callback to your own specified function. Depending on the implementation, this uses either an interrupt-driven or DMA-driven execution style under the hood, or a combination of both.

event_callback_t

This is the new type definition for callbacks generated by asynchronous I/O operations. It can point to a void (int) function, and needs to be initialized by the calling code. For example, when you want to define a callback to a function called handler, you would do something like this:

event_callback_t functionpointer;

void handler(int events) {
	// Handle events here
}

void init_fp() {
	functionpointer.attach(handler);
}

I2C

For I2C, the traditional start, stop, read and write functions have been superseded by a single ‘transfer’ function. It takes both a write (tx) and read (rx) length in bytes, and char pointers to the respective write and read buffers. It will automatically add start and stop conditions before and after the transaction, and if both write and read lengths are greater then zero, a restart condition will be generated between the write and the read. Additionally, it takes an event argument, which is the logical OR of all events you want to get a callback for (defined in i2c_api.h), and a pointer to an event_callback_t for your callback handler.

When the transfer has started, the function will return 0. If not, there is an error or there is another I2C transfer already in progress. The abort_transfer function will abort the current transfer.

You can see the new APIs being used in a real-world scenario in Silicon Labs’ mbed library for the si70xx series of relative temperature and humidity sensors.

SPI

Until now, SPI really only consisted of one function, write, which would write the configured amount of bits to the bus, and return the response. With the asynchronous API, we are introducing the ‘transfer’ function, much like the I2C equivalent. The difference here is that if the amount of bytes to receive is greater then the amount of writes, the transaction will automatically write fill words to receive the given number of bytes. The other way around also works, the transaction will only fill the receive buffer with the specified amount of data, even if the write buffer is larger.

The transfer function also takes an event argument, which is the logical OR of all events you want to get a callback for (defined in spi_api.h), and a pointer to an event_callback_t for your callback handler.

You can see the new APIs being used in Silicon Labs’ mbed library for the Sharp LS013B7DH03 memory LCD.

Serial

The serial bus is the odd one out of the three. Because it is asynchronous by nature, the functions had to be split up in two: one for sending, and one for receiving data.

The transmit function is called ‘write’, and takes a buffer, its length, a logical OR of events to listen for, and a pointer to the relevant event_callback_t object. The event flags are defined in serial_api.h. The transmit function will return immediately, and the callback will get called on completion of the transmission, or on an error condition. When there is already a transmission in progress, the transmit function will return an error code, and the callback will not get called.

Likewise, the receive function, called ‘read’, takes a buffer, its length, a collection of event flags, and a pointer to the relevant event_callback_t object. However, it can also take a fifth argument, which is a character to listen for. If the SERIAL_EVENT_RX_CHARACTER_MATCH flag is enabled, reception will cease upon seeing this character, and the callback will get called with this flag.

You can see the new API in action in Silicon Labs’ low power serial demo program.

Harmonized sleep calls

Historically, mbed has had sleep() and deepsleep() calls, that would put the system in the specified state. Silicon Labs’, however, is making it easier for you to write low power optimized applications! Now, when you call sleep(), the system will decide which sleep mode to go to, depending on the callbacks that have been registered.

Beware that sleep() currently returns after every interrupt, and that interrupt may or may not be related to any callbacks you have registered. Therefore, if you want to sleep until a certain callback has fired, you need to have a variable changed by that callback, and keep calling sleep until the change has happened.

	while(condition == true) sleep();


For example, when you have a serial transaction going on while you are actually waiting on a low powered timeout that is set to happen after the serial transaction completes, this implementation allows the system to go to a lower power mode after completion of the serial transaction.

Low-power Ticker/Timer/Timeout

The Ticker, Timer and Timeout classes now have a low-power equivalent with the exact same API, but a different underlying implementation. On Silicon Labs platforms, the non low-power classes rely on a high-frequency timer for timekeeping, preventing the system from going into a significant sleeping mode. The low-power classes, on the other hand, are implemented using the RTC, which means they have a lower resolution, but allow for higher energy savings.


Guest Blogger:

Steven Cooreman, Software Engineer, IoT MCU & Wireless, Silicon Labs. Originally from Belgium, Steven has always been fascinated by the wondrous world of electronics. Also bitten by the travel bug, he pursued multiple exchange semesters during his EE education (in Finland and the USA) before joining Silicon Labs in Oslo, Norway. Having started his career in the summer of 2014, Steven now works as the main mbed developer within Silicon Labs.

You need to log in to post a discussion