Power Management

Comprehensive guide to power management and optimization for Nexus embedded applications.

Overview

Power management is critical for battery-powered embedded systems. This guide covers power optimization strategies, sleep modes, and energy-efficient design.

Power Management Goals:

  • Minimize power consumption

  • Extend battery life

  • Reduce heat generation

  • Meet power budgets

  • Maintain performance requirements

Power Consumption Sources:

  • CPU execution

  • Peripheral operation

  • Memory access

  • Clock generation

  • I/O pin states

  • Leakage current

Power Modes

ARM Cortex-M Power Modes

Available Modes:

Mode

CPU

Peripherals

Wake-up Time

Run

Active

Active

N/A

Sleep

Stopped

Active

Fast (<1µs)

Stop

Stopped

Stopped

Medium (5-50µs)

Standby

Off

Off

Slow (50-500µs)

Power Consumption (Typical STM32F4):

  • Run Mode: 50-100 mA @ 168 MHz

  • Sleep Mode: 30-50 mA

  • Stop Mode: 100-500 µA

  • Standby Mode: 1-10 µA

Sleep Mode

Enter Sleep Mode:

#include "hal/nx_power.h"

void enter_sleep_mode(void)
{
    /* Configure wake-up sources */
    nx_power_config_wakeup(WAKEUP_UART | WAKEUP_GPIO);

    /* Enter sleep mode */
    nx_power_enter_sleep();

    /* CPU stops here until interrupt */

    /* Resume execution after wake-up */
    LOG_INFO("Woke up from sleep");
}

Sleep-on-Exit (Cortex-M):

/* Enable sleep-on-exit for interrupt-driven systems */
void enable_sleep_on_exit(void)
{
    SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;

    /* CPU sleeps after returning from ISR */
    /* Wakes up on next interrupt */
}

FreeRTOS Idle Hook:

void vApplicationIdleHook(void)
{
    /* Enter sleep when idle */
    __WFI();  /* Wait For Interrupt */
}

Stop Mode

Enter Stop Mode:

void enter_stop_mode(void)
{
    /* Save peripheral state */
    save_peripheral_state();

    /* Configure wake-up sources */
    nx_power_config_wakeup(WAKEUP_RTC | WAKEUP_BUTTON);

    /* Enter stop mode */
    nx_power_enter_stop();

    /* CPU and most peripherals stopped */
    /* Wake up on configured interrupt */

    /* Restore peripheral state */
    restore_peripheral_state();

    LOG_INFO("Woke up from stop mode");
}

RTC Wake-up:

void setup_rtc_wakeup(uint32_t seconds)
{
    nx_rtc_t* rtc = nx_factory_rtc(0);

    /* Configure RTC wake-up timer */
    nx_rtc_wakeup_config_t config = {
        .period = seconds,
        .callback = rtc_wakeup_callback,
        .user_data = NULL,
    };

    rtc->configure_wakeup(rtc, &config);
    rtc->enable_wakeup(rtc);

    nx_factory_rtc_release(rtc);
}

void rtc_wakeup_callback(void* user_data)
{
    LOG_INFO("RTC wake-up triggered");
}

Standby Mode

Enter Standby Mode:

void enter_standby_mode(void)
{
    /* Save critical data to backup SRAM or Flash */
    save_critical_data();

    /* Configure wake-up pin */
    nx_power_config_wakeup_pin(WAKEUP_PIN_1, RISING_EDGE);

    /* Enter standby mode */
    nx_power_enter_standby();

    /* System resets on wake-up */
    /* Execution starts from reset vector */
}

Check Wake-up Source:

void check_wakeup_source(void)
{
    if (nx_power_is_wakeup_from_standby()) {
        uint32_t source = nx_power_get_wakeup_source();

        if (source & WAKEUP_PIN_1) {
            LOG_INFO("Woke up from button press");
        } else if (source & WAKEUP_RTC) {
            LOG_INFO("Woke up from RTC alarm");
        }

        /* Restore critical data */
        restore_critical_data();
    }
}

Clock Management

Dynamic Frequency Scaling

Adjust Clock Frequency:

typedef enum {
    CLOCK_SPEED_LOW = 0,      /* 16 MHz */
    CLOCK_SPEED_MEDIUM,       /* 84 MHz */
    CLOCK_SPEED_HIGH,         /* 168 MHz */
} clock_speed_t;

