MLRelated.com
Blogs

7 Essential Steps for Reducing Power Consumption in Embedded Devices

Jacob BeningoJune 26, 20241 comment

Reducing the amount of power your embedded device is consuming is not trivial. With so many devices moving to battery operations today, maximizing battery life can be the difference between a happy, raving customer and an unhappy one that ruins your company's reputation. 

Reducing the power consumption of your embedded device early in the development cycle is a strategic move and a time-saving one. In my experience with low-power devices, I've always started optimizing power consumption from the very beginning. The outcome? A device that maximizes battery life and avoids the stress of last-minute optimization attempts. 

The process I use is relatively simple and something you can easily duplicate. Here are the steps:
  1. Define Your Power Requirements Early
  2. Profile Your Devices Power Consumption Regularly
  3. Choose Efficient Power Supplies and Hardware
  4. Leverage an Event-Driven Architecture
  5. Enable Compiler Optimizations
  6. Utilize Hardware Accelerators
  7. Apply Software Optimization Techniques as Needed

The above process seems obvious, yet I don't see many teams follow it! Let's look at some of the nuances and details to help you implement it successfully. After all, the optimization is really in the details.

Resources you might find useful from this post:

Step #1 - Define Your Power Requirements Early

Defining your power requirements early in the design phase is crucial to ensure that you don't over-optimize your device. It's not uncommon to find engineers playing the game of "How low can you go." While squeezing every joule out of a battery can be fun, the time commitment and monetary investment might not be worth it!

You need to start by carefully reviewing your use cases. You need to ask yourself and your teammates some simple questions:

  • How will your customers use their devices? 
  • How long will they need to use the device before changing or recharging the batteries? 
  • Will different use cases require different uses of power?
  • How do the different uses affect power use?

These questions will help you quantify your device's operational power usage, which you can then use to develop a power budget. Without a power budget, you have a very small chance of meeting your customers' battery life needs.  

Understanding the device's power budget as a whole won't fully help you develop all your power requirements. You'll need to consider all aspects of your system, including the microcontroller, sensors, communication modules, and any other peripherals. Establish power consumption limits for each component, ensuring that the total does not exceed your budget.

I often document these limits and my assumptions in a battery budget worksheet. The worksheet allows you to adjust assumptions as your design evolves. You can also experiment with different scenarios and battery sizes to estimate how long your battery will last. A good worksheet will help you understand your power requirements and ensure your design stays on track. 

Warning: Remember that you'll likely have to derate your battery to account for changes in internal resistance as it is used. 

Step #2 - Profile Your Devices Power Consumption Regularly

A battery budget worksheet is a great way to gauge your theoretical power consumption. Unfortunately, it doesn't mean it's reality. To track how much power you're using, you should take power profiles of your embedded device throughout the development cycle to understand exactly how much power they are consuming. 

A profile will help you understand your average power consumption and can be associated with specific use cases and power profiles. These measurements can be used to test optimizations, verify assumptions, and even be fed back into your battery budget for more accurate results. 

Typically, we can't directly measure the power usage of our embedded devices. However, there are methods that we can use to calculate the power if desired. A better method is to measure supply current. Batteries are often rated in milliamp hours (mAh). If you want to know how long your battery will last and you know the average current per hour, we can calculate how long it will take for the battery to die. 

The most common way to measure small currents is with a shunt resistor.  A shunt resistor is simply a resistor with a known value that we insert into the circuit.  Using Ohm’s law, we can measure the voltage over the resistor and compute the current as the measured voltage divided by the known resistance.  

You'll likely need to profile the current draw over various current ranges. Sleep currents today are typically in the microamp range, while their operational modes are in the tens or hundreds of milliamps. This large dynamic current range makes using a single known resistor impractical, as can switching times between shunt resistors. Oscilloscopes struggle to measure the voltage across a shunt resistor because they typically have a 12-bit or less dynamic range, which limits them to measuring only the active current.  

I solve these current measuring issues using the Joulescope JS220, a test instrument specifically designed to accurately measure from nanoamps to 10 amps with seamless auto-ranging and a 300 kHz bandwidth. It also measures voltage to compute power, energy, and charge. I typically use a setup similar to that shown below in Figure 1.

