Power Manager Usage Scenarios#
Scenario 1: Asynchronous Sensor Reading with Power Management#
This scenario demonstrates fundamental Power Manager usage in a bare-metal application that implements asynchronous sensor reading to optimize power consumption. The key principle is that EM1 requirements are only active while the sensor hardware is actually busy.
Application Requirements:#
Periodic sensor readings every 5 seconds triggered by timer
Asynchronous sensor operation with non-blocking data acquisition
EM1 requirement only during actual sensor hardware operation (~10ms)
Optimal power management allowing deep sleep when sensor is idle
State-driven power requirements based on sensor activity
Implementation Strategy:#
Initialization: Power Manager automatically initialized via sl_main framework
Timer-Based Operations: Use sleep timer for periodic wake-ups
Requirement Management: Add EM1 requirement during sensor operations
Optimal Sleep: Allow deep sleep (EM2/EM3) between operations
Non-Blocking Design: Main loop remains responsive for other system tasks during sensor operation
Expected Power Behavior:#
Deep Sleep (EM2/EM3): 99% of the time between sensor readings
Sleep (EM1): Only during the ~10ms sensor reading operation
Wake-up Events: Sleep timer every 5 seconds
Power Optimization: Automatic selection of lowest possible energy mode
Complete Implementation:#
#include "sl_main_init.h"
#include "sl_main_process_action.h"
#include "sl_power_manager.h"
#include "sl_sleeptimer.h"
#include <stdio.h>
sl_sleeptimer_timer_handle_t periodic_timer;
sl_sleeptimer_timer_handle_t sensor_timer;
bool sensor_reading_pending;
uint32_t reading_count;
uint32_t reading;
// Simulated sensor reading function represents actual hardware sensor access
uint32_t read_temperature_sensor(void)
{
return 2350 + (reading_count % 100); // 23.50°C + variation
}
// Sensor completion callback runs in ISR context
void sensor_callback(sl_sleeptimer_timer_handle_t *timer, void *data)
{
(void)timer;
(void)data;
// Sensor hardware has completed acquisition. Read the result
reading = read_temperature_sensor();
reading_count++;
sensor_reading_pending = true;
// Remove EM1 requirement as soon as sensor hardware is idle
sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1);
}
// Simulated sensor start function
void start_temperature_sensor(void)
{
// Add EM1 requirement to maintain system responsiveness during sensor acquisition
// This ensures stable clocks and fast interrupt response while sensor is active
sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1);
// Send command to start sensor (hardware-specific implementation would start here)
// In real implementation, this would be replaced by hardware interrupt when conversion completes
// NOTE: The callback sensor_callback is called after 10 ms to mimic the acquisition time
sl_sleeptimer_start_timer_ms(&sensor_timer, 10, sensor_callback, NULL, 0, 0);
}
void app_init(void)
{
// Configure periodic timer to trigger sensor readings every 5 seconds
// Note: This only triggers the START of sensor reading, not the completion
// The sensor reading itself is asynchronous and managed separately
sl_sleeptimer_start_periodic_timer_ms(&periodic_timer, 5000, NULL, NULL, 0, 0);
}
void app_process_action(void)
{
// Check if sensor data is ready for processing
if (sensor_reading_pending) {
sensor_reading_pending = false;
// Process the completed sensor reading
printf("Temperature: %lu.%02lu°C (reading #%lu)\n", reading / 100, reading % 100, reading_count);
printf("Sensor reading complete, going to deep sleep (EM2)\n");
} else {
// No data pending - it's time to start a new sensor reading
start_temperature_sensor();
printf("Sensor reading in progress, going to sleep (EM1)\n");
}
}
int main(void)
{
sl_main_init(); // Power Manager automatically initialized here
app_init();
while (1) {
sl_main_process_action();
app_process_action();
// Sleep at lowest allowed energy mode (EM2/EM3 when there are no requirements)
sl_power_manager_sleep();
}
}Scenario 2: Ultra-Low Power EM4 Sleep with Data Retention#
This scenario demonstrates EM4 deep sleep operation with BURAM data persistence and with EM4 wakeup from BURTC and GPIO.
Application Requirements:#
Enter EM4 for maximum power savings
Periodic wake-up every 5 minutes via BURTC timer
Preserve critical data across EM4 resets using BURAM
Simple battery monitoring with wake-up counting
Implementation Strategy:#
Initialization: Check if this is an EM4 wake-up or normal boot
Data Management: Use BURAM registers to persist data across EM4 resets
Timer Setup: Configure BURTC for periodic EM4 wake-ups
EM4 Entry: Enter deepest sleep mode (system resets on wake-up)
Expected Power Behavior:#
EM4 Deep Sleep: Ultra-low consumption during 5-minute sleep periods
Simple State Management: Only two states: awake (brief) and EM4 sleep (majority of time)
Data Persistence: Wake count and battery data preserved across resets using BURAM
Reset-Based Operation: Each wake-up is actually a full device reset
Complete Implementation:#
// In sl_power_manager_config.h, configure the EM4 pin retention mode
#define SL_POWER_MANAGER_INIT_EMU_EM4_PIN_RETENTION_MODE EMU_EM4CTRL_EM4IORETMODE_SWUNLATCH#include "sl_main_init.h"
#include "sl_main_process_action.h"
#include "sl_clock_manager.h"
#include "sl_power_manager.h"
#include "sl_hal_emu.h"
#include "sl_hal_burtc.h"
#include "sl_gpio.h"
#include "sl_hal_gpio.h"
#include <stdio.h>
#include <string.h>
#define BATTERY_CRITICAL_MV 2000
// Data retained across EM4 resets
typedef struct {
uint32_t wake_count; // Number of EM4 wake-ups
uint32_t battery_mv; // Battery voltage in millivolts
} buram_data_t;
static buram_data_t buram_data = { 0 };
static uint32_t reset_cause;
// Configure GPIO pin as input for EM4 wake-up
// This example uses GPIO PB1 as a button input
void init_gpio_em4(void)
{
// Configure PB1 as input
sl_gpio_set_pin_mode(PB1, SL_GPIO_MODE_INPUT, false);
// Configure PB1 as EM4 wake-up source
int32_t interrupt_number = sl_hal_gpio_get_em4_interrupt_number(PB1);
sl_gpio_configure_wakeup_em4_interrupt(PB1, &interrupt_number, false, NULL, NULL);
printf("GPIO PB1 configured for EM4 wake-up (button press)\n");
}
// Configure BURTC for EM4 wake-up every 5 minutes
void init_burtc(void)
{
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_BURTC);
sl_hal_burtc_init_config_t config = {
.debug_run = false,
.clock_divider = 32768,
.compare0_top = true,
.em4_comparator = true, // Enable EM4 wake-up for compare interrupt
.em4_overflow = false
};
sl_hal_burtc_init(&config);
sl_hal_burtc_enable();
sl_hal_burtc_set_compare(300); // 5 minutes at 32.768 kHz with 32768 divider
sl_hal_burtc_enable_interrupts(BURTC_IF_COMP);
sl_hal_burtc_start();
}
// Simulate battery voltage reading with degradation over time
uint32_t read_battery_voltage()
{
// Simulate gradual battery discharge over multiple EM4 cycles
// In a real application, use ADC
return buram_data.battery_mv - 1; // Voltage drops slowly
}
void normal_boot(void)
{
// Initialize data structure
buram_data.wake_count = 0;
buram_data.battery_mv = 3000; // Starting at 3.0 V
// Configure a GPIO to wake-up from EM4
init_gpio_em4();
// Configure BURTC for EM4 wake-up every 5 minutes
init_burtc();
// Add a 1 second delay to let the debugger connect
sl_sleeptimer_delay_millisecond(1000);
}
void em4_boot(void)
{
// Restore data from BURAM after EM4 wake-up
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_BURAM);
memcpy(&buram_data, &BURAM->RET[0], sizeof(buram_data));
// Increment the number of wake-up
buram_data.wake_count++;
printf("Wake-up from EM4 #%lu\n", buram_data.wake_count);
// Configure a GPIO to wake-up from EM4
// Required as GPIO is not retained in EM4
init_gpio_em4();
// BURTC is retained in EM4 so no need to initialize it again
// Resetting the counter to 0
sl_hal_burtc_reset_counter();
}
// The interrupts caused by EM4 wake-up sources and the unlatching of pins
// Handle as early as possible
void app_init_pre_clock(void)
{
// Check reset cause to determine boot type
// Note: This only works without the bootloader. If using a bootloader, refer to specific documentation
reset_cause = sl_hal_emu_get_reset_cause();
sl_hal_emu_clear_reset_cause();
if (reset_cause == EMU_RSTCAUSE_EM4) {
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_GPIO);
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_BURTC);
// Reset the interrupts from EM4 wake-up sources
int32_t interrupt_number = sl_hal_gpio_get_em4_interrupt_number(PB1);
sl_gpio_deconfigure_wakeup_em4_interrupt(interrupt_number);
sl_hal_burtc_clear_interrupts(BURTC_IF_COMP);
// Set pins that need to always stay steady before unlatching pin retention
sl_gpio_set_pin_mode(PB2, SL_GPIO_MODE_PUSH_PULL, true);
// This unlocks pins from their EM4-preserved state back to peripheral control
sl_power_manager_em4_unlatch_pin_retention();
}
}
void app_init(void)
{
// Branch based on wake-up source
if (reset_cause == EMU_RSTCAUSE_EM4) {
// EM4 wake-up path: restored from ultra-low power state
em4_boot();
} else {
// Normal boot path: starts fresh initialization
normal_boot();
}
}
void app_process_action(void)
{
if (buram_data.battery_mv <= BATTERY_CRITICAL_MV) {
printf("CRITICAL: Battery too low, shutting down\n");
} else {
// Check the battery voltage
buram_data.battery_mv = read_battery_voltage();
printf("Battery: %lu mV\n", buram_data.battery_mv);
// Do some processing...
}
// Save key variables to BURAM for persistence across EM4 reset
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_BURAM);
memcpy(&BURAM->RET[0], &buram_data, sizeof(buram_data));
// All GPIO outputs will be latched (preserved) when entering EM4
// Power Manager's pin retention feature maintains this state during ultra-low power
sl_gpio_set_pin_mode(PB2, SL_GPIO_MODE_PUSH_PULL, true);
// Enters EM4 ultra-low power mode and never returns
printf("Entering EM4 sleep\n");
sl_power_manager_enter_em4();
}
int main(void)
{
sl_main_init(); // Power Manager automatically initialized here
app_init();
while (1) {
sl_main_process_action();
app_process_action();
// Sleep at lowest allowed energy mode (EM2/EM3 when there are no requirements)
sl_power_manager_sleep();
}
}