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