void set_clock_speed(clock_speed_t speed)
{
    switch (speed) {
    case CLOCK_SPEED_LOW:
        /* Configure PLL for 16 MHz */
        nx_clock_set_frequency(16000000);
        LOG_INFO("Clock: 16 MHz (low power)");
        break;

    case CLOCK_SPEED_MEDIUM:
        /* Configure PLL for 84 MHz */
        nx_clock_set_frequency(84000000);
        LOG_INFO("Clock: 84 MHz (balanced)");
        break;

    case CLOCK_SPEED_HIGH:
        /* Configure PLL for 168 MHz */
        nx_clock_set_frequency(168000000);
        LOG_INFO("Clock: 168 MHz (high performance)");
        break;
    }

    /* Update SystemCoreClock variable */
    SystemCoreClockUpdate();

    /* Reconfigure peripherals if needed */
    reconfigure_peripherals();
}

Adaptive Frequency:

void adaptive_frequency_task(void* arg)
{
    while (1) {
        uint32_t cpu_usage = get_cpu_usage_percent();

        if (cpu_usage > 80) {
            /* High load - increase frequency */
            set_clock_speed(CLOCK_SPEED_HIGH);
        } else if (cpu_usage < 20) {
            /* Low load - decrease frequency */
            set_clock_speed(CLOCK_SPEED_LOW);
        } else {
            /* Medium load - balanced frequency */
            set_clock_speed(CLOCK_SPEED_MEDIUM);
        }

        osal_task_delay(1000);
    }
}

Peripheral Clock Gating

Disable Unused Peripherals:

void disable_unused_peripherals(void)
{
    /* Disable unused peripheral clocks */
    nx_clock_disable_peripheral(PERIPHERAL_SPI2);
    nx_clock_disable_peripheral(PERIPHERAL_I2C2);
    nx_clock_disable_peripheral(PERIPHERAL_USART3);
    nx_clock_disable_peripheral(PERIPHERAL_TIM3);
    nx_clock_disable_peripheral(PERIPHERAL_TIM4);

    LOG_INFO("Disabled unused peripheral clocks");
}

Dynamic Clock Control:

void use_spi_with_clock_control(void)
{
    /* Enable SPI clock before use */
    nx_clock_enable_peripheral(PERIPHERAL_SPI1);

    /* Use SPI */
    nx_spi_t* spi = nx_factory_spi(0);
    spi->transfer(spi, tx_data, rx_data, len, 1000);
    nx_factory_spi_release(spi);

    /* Disable SPI clock after use */
    nx_clock_disable_peripheral(PERIPHERAL_SPI1);
}

Peripheral Power Management

GPIO Power Optimization

Configure GPIO for Low Power:

void configure_gpio_low_power(void)
{
    /* Set unused pins to analog input (lowest power) */
    for (uint8_t pin = 0; pin < 16; pin++) {
        if (!is_pin_used('A', pin)) {
            nx_gpio_t* gpio = nx_factory_gpio('A', pin);
            gpio->set_mode(gpio, NX_GPIO_MODE_ANALOG);
            nx_factory_gpio_release(gpio);
        }
    }

    /* Disable pull-ups/pull-downs on unused pins */
    /* Configure output pins to known state */
}

Output Pin States:

void set_output_pins_low_power(void)
{
    /* Set output pins to low to minimize current */
    nx_gpio_write_t* led = nx_factory_gpio_write('D', 12);
    led->write(led, 0);  /* Turn off LED */
    nx_factory_gpio_release((nx_gpio_t*)led);

    /* Or configure as input to save power */
    nx_gpio_t* gpio = nx_factory_gpio('D', 13);
    gpio->set_mode(gpio, NX_GPIO_MODE_INPUT);
    nx_factory_gpio_release(gpio);
}

UART Power Management

UART Sleep Mode:

void uart_power_management(void)
{
    nx_uart_t* uart = nx_factory_uart(0);

    /* Use UART */
    nx_tx_sync_t* tx = uart->get_tx_sync(uart);
    tx->send(tx, data, len, 1000);

    /* Enter low power mode when idle */
    nx_power_t* power = uart->get_power(uart);
    power->set_mode(power, NX_POWER_MODE_SLEEP);

    /* UART wakes up automatically on RX activity */

    nx_factory_uart_release(uart);
}

DMA for Power Savings

Use DMA to Allow CPU Sleep:

void uart_send_with_dma_sleep(nx_uart_t* uart,
                               const uint8_t* data,
                               size_t len)
{
    /* Start DMA transfer */
    nx_tx_dma_t* tx_dma = uart->get_tx_dma(uart);
    tx_dma->send(tx_dma, data, len);

    /* CPU can sleep while DMA transfers data */
    while (!tx_dma->is_complete(tx_dma)) {
        __WFI();  /* Sleep until DMA interrupt */
    }

    LOG_INFO("DMA transfer complete");
}

Battery Monitoring

Battery Voltage Measurement

Measure Battery Voltage:

