So far I’ve discussed mostly hardware aspects of low power design. This is the first of several posts on low power firmware design. Some high level concepts will be presented first but they shouldn’t be glossed over, they can make a profound difference on power consumption. As was mentioned in the introductory post, a product’s hardware design will establish the minimum level of power consumption for the product. For many types of products, the firmware will determine the highest level of power consumption. More importantly, the firmware will also determine how much time is spent at the minimum level of power consumption.
Some power saving concepts will be presented that can be applied whether your firmware is in assembly language or C. There are also a number of techniques that can be applied when using C to force more power efficient code than a compiler would normally generate. Some of these techniques will go against the basic principles of structured code. Highly structured code unfortunately is very inefficient power-wise due to all the instructions the micro has to execute because of the code structure that don’t directly contribute to completing the task at hand.
Power saving in firmware is all about (1) eliminating unnecessary clock cycles the micro uses while performing a task and (2) putting the micro in a low-power state as often and for as long as possible for the given application. To a large extent, eliminating unnecessary clock cycles naturally leads to spending more time in a low-power state but the time spent in a low-power state is more heavily influenced by the code structure. Keep in mind that everything your code does that it doesn’t need to do or doesn’t need to do as frequently as it does is just wasting power. This is a good place to remind you about equation for current used during a specific event:
Ievent = (Timeoperating x Ioperating) + (Timesleep x Isleep)
Since the operating current of modern micros can be several orders of magnitude higher than their standby or sleep currents, your goal is to reduce time spent performing the task and maximize the time spent in a low power state.
Choice of programming languages
For the ultimate power savings you must be in control of the instructions your micro executes as much as possible. Carefully written assembly language can provide lower power consumption than the best compiled code but is slower to develop and harder to maintain. If you choose to use a compiled language, C is probably the best choice since you can achieve a decent level of control over the compiled code and there won’t be as much run-time code generated by the compiler that you aren’t aware of as there can be with C++. A considerable amount has been written about the suitability of C++ for embedded programming. Whatever your stance on this, it is hard to debate that well written C code is more efficient than equally well written C++ code.
Main function vs the rest of the time
Very few products actively perform their main function 100% of the time yet that is where many engineers spend their time trying to reduce power consumption. Particularly in embedded applications, most firmware spends most of its time waiting for something to do. This may be waiting for input from a user, waiting for some event or just waiting for time to perform some repetitive task. What the firmware does while inactive often has a bigger impact on power consumption than what it does while active. Maximizing the time the micro spends in the highest power saving mode suitable for the application is key to reducing long term power consumption.
Frequency of events
Make sure your product only performs its main function as often as is really necessary. Except for low level control systems, it is fairly rare that monitoring physical conditions needs to be done multiple times per second or even per minute. For example, if monitoring temperature is a primary activity you may need to do you may be able to get by only checking the temperature once every few minutes. The same can be said for monitoring battery voltage. Even when response time is critical, for monitoring most physical conditions a longer interval can be used when the condition being monitored is well within normal bounds and then the interval reduced as the condition being monitored starts approaching a critical threshold.
Polled vs interrupt driven events
Particularly with slow peripherals such as UARTs, interrupt driven firmware will use considerably less power than pollIng firmware. A micro may execute thousands of instructions during the time it takes for a UART to transmit or receive a byte of data. Even at a relatively fast 115.2K baud, an 8Mhz processor will burn almost 700 clocks in one byte time, drop that to 19.2K baud and it goes up to over 4,100 clocks per byte. On the other hand, there are situations where polling can be more power efficient. For example, with a fast peripheral such as an A/D converter or SPI/I2C controller the interrupt processing overhead for a slow 8-bit micro may use more power than a simple polling loop.
DMA vs firmware loops
If an application requires moving large amounts of data to or from a peripheral (or even small amounts of data with slow peripherals), it is generally more power efficient to move the data with a DMA controller than in a firmware loop. Even if the DMA controller is running at the same clock speed as the micro, the circuitry in the DMA controller is much smaller than the circuitry in the micro core so it will use less power. This is an area where you must do your homework up front to ensure you select a micro that supports DMA operations with the required peripherals and that the DMA controller is operational with the micro in a sleep mode.
If DMA is not an option and your micro has transmit/receive FIFOs in its UARTs, take advantage of these FIFO so that your firmware doesn’t take an interrupt for each byte transferred. For instance, when transmitting data you can fill the FIFO and only take an interrupt when the last byte in the FIFO has started shifting out. Many newer micros with UART FIFOs allow you to set the point where the interrupt is generated so you don’t have to wait for the last byte to get an interrupt if you have over-run/under-run concerns. If you are moving a string of data between a UART and a buffer in memory and only taking an interrupt every 8th byte, not only will you save power incurred in the processing overhead for seven interrupts every eight bytes, a good compiler will use the micro’s registers for the loop control variables, buffer pointers and only load/save those variables once for every eight bytes.
Next week we’ll continue with a few more high level concepts before getting into some of the details of low power firmware design.