Key Points in Designing Battery-Powered Microcontroller Products

Source | Embedded Miscellaneous

First, let me ask everyone, how many of you have worked on low-power battery-powered products?

In this era of intense competition, not only hardware is being pushed to the limit, but also low power consumption, with standby currents of 1uA and a single battery lasting for 2 years…

Of course, for battery-powered hardware products, low-power design is one of the key factors in enhancing product competitiveness.

The main significance of low-power design:

  • Extending Battery Life: For portable devices (such as smartphones, tablets, wearables, etc.), low-power design can significantly extend battery usage time, reducing the need for frequent recharging and enhancing user experience.

  • Reducing Operating Costs: In long-term operational scenarios, low-power design can significantly lower energy costs.

  • Improving System Reliability: High power consumption can lead to increased device temperatures, and high temperatures are one of the main causes of electronic component aging and failure. By implementing low-power design, the operating temperature of the device can be reduced, minimizing failures due to overheating and improving system stability and reliability.

The specific implementation of low-power design involves multiple aspects: hardware design, software design, thermal design, etc.

This article will focus on some key points of low-power software design.

Task Scheduling and Sleep Management

RTOS typically has efficient task scheduling mechanisms and resource management capabilities, which can reduce CPU idle time and avoid unnecessary energy consumption. Additionally, some RTOS support low-power modes, such as sleep mode or deep sleep mode, allowing the system to automatically enter low-power states when idle, significantly reducing energy consumption.

For example, FreeRTOS provides a low-power mode called Tickless, which reduces power consumption by minimizing unnecessary system clock interrupts.

In Tickless mode, the system tick interrupt (tick timer interrupt) is turned off during idle task execution, and the processor is only awakened when other interrupts occur or tasks need to be processed. This can significantly reduce processor power consumption during idle periods.

Tickless mode can be enabled and configured by setting macros in the FreeRTOSConfig.h file, such as <span>configUSE_TICKLESS_IDLE</span> and <span>configEXPECTED_IDLE_TIME_BEFORE_SLEEP</span>, etc.

Data Processing and Algorithm Optimization

Optimizing algorithms and data processing is also an effective way to reduce power consumption in embedded systems. By selecting efficient algorithms and data structures, the CPU’s computational load and memory access frequency can be reduced, thereby lowering system energy consumption. Additionally, for applications that require frequent data processing, hardware accelerators (such as DSPs, GPUs, etc.) can be considered to offload computational tasks from the CPU, further improving system energy efficiency.

1. Use Fixed Point Instead of Floating Point

In many embedded systems, using fixed-point arithmetic instead of floating-point arithmetic can significantly reduce computational load and power consumption, as fixed-point operations are generally faster and consume less energy than floating-point operations.

Floating Point Calculation Example (Non-Optimized)

#include <stdio.h>  

float multiplyAndAdd(float a, float b, float c)  
{  
    return a * b + c;  
}  

int main(void)  
{  
    float result = multiplyAndAdd(1.5f, 2.3f, 4.2f);  
    printf("Result: %f\n", result);  
    return 0;  
}

Fixed Point Calculation Example (Optimized)

In this example, we use integers to represent fixed-point decimals, assuming we use a fixed scaling factor (e.g., 1000) to represent the decimal part. This means we multiply all floating-point numbers by 1000 and convert them to integers before performing calculations.

#include <stdio.h>  

// Scaling factor  
#define FIXED_POINT_SCALE 1000  

int floatToFixed(float f)  
{  
    return (int)(f * FIXED_POINT_SCALE);  
}  

int fixedMultiply(int a, int b)  
{  
    return (a * b) / FIXED_POINT_SCALE;  
}  

int fixedMultiplyAndAdd(int a, int b, int c)  
{  
    return ((a * b) / FIXED_POINT_SCALE) + c;  
}  

int main(void)  
{  
    // Convert floating-point numbers to fixed-point numbers  
    int a = floatToFixed(1.5f);  
    int b = floatToFixed(2.3f);  
    int c = floatToFixed(4.2f);  
    // Perform fixed-point calculations  
    int result = fixedMultiplyAndAdd(a, b, c);  
    // Convert the result back to floating-point  
    float resultFloat = (float)result / FIXED_POINT_SCALE;  
    printf("Fixed-Point Result: %f\n", resultFloat);  
    return 0;  
}