float measure_battery_voltage(void)
{
    nx_adc_t* adc = nx_factory_adc(0);

    /* Configure ADC for battery measurement */
    nx_adc_config_t config = {
        .resolution = ADC_RESOLUTION_12BIT,
        .sampling_time = ADC_SAMPLING_480_CYCLES,
    };
    adc->configure(adc, &config);

    /* Read battery voltage (with voltage divider) */
    uint16_t raw_value = adc->read(adc, ADC_CHANNEL_VBAT);

    /* Convert to voltage (assuming 12-bit ADC, 3.3V ref) */
    float voltage = (raw_value * 3.3f) / 4096.0f;

    /* Account for voltage divider (if used) */
    voltage *= 2.0f;  /* Example: 1:2 divider */

    nx_factory_adc_release(adc);

    return voltage;
}

Battery Level Estimation:

typedef enum {
    BATTERY_CRITICAL = 0,  /* < 10% */
    BATTERY_LOW,           /* 10-25% */
    BATTERY_MEDIUM,        /* 25-75% */
    BATTERY_HIGH,          /* > 75% */
} battery_level_t;

battery_level_t get_battery_level(void)
{
    float voltage = measure_battery_voltage();

    /* LiPo battery voltage ranges */
    const float V_MIN = 3.0f;   /* Empty */
    const float V_MAX = 4.2f;   /* Full */

    if (voltage < V_MIN) {
        return BATTERY_CRITICAL;
    }

    float percent = ((voltage - V_MIN) / (V_MAX - V_MIN)) * 100.0f;

    if (percent < 10.0f) {
        return BATTERY_CRITICAL;
    } else if (percent < 25.0f) {
        return BATTERY_LOW;
    } else if (percent < 75.0f) {
        return BATTERY_MEDIUM;
    } else {
        return BATTERY_HIGH;
    }
}

Battery Monitoring Task:

void battery_monitor_task(void* arg)
{
    while (1) {
        float voltage = measure_battery_voltage();
        battery_level_t level = get_battery_level();

        LOG_INFO("Battery: %.2fV (%s)",
                 voltage, battery_level_to_string(level));

        if (level == BATTERY_CRITICAL) {
            LOG_WARN("Battery critical - entering low power mode");
            enter_emergency_low_power_mode();
        } else if (level == BATTERY_LOW) {
            LOG_WARN("Battery low - reducing power consumption");
            reduce_power_consumption();
        }

        /* Check every 60 seconds */
        osal_task_delay(60000);
    }
}

Power Optimization Strategies

Interrupt-Driven Design

Avoid Polling:

/* Bad: Polling wastes power */
void check_button_polling(void)
{
    while (1) {
        if (read_button() == PRESSED) {
            handle_button_press();
        }
        osal_task_delay(10);  /* Still wastes power */
    }
}

/* Good: Interrupt-driven */
void button_interrupt_handler(void* ctx)
{
    handle_button_press();
}

void setup_button_interrupt(void)
{
    nx_gpio_t* button = nx_factory_gpio('A', 0);
    button->set_mode(button, NX_GPIO_MODE_INPUT);
    button->set_exti(button, NX_GPIO_EXTI_FALLING,
                     button_interrupt_handler, NULL);
    nx_factory_gpio_release(button);

    /* CPU can sleep until button press */
}

Efficient Task Scheduling

Consolidate Wake-ups:

/* Bad: Multiple tasks with different periods */
void sensor_task_1(void* arg)
{
    while (1) {
        read_sensor_1();
        osal_task_delay(100);  /* Wake every 100ms */
    }
}

void sensor_task_2(void* arg)
{
    while (1) {
        read_sensor_2();
        osal_task_delay(150);  /* Wake every 150ms */
    }
}

/* Good: Single task with synchronized periods */
void sensor_task_combined(void* arg)
{
    uint32_t count = 0;

    while (1) {
        if (count % 100 == 0) {
            read_sensor_1();
        }
        if (count % 150 == 0) {
            read_sensor_2();
        }

        count += 50;
        osal_task_delay(50);  /* Single wake-up period */
    }
}

Batch Processing

Batch Operations:

/* Bad: Process data immediately */
void data_callback(uint8_t byte)
{
    process_single_byte(byte);  /* Frequent wake-ups */
}

/* Good: Batch processing */
#define BATCH_SIZE 64
static uint8_t batch_buffer[BATCH_SIZE];
static size_t batch_count = 0;

void data_callback_batched(uint8_t byte)
{
    batch_buffer[batch_count++] = byte;

    if (batch_count >= BATCH_SIZE) {
        /* Process entire batch at once */
        process_batch(batch_buffer, batch_count);
        batch_count = 0;
    }
}

Tickless Idle

