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:
Stop the running timer.
Update mode, type, or match value.
Restart the timer.
Validate using
get_count()andget_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#
Define system requirements: Determine timing intervals, accuracy, and power constraints.
Plan power-state behavior: Identify how timers will operate in PS4, PS2, and PS1 states.
Select appropriate clock sources: Choose between 32 MHz, 32 kHz, or RC clocks based on performance and power needs.
Assign timers to tasks: Allocate individual ULP Timer instances for specific functions such as sensor sampling or maintenance.
Design configurations: Specify timer type, mode, direction, and match value for each instance.
Develop lightweight callbacks: Keep interrupt routines short; delegate heavy processing to background tasks.
Integrate with the Power Manager: Configure timers as wake sources where applicable.
Test across power states: Validate timer behavior and wake-up accuracy in PS4, PS2, and PS1.
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