image.png

Figure 1 - A typical setup to perform power profiling measurements of an embedded device. 

It's important to perform these types of measurements regularly. They can tell you where your power consumption is currently at. It's not uncommon for new features to dramatically increase current consumption, so you must regularly ensure that your system isn't losing power performance. 

One way to do this is to add power performance measurements to your CI/CD pipeline. You can perform hardware-in-the-loop power tests with the JS220 to automate energy performance as another critical metric. That way, whenever you add a feature, you can automatically take a power profile and ensure that your battery lifetime hasn't been significantly affected. 

Note: If you aren't experienced in Embedded DevOps and CI/CD Pipelines, you might find Embedded DevOps: Intro to Build Systems and CI/CI Pipelines helpful:

Step #3 - Choose Efficient Power Supplies and Hardware

If you want to optimize your power usage, you need hardware designed into your system to minimize power consumption. The obvious focus is on carefully choosing your microcontroller, sensors, and communication modules. You should consider these components' power consumption specifications in both active and idle states. Focusing specifically on these hardware components is not the only place you should put your attention.   

A common solution to manage various components' power usage is to design your hardware so that your system has power domains. A power domain is a distinct section of an electronic circuit or system where all the components share a common power supply voltage. The primary purpose of defining power domains is to allow different sections of a system to be powered on or off independently of each other. This separation enables fine-grained control over power consumption, as specific circuit parts can be turned off when not in use, thus conserving energy and extending battery life in power-sensitive applications.

By organizing a system into multiple power domains, you can optimize power usage by dynamically adjusting the power state of each domain based on operational requirements. For example, an embedded device might have separate power domains for its microcontroller, communication modules, and sensors, allowing each section to enter a low-power state or be completely powered down when not needed. This approach enhances the overall energy efficiency of the device, enabling longer operation on battery power and reducing heat dissipation. Remember that power domains are only necessary if the component's quiescent current exceeds the available budget. You must also be careful not to backpower any components, which can waste precious battery life. 

Another area that is often overlooked is the power supply design. When selecting a power supply, opt for one with high efficiency and low quiescent current. Efficient power supplies minimize energy loss and ensure more power is available for your device's operation. Additionally, consider integrating energy harvesting methods, such as solar or kinetic energy sources, to supplement your device's battery power. You'd be surprised how much extra energy you can save on some devices using these types of methods. 

I don't have any specific recommendations for components because the details will depend on your system requirements. However, when you select parts, ensure they include an enable line so you can use your microcontroller to enable and disable them. Also, don't overlook the start-up times of active components in your system. In some cases, putting an active device to sleep can be more energy efficient than just turning it off. 

Step #4 -Leverage an Event-Driven Architecture

Developers can use various software architectures to solve common problems in embedded system development. An event-driven architecture can help reduce power consumption by minimizing the time your device spends in active mode. Instead of continuously polling for events or data or processing background tasks, use interrupts to wake the system only when necessary. This approach allows your device to remain in low-power sleep modes for longer periods, conserving energy.

In an event-driven architecture, the system reacts to events or changes in state rather than actively checking for changes through polling. Events can be generated by hardware peripherals, sensors, or even software conditions. When an event occurs, it triggers an interrupt, temporarily waking the system to handle it and then returning it to a low-power state.

When you think about an event-driven architecture, there are several benefits that you receive:

  • Reduced Active Time: The system remains in a low-power sleep mode until an event occurs, significantly reducing the time it spends in active mode.
  • Energy Efficiency: By minimizing active time, power consumption is reduced, extending battery life.
  • System Responsiveness: Interrupts can be processed immediately, making the system more responsive to external events compared to a polling-based approach.

If your application uses an RTOS, it's also a good idea to enable tickless mode. Tickless mode allows you to elongate the RTOSes tick so that your system can stay in a deeper sleep longer. If you don't enable this mode, the RTOSes periodic tick will wake the system up, only to discover there is nothing to do. Tickless mode can allow you to put the system to sleep, sometimes for as long as days, without waking the system. 

