Ultra-Low-Power (ULP) Timer Usage Scenarios#

The Ultra-Low-Power (ULP) Timer is a hardware resource designed for efficient timing operations in SiWx917 power-sensitive systems. You can use ULP Timers for periodic sampling, one-shot timeouts, runtime reconfiguration, and scheduling wake events from low-power states.

Each timer runs its callback in interrupt context, so callback functions must remain short and efficient. Use callbacks only to signal events — not to execute long operations.

Key Principle: Keep Callbacks Lightweight#

Callbacks run in interrupt context and must complete quickly to maintain system timing and power efficiency.

Why This Matters#

When a timer expires, the interrupt controller immediately executes your callback function.
If the callback takes too long, it can:

  • Introduce timing jitter

  • Delay system responsiveness

  • Increase power consumption

  • Cause missed deadlines in real-time applications

Best Practice#

Use callbacks as signals, not work engines.

Within a callback:

  • Set flags or counters.

  • Post events to your RTOS or main loop.

  • Defer heavy processing to background tasks.

Tip: Keep callback logic under 10 µs execution time whenever possible.

Common Usage Patterns#

ULP Timers support multiple common use cases. Each of the following examples highlights configuration details and power-state considerations.

1. Periodic 1 Hz Toggle#

Use Case:
Blink an LED or toggle a status GPIO every second while the MCU sleeps.

Parameter

Value

Type

1 µs

Mode

Periodic

Match Value

1,000,000 (1 second)

Behavior:
The timer counts down from 1,000,000 to 0, reloads automatically, and repeats.

Power State Compatibility:
Operates in PS4 and PS2, and functions as a wake source in PS1.

2. One-Shot Timeout#

Use Case:
Implement watchdog or delay functions.

Parameter

Value

Mode

One-shot

Example Duration

5 seconds

Behavior:
The timer runs once, triggers an interrupt, then stops.

Example:
Wake a sensor exactly five seconds after power-up for its first reading.

Power State:
Ideal for PS1 wake-source operations.

3. Runtime Reconfiguration#

Use Case:
Adaptive timing or multi-rate sampling (for example, 1-second active sampling or 5-minute idle interval).

Concept:
Dynamically change timer parameters at runtime.

Steps:

  1. Stop the running timer.

  2. Update mode, type, or match value.

  3. Restart the timer.

  4. Validate using get_count() and get_direction() APIs.

Power State:
Supports mode switching between PS4 and PS2 for flexible performance control.

4. Multi-Instance Operation#

Architecture:
All four ULP Timers share the same clock domain but run independently.

Example Use Case:

Timer

Interval

Purpose

Timer 0

100 ms

Soil moisture sampling

Timer 1

1 s

Temperature monitoring

Timer 2

5 min

Health check

Timer 3

1 hr

Data logging

Benefit:
Parallel timing for multi-sensor or system tasks.

Advanced Usage Patterns#

These patterns extend the ULP Timer’s flexibility for sophisticated timing solutions.

5. Cascading Timers – Extended Sequencing#

Concept:
Trigger one timer from another for multi-phase or chained events.

Use Case:
Sequenced tasks such as staged sensor activation or multi-step wake-ups.

Example:
Timer 0 triggers Timer 1 after expiration to start a follow-up process.

6. Adaptive Timing – Dynamic Period Adjustment#

Concept:
Adjust timer period or resolution dynamically based on system conditions.

Use Case:
Adaptive sampling or power scaling.

Example:
Sample sensors every 10 seconds with full battery and every 60 seconds when the battery is low.

Benefit:
Optimizes balance between timing precision and energy efficiency.

7. Synchronized Timers – Coordinated Operation#

Concept:
Align multiple timers with precise offsets for synchronous behavior.

Use Case:
Motor control, communication protocols, and synchronized sampling.

Example:
Timers 0–2 start with fixed phase offsets to align control signals and measurements.

Tip: Use consistent clock sources and calibrated match values to maintain long-term synchronization accuracy.

Basic Usage Example#

status = sl_si91x_ulp_timer_init(&sl_timer_clk_handle);
status = sl_si91x_ulp_timer_get_match_value(SL_ULP_TIMER_HANDLE.timer_type, TIME_IN_MICROSECONDS, &match_value);
SL_ULP_TIMER_HANDLE.timer_match_value = match_value;
status = sl_si91x_ulp_timer_set_configuration(&(SL_ULP_TIMER_HANDLE));
status = sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_INSTANCE, &(SL_ULP_TIMER_CALLBACK));
status = sl_si91x_ulp_timer_start(ULP_TIMER_INSTANCE);

What Each Line Does#

Step

Description

1. Initialize

Sets up the timer clock source and enables ULP operation.

2. Get Match Value

Calculates the match value based on resolution and timeout.

3. Configure

Defines mode, type, and match value, enabling interrupts.

4. Register Callback

Links your timeout handler to the timer.

5. Start

Begins countdown and operation independently of the CPU.

Example: Smart Sensor System#

The following example demonstrates multiple timers in a low-power smart sensor design.

#include "sl_si91x_ulp_timer.h"
#include "sl_si91x_power_manager.h"

// Global variables for the application
volatile bool sensor_read_pending = false;
volatile bool system_maintenance_pending = false;
volatile uint32_t sensor_read_count = 0;

// Timer callback functions
void sensor_timer_callback(void) {
    sensor_read_pending = true;
    sensor_read_count++;
}

void maintenance_timer_callback(void) {
    system_maintenance_pending = true;
}

// Main application function
void setup_smart_sensor_system(void) {
    sl_status_t status;
    
    // 1. Initialize the ULP timer system with 32kHz XTAL for low power
    ulp_timer_clk_src_config_t clock_config = {
        .ulp_timer_clk_type = ULP_TIMER_CLK_TYPE_STATIC,
        .ulp_timer_sync_to_ulpss_pclk = false,
        .ulp_timer_clk_input_src = ULP_TIMER_32KHZ_XTAL_CLK_SRC,
        .ulp_timer_skip_switch_time = true
    };
    
    status = sl_si91x_ulp_timer_init(&clock_config);
    if (status != SL_STATUS_OK) {
        // Handle initialization error
        return;
    }
    
    // 2. Configure Timer 0 for sensor readings every 30 seconds
    uint32_t sensor_match_value;
    status = sl_si91x_ulp_timer_get_match_value(ULP_TIMER_TYP_1US, 30000000, &sensor_match_value);
    if (status != SL_STATUS_OK) {
        // Handle match value calculation error
        return;
    }
    
    ulp_timer_config_t sensor_timer_config = {
        .timer_num = ULP_TIMER_0,
        .timer_mode = ULP_TIMER_MODE_PERIODIC,
        .timer_type = ULP_TIMER_TYP_1US,
        .timer_match_value = sensor_match_value,
        .timer_direction = ULP_TIMER_DIRECTION_DOWN
    };
    
    status = sl_si91x_ulp_timer_set_configuration(&sensor_timer_config);
    if (status != SL_STATUS_OK) {
        // Handle configuration error
        return;
    }
    
    // 3. Configure Timer 1 for system maintenance every 5 minutes
    uint32_t maintenance_match_value;
    status = sl_si91x_ulp_timer_get_match_value(ULP_TIMER_TYP_1US, 300000000, &maintenance_match_value);
    if (status != SL_STATUS_OK) {
        // Handle match value calculation error
        return;
    }
    
    ulp_timer_config_t maintenance_timer_config = {
        .timer_num = ULP_TIMER_1,
        .timer_mode = ULP_TIMER_MODE_PERIODIC,
        .timer_type = ULP_TIMER_TYP_1US,
        .timer_match_value = maintenance_match_value,
        .timer_direction = ULP_TIMER_DIRECTION_DOWN
    };
    
    status = sl_si91x_ulp_timer_set_configuration(&maintenance_timer_config);
    if (status != SL_STATUS_OK) {
        // Handle configuration error
        return;
    }
    
    // 4. Register callback functions
    status = sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_0, sensor_timer_callback);
    if (status != SL_STATUS_OK) {
        // Handle callback registration error
        return;
    }
    
    status = sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_1, maintenance_timer_callback);
    if (status != SL_STATUS_OK) {
        // Handle callback registration error
        return;
    }
    
    // 5. Configure power manager to use ULP timer as wake source
    status = sl_si91x_power_manager_set_wakeup_sources(SL_SI91X_POWER_MANAGER_ULPSS_WAKEUP, true);
    if (status != SL_STATUS_OK) {
        // Handle power manager configuration error
        return;
    }
    
    // 6. Start both timers
    status = sl_si91x_ulp_timer_start(ULP_TIMER_0);
    if (status != SL_STATUS_OK) {
        // Handle start error
        return;
    }
    
    status = sl_si91x_ulp_timer_start(ULP_TIMER_1);
    if (status != SL_STATUS_OK) {
        // Handle start error
        return;
    }
    
    // System is now configured and running!
    // The main application can enter low power mode while timers continue running
}

This example demonstrates:

  • Multiple independent timers

  • Low-power operation with sleep and wake cycles

  • Proper initialization, configuration, and callback registration

  • ULP timers as power manager wake sources

Power State-Specific Examples#

PS4 (High Power Mode) - Full Functionality#

Use high-speed clocks for precision timing.

// High-precision timing for active operation
void setup_high_precision_timing(void) {
    // Use REF clock for maximum accuracy
    ulp_timer_clk_src_config_t high_accuracy_clock = {
        .ulp_timer_clk_type = ULP_TIMER_CLK_TYPE_STATIC,
        .ulp_timer_sync_to_ulpss_pclk = false,
        .ulp_timer_clk_input_src = ULP_TIMER_REF_CLK_SRC,  // 32MHz for precision
        .ulp_timer_skip_switch_time = true
    };
    
    sl_si91x_ulp_timer_init(&high_accuracy_clock);
    
    // Configure for microsecond precision
    ulp_timer_config_t precision_config = {
        .timer_num = ULP_TIMER_0,
        .timer_mode = ULP_TIMER_MODE_PERIODIC,
        .timer_type = ULP_TIMER_TYP_1US,
        .timer_match_value = 1000,  // 1ms with microsecond precision
        .timer_direction = ULP_TIMER_DIRECTION_DOWN
    };
    
    sl_si91x_ulp_timer_set_configuration(&precision_config);
    sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_0, precision_callback);
    sl_si91x_ulp_timer_start(ULP_TIMER_0);
}

PS2 (Ultra-Low-Power Mode) - Balanced Operation#

Use 32 kHz XTAL for long intervals with minimal power.

// Balanced timing for ULP mode
void setup_ulp_mode_timing(void) {
    // Use 32kHz XTAL for good balance of power and accuracy
    ulp_timer_clk_src_config_t ulp_clock = {
        .ulp_timer_clk_type = ULP_TIMER_CLK_TYPE_STATIC,
        .ulp_timer_sync_to_ulpss_pclk = false,
        .ulp_timer_clk_input_src = ULP_TIMER_32KHZ_XTAL_CLK_SRC,
        .ulp_timer_skip_switch_time = true
    };
    
    sl_si91x_ulp_timer_init(&ulp_clock);
    
    // Configure for second-level precision
    ulp_timer_config_t ulp_config = {
        .timer_num = ULP_TIMER_0,
        .timer_mode = ULP_TIMER_MODE_PERIODIC,
        .timer_type = ULP_TIMER_TYP_1US,
        .timer_match_value = 30000000,  // 30 seconds
        .timer_direction = ULP_TIMER_DIRECTION_DOWN
    };
    
    sl_si91x_ulp_timer_set_configuration(&ulp_config);
    sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_0, ulp_callback);
    sl_si91x_ulp_timer_start(ULP_TIMER_0);
}

PS1 (Sleep Mode) - Wake Source Only#

ULP Timer as a wake source for scheduled wake-ups.