Note:

  • • In this simplified example, we directly performed integer division and multiplication, but in practical applications, large number multiplication may lead to integer overflow. More complex algorithms are needed to handle large number calculations.

  • • The precision of fixed-point numbers depends on the scaling factor you choose. The larger the scaling factor, the higher the precision, but the required integer size also increases, which may lead to increased memory usage.

  • • The scaling factor or dynamic scaling factors should be adjusted based on specific application scenarios to balance precision and performance.

2. Optimize Loops and Conditional Statements

Reducing the number of loop iterations and avoiding deeply nested conditional statements can lower power consumption.

Non-optimized:

for (int i = 0; i < arraySize; i++)  
{  
    // Assume we always process each element  
    processElement(array[i]);  
}

Optimized:

int findLastValidIndex(int* array, int size)  
{  
    for(int i = size - 1; i >= 0; i--)  
    {  
        if(array[i] != SOME_INVALID_VALUE)  
        {  
            return i;  
        }  
    }  
    return -1;  
}  

int lastValidIndex = findLastValidIndex(array, arraySize);  
for(int i = 0; i <= lastValidIndex; i++)  
{  
    processElement(array[i]);  
}

3. Data Compression

Compressing data before transmission or storage can reduce the power consumption of data transfer and storage. For more information on lz4 compression, refer to the article: Using the lz4 Compression Library.

Interrupts and Event-Driven Programming

Interrupts and event-driven programming are common programming patterns in embedded systems that can reduce CPU polling time, thereby lowering system energy consumption. By properly configuring interrupt sources and priorities, only important events can wake up the CPU, reducing CPU usage.

void GPIO_IRQHandler(void)  
{  
    if(GPIO_PinRead(PIN_BUTTON) == LOW)  
    {  
        // Handle button press event  
        processButtonPress();  
    }  
    // Clear interrupt flags, etc.  
}  

void processButtonPress(void)  
{  
    // Execute actions after button press  
}  

// Configure GPIO interrupt during system initialization  
void GPIO_Init(void)  
{  
    // Configure GPIO pins as input, enable interrupts, etc.  
}

Power Management Strategies

Intelligently controlling the power supply to various peripherals or subsystems, supplying power only when needed. This often involves hardware power management features, but software can also control power enable pins or send power management commands.

void PowerGatePeripheral(uint8_t peripheral_id, bool enable)  
{  
    if(enable)  
    {  
        // Enable peripheral power  
        HAL_PowerEnable(peripheral_id);  
    }  
    else  
    {  
        // Disable peripheral power  
        HAL_PowerDisable(peripheral_id);  
    }  
}

Sensor and Peripheral Management

Dynamically adjust the sampling rate based on demand.

void SensorSamplingTask(void *pvParameters)  
{  
    while(1)  
    {  
        // Check if high sampling rate is needed  
        if(needHighSamplingRate())  
        {  
            sampleSensorAtHighRate();  
        }  
        else  
        {  
            sampleSensorAtLowRate();  
        }  
        vTaskDelay(pdMS_TO_TICKS(samplingInterval));  
    }  
}

Implementation of Low-Power Communication Protocols

Implementing low-power communication protocols, such as BLE (Bluetooth Low Energy) or Zigbee, in embedded systems requires careful management of connections, data transmission, and disconnections to ensure low power consumption during communication.

void BLE_ConnectionHandler(ble_evt_t *p_ble_evt)  
{  
    switch(p_ble_evt->header.evt_id)  
    {  
        case BLE_GAP_EVT_CONNECTED:  
            // Set connection parameters (e.g., connection interval) to optimize power consumption  
            ble_conn_params_init();  
            break;  
        case BLE_GAP_EVT_DISCONNECTED:  
            // Handle disconnection  
            // Can restart advertising or enter deep sleep  
            ble_advertising_start(&adv_params);  
            break;  
        // Other BLE event handling...  
    }  
}

Low-power design in embedded systems is a comprehensive engineering issue that requires us to consider various factors during the software design process.

By reasonably selecting embedded operating systems, optimizing algorithms and data processing, etc., we can effectively reduce the power consumption levels of embedded systems, improving system energy efficiency and endurance.

———— END ————

Key Points in Designing Battery-Powered Microcontroller Products

● Column “Embedded Tools”

● Column “Embedded Development”

● Column “Keil Tutorials”

● Selected Tutorials from the Embedded Column

Follow our public account Reply “Add Group” to join the technical exchange group according to the rules, reply “1024” to see more content.

Click “Read the Original” to see more shares.

Leave a Comment