Using an event-driven architecture is the de facto standard for designing low-power embedded systems. This architecture not only conserves energy but also enhances system performance and responsiveness. For instance, in battery-operated devices like wearables, smart home gadgets, and IoT sensors, an event-driven design ensures that the device only wakes up to perform necessary tasks, thereby prolonging battery life and ensuring reliable operation over extended periods.

Step #5 - Enable Compiler Optimizations

One mechanism that I often see development teams overlook is the importance of using compiler optimizations in power-sensitive devices. If you are using a compiler like GCC, you'll find that there are several optimization levels:

  • -O0: This level performs no optimization, which is useful for debugging but results in the least efficient code in terms of speed and power consumption.
  • -O1: This level performs minimal optimizations, balancing compile-time and run-time performance with moderate power savings.
  • -O2: This level enables a variety of optimizations that do not involve space-speed trade-offs, offering better performance and power efficiency.
  • -O3: This level performs aggressive optimizations, including those that may increase code size, providing the highest performance and power efficiency but potentially at the cost of increased binary size.
  • -Os: This level optimizes for size, reducing code footprint, which can also positively impact power consumption by reducing the memory access overhead.
  • -Ofast: This level enables all optimizations from -O3 and non-standard compliant optimizations that may result in faster and more power-efficient code but could lead to non-portable code.

Choosing the appropriate optimization level depends on your specific application requirements. For power-sensitive embedded systems, -O2 and -Os are often good starting points, as they strike a balance between performance, power efficiency, and code size. Below is an example of how you can apply these optimizations in your makefile project:

CFLAGS = -O2 -mcpu=cortex-m4 -mthumb
LDFLAGS = -O2

Beyond setting a global optimization level, consider the following strategies to fine-tune your compiler optimizations:

1. Profile-Guided Optimization (PGO): Collect runtime profile data to guide optimizations that improve performance and power efficiency. PGO can help the compiler make more informed decisions about which parts of the code to optimize.

2. Function-Level Optimization: Apply different optimization levels to different parts of the code. Critical sections of your code that run frequently can be compiled with higher optimization levels, while less critical sections use lower levels to save on code size. You can do this using compiler intrinsics as shown below:

__attribute__((optimize("O3")))
void critical_function() {
    // Code that benefits from aggressive optimization
}

__attribute__((optimize("O1")))
void less_critical_function() {
    // Code that doesn't need aggressive optimization
}

3. Link-Time Optimization (LTO): Enable LTO to optimize the entire program, including inlining and removing unused code, improving performance, and reducing power consumption.

CFLAGS += -flto
LDFLAGS += -flto

Compiler optimizations can lead to significant power savings, especially in power-sensitive applications. For instance, in a wearable device, enabling optimizations can reduce the CPU's active time, resulting in lower overall power consumption and extended battery life. Similarly, in IoT sensors, optimized code can ensure that the device processes data quickly and returns to sleep mode faster, conserving energy. 