FreeRTOS Tickless Idle:

/* Enable tickless idle in FreeRTOSConfig.h */
#define configUSE_TICKLESS_IDLE 1

/* Implement tickless idle hook */
void vApplicationSleep(TickType_t xExpectedIdleTime)
{
    /* Calculate sleep duration */
    uint32_t sleep_ms = xExpectedIdleTime * portTICK_PERIOD_MS;

    /* Configure wake-up timer */
    setup_wakeup_timer(sleep_ms);

    /* Enter low power mode */
    enter_stop_mode();

    /* Adjust tick count after wake-up */
    vTaskStepTick(xExpectedIdleTime);
}

Power Profiling

Measuring Power Consumption

Hardware Setup:

  • Power supply with current measurement

  • Multimeter or oscilloscope

  • Current sense resistor

  • Data logging

Software Profiling:

void profile_power_modes(void)
{
    LOG_INFO("Starting power profiling...");

    /* Measure run mode */
    LOG_INFO("Run mode - measure current now");
    osal_task_delay(5000);

    /* Measure sleep mode */
    LOG_INFO("Sleep mode - measure current now");
    for (int i = 0; i < 50; i++) {
        __WFI();
        osal_task_delay(100);
    }

    /* Measure stop mode */
    LOG_INFO("Stop mode - measure current now");
    enter_stop_mode();
    osal_task_delay(5000);

    LOG_INFO("Power profiling complete");
}

Energy Calculation

Calculate Energy Consumption:

typedef struct {
    const char* mode;
    float current_ma;
    uint32_t duration_ms;
} power_profile_t;

float calculate_energy(power_profile_t* profile, size_t count)
{
    float total_energy_mah = 0.0f;

    for (size_t i = 0; i < count; i++) {
        /* Energy (mAh) = Current (mA) × Time (h) */
        float time_hours = profile[i].duration_ms / 3600000.0f;
        float energy = profile[i].current_ma * time_hours;
        total_energy_mah += energy;

        LOG_INFO("%s: %.2f mA × %.2f h = %.4f mAh",
                 profile[i].mode,
                 profile[i].current_ma,
                 time_hours,
                 energy);
    }

    return total_energy_mah;
}

void estimate_battery_life(void)
{
    power_profile_t profile[] = {
        {"Active",  50.0f, 1000},    /* 1s active */
        {"Sleep",   5.0f,  9000},    /* 9s sleep */
    };

    /* Calculate energy per cycle (10 seconds) */
    float energy_per_cycle = calculate_energy(profile, 2);

    /* Calculate daily energy */
    float cycles_per_day = (24.0f * 3600.0f) / 10.0f;
    float energy_per_day = energy_per_cycle * cycles_per_day;

    /* Estimate battery life */
    float battery_capacity = 2000.0f;  /* 2000 mAh */
    float battery_life_days = battery_capacity / energy_per_day;

    LOG_INFO("Estimated battery life: %.1f days", battery_life_days);
}

Best Practices

  1. Design for Low Power from Start * Choose low-power MCU * Plan power modes * Design for sleep * Consider battery capacity

  2. Use Appropriate Power Modes * Sleep for short idle periods * Stop for medium idle periods * Standby for long idle periods * Match mode to wake-up latency requirements

  3. Optimize Clock Usage * Use lowest frequency that meets requirements * Disable unused peripheral clocks * Use dynamic frequency scaling * Consider external oscillator power

  4. Minimize Active Time * Process data efficiently * Use DMA for transfers * Batch operations * Use hardware acceleration

  5. Configure Peripherals Properly * Disable unused peripherals * Use low-power peripheral modes * Configure GPIO for low power * Use pull-ups/pull-downs appropriately

  6. Monitor and Measure * Measure actual power consumption * Profile different modes * Calculate battery life * Optimize based on measurements

  7. Test in Real Conditions * Test with actual battery * Test temperature effects * Test aging effects * Verify wake-up reliability

Power Optimization Checklist

Hardware:

  • [ ] Low-power MCU selected

  • [ ] Appropriate voltage regulator

  • [ ] Battery capacity adequate

  • [ ] Power measurement capability

Software:

  • [ ] Sleep modes implemented

  • [ ] Unused peripherals disabled

  • [ ] GPIO configured for low power

  • [ ] Interrupt-driven design

  • [ ] DMA used where appropriate

Testing:

  • [ ] Power consumption measured

  • [ ] Battery life calculated

  • [ ] Wake-up latency verified

  • [ ] Temperature effects tested

Optimization:

  • [ ] Clock frequency optimized

  • [ ] Active time minimized

  • [ ] Batch processing implemented

  • [ ] Tickless idle enabled

See Also