// Wake source configuration for sleep mode
void setup_sleep_mode_wakeup(void) {
    // Must use 32kHz clock for PS1
    ulp_timer_clk_src_config_t sleep_clock = {
        .ulp_timer_clk_type = ULP_TIMER_CLK_TYPE_STATIC,
        .ulp_timer_sync_to_ulpss_pclk = false,
        .ulp_timer_clk_input_src = ULP_TIMER_32KHZ_XTAL_CLK_SRC,
        .ulp_timer_skip_switch_time = true
    };
    
    sl_si91x_ulp_timer_init(&sleep_clock);
    
    // Configure as one-shot wake source
    ulp_timer_config_t wakeup_config = {
        .timer_num = ULP_TIMER_0,
        .timer_mode = ULP_TIMER_MODE_ONESHOT,
        .timer_type = ULP_TIMER_TYP_1US,
        .timer_match_value = 60000000,  // 1 minute
        .timer_direction = ULP_TIMER_DIRECTION_DOWN
    };
    
    sl_si91x_ulp_timer_set_configuration(&wakeup_config);
    sl_si91x_ulp_timer_register_timeout_callback(ULP_TIMER_0, wakeup_callback);
    
    // Configure as wake source in power manager
    sl_si91x_power_manager_set_wakeup_sources(SL_SI91X_POWER_MANAGER_ULPSS_WAKEUP, true);
    
    // Start timer before entering sleep mode
    sl_si91x_ulp_timer_start(ULP_TIMER_0);
}

Best Practices and Testing#

  • Starting a timer before registering a callback.

  • Ignoring status codes.

  • Running heavy logic inside callbacks.

  • Using an incorrect clock for the power state.

  • Forgetting wake-source configuration for PS1.

Testing Recommendations#

  • Start with simple 1-second blink tests.

  • Measure accuracy using GPIO toggling.

  • Validate timer operation across all power states.

  • Perform long-duration and stress testing.

  • Confirm wake behavior during PS1 sleep mode.

Implementation Strategy#

Use the following structured approach to implement ULP Timers effectively in your SiWx917 application.

Step-by-step Integration Plan#

  1. Define system requirements: Determine timing intervals, accuracy, and power constraints.

  2. Plan power-state behavior: Identify how timers will operate in PS4, PS2, and PS1 states.

  3. Select appropriate clock sources: Choose between 32 MHz, 32 kHz, or RC clocks based on performance and power needs.

  4. Assign timers to tasks: Allocate individual ULP Timer instances for specific functions such as sensor sampling or maintenance.

  5. Design configurations: Specify timer type, mode, direction, and match value for each instance.

  6. Develop lightweight callbacks: Keep interrupt routines short; delegate heavy processing to background tasks.

  7. Integrate with the Power Manager: Configure timers as wake sources where applicable.

  8. Test across power states: Validate timer behavior and wake-up accuracy in PS4, PS2, and PS1.

  9. Optimize after validation: Fine-tune match values, clock sources, and resolution based on measured performance.

Tip: Begin testing with simple timing tasks (for example, 1-second LED blink) before scaling to complex multi-timer applications.

Advanced Power Management Patterns#

Advanced timing strategies allow ULP Timers to adapt dynamically to system power modes and operational priorities.

Adaptive Clock Switching#

You can switch clock sources dynamically to balance performance and power consumption across different power states.

// Switch clocks based on power state
void adapt_timing_for_power_state(power_state_t target_ps) {
    switch (target_ps) {
        case PS4:
            // Use high-precision clock for active operation
            switch_to_ref_clock();
            break;
            
        case PS2:
            // Use balanced clock for ULP mode
            switch_to_32khz_xtal_clock();
            break;
            
        case PS1:
            // Use low-power clock for sleep mode
            switch_to_32khz_xtal_clock();
            configure_as_wake_source();
            break;
            
        case PS0:
            // Timers not available in deep sleep
            stop_all_timers();
            break;
    }
}

Note: Use 32 MHz reference clock in PS4 for high-speed applications, and switch to 32 kHz XTAL in PS2 and PS1 for optimal energy savings.

Multi-Timer Power Coordination - Overview#

Coordinate multiple ULP Timers to perform complementary tasks across different power states.
For example, use fast timers in active mode for time-critical operations and slow timers in low-power modes for background or maintenance tasks.

// Coordinate multiple timers for power management
void setup_power_management_timers(void) {
    // Timer 0: Fast timing for active mode
    setup_timer_for_active_mode(ULP_TIMER_0);
    
    // Timer 1: Medium timing for ULP mode
    setup_timer_for_ulp_mode(ULP_TIMER_1);
    
    // Timer 2: Slow timing for sleep mode wakeup
    setup_timer_for_sleep_wakeup(ULP_TIMER_2);
    
    // Timer 3: System maintenance timing
    setup_timer_for_maintenance(ULP_TIMER_3);
}

Tip: Each timer can operate independently but share the same clock source, enabling synchronized, low-power multitasking across system modes