Unfortunately, I see many teams overlook such simple, low-hanging fruit! (And I didn't even mention that commercial compilers can also improve your code speed by 10 - 40%, too!). 

Step #6 - Utilize Hardware Accelerators

Utilizing hardware accelerators can offload intensive tasks from the CPU, reducing its workload and power consumption. Many modern microcontrollers include specialized hardware for tasks such as cryptography, digital signal processing, data compression, and other computationally intensive operations. By leveraging these accelerators, you can perform complex operations more efficiently and with less power.

The accelerators included in hardware today are constantly changing and improving, but there are several different types that you'll want to be familiar with and use. 

The first type of hardware accelerator is Direct Memory Access (DMA). DMA controllers can transfer data between peripherals and memory without involving the CPU, significantly reducing CPU load and power consumption. For instance, DMA can handle data transfers from sensors to memory or from memory to a display, allowing the CPU to stay in a low-power mode during these operations.

The next type of hardware accelerator is a Cryptographic Accelerator. Many microcontrollers come with built-in cryptographic modules that can perform encryption and decryption tasks much faster and with less power than the CPU. Utilizing these hardware modules for secure communication, data storage, and authentication processes can greatly enhance power efficiency.

Cyclic Redundancy Check (CRC) Units are another hardware accelerator that you will find in my modern microcontroller. CRC units can calculate checksums for data integrity verification faster than software implementations. Offloading CRC calculations to dedicated hardware can save CPU cycles and reduce power consumption, especially in communication protocols and data storage applications.

One last hardware tool that I want to mention that really isn't an accelerator but can make a huge difference is the Internal Cache. Leveraging the internal cache effectively can reduce the number of memory accesses, saving power. By optimizing cache usage, you can ensure that frequently accessed data and instructions are stored in the cache, minimizing power-hungry accesses to slower, off-chip memory.

If you are going to minimize your battery consumption, then you absolutely need to make use of these accelerators. 

Step #7 - Apply Software Optimization Techniques as Needed

Applying software optimization techniques is crucial for reducing power consumption in embedded devices. These techniques involve refining the code to run more efficiently, lowering overall power usage. In fact, you'll find that when you are trying to reduce power consumption, you'll spend more time on this step than on any other! 

The techniques that are available to you can vary based on the architecture and specific microcontroller you've selected. However, there are some general strategies and techniques that you can employ. Here are some effective software optimization strategies:

1. Code Profiling and Analysis. Regularly profile your code to identify performance bottlenecks and high-power-consuming sections. Use profiling tools to gain insights into which parts of your code are consuming the most resources and optimize those sections for better efficiency.

2. Algorithm Optimization. Choose algorithms that are not only computationally efficient but also power-efficient. For example, replacing recursive algorithms with iterative ones can reduce stack usage and power consumption. Similarly, using more efficient sorting or searching algorithms can save power.

3. Optimize Memory Usage. Efficient memory management can significantly reduce power consumption. Avoid memory leaks and excessive dynamic memory allocation, leading to higher power usage. Utilize stack memory instead of heap memory when possible, as stack allocations are generally faster and more power-efficient.

4. Minimize Peripheral Usage. Turn off or put peripherals in low-power mode when they are not in use. Ensure that peripherals such as ADCs, DACs, and communication interfaces are only active when needed.

5. Reduce Clock Speeds. Lowering the clock frequency of the microcontroller can reduce power consumption. Many microcontrollers support dynamic clock scaling, which allows you to adjust the clock speed based on the current processing requirements.

6. Sleep Modes and Power States. Take full advantage of the microcontroller’s sleep modes and low-power states. Implement power management strategies that put the CPU and peripherals into sleep modes during periods of inactivity.

7. Code Efficiency. Write efficient code by avoiding unnecessary loops, conditional checks, and function calls. Inline functions where appropriate, and avoid using floating-point arithmetic if fixed-point arithmetic can be used instead.

Next Steps to Reduce Power Consumption

You're now familiar with the seven steps for optimizing an embedded device for low power. Some of these steps are activities that you'll only have to do once, while others you'll have to do periodically to ensure that your device is still operating at the lowest possible power levels. 

If you are looking for additional ideas on how to lower your power consumption, you can download my Low Power Design Checklist. The checklist contains another 50 ideas and techniques for optimizing the power usage of embedded devices. You can also download a basic battery budget template that I use to estimate battery life. 

With these tools and ideas from this post, you should be able to design an embedded device that minimizes its power consumption and maximizes your battery life!



[ - ]
Comment by StrooomJuly 12, 2024

Hi Jacob, this is really a useful blogpost.

As for #6 : I recently benchmarked AES encryption for LoRaWAN done in SW versus using the HW-accelerator. It's about 30..40 times faster in HW, it has a smaller ROM/RAM footprint, and the implementation is guaranteed to be standards compliant.

You can read my results here : https://www.linkedin.com/pulse/lorawan-encryption-...

For measuring the current consumption, I use the Nordic PPK2, which is very affordable and does the job perfectly : Power Profiler Kit II - nordicsemi.com

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.

Please login (on the right) if you already have an account on this platform.

Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: