System Real-Time Clock (SYSRTC) Usage Scenarios#
The System Real-Time Clock (SYSRTC) supports a wide range of timing use cases—from simple periodic interrupts to complex multi-channel schedules. This section provides practical examples and implementation guidance for common patterns.
Note: Before continuing, review Initialization and Configuration for basic SYSRTC setup.
For power-state behavior, see the Power State Reference table in the main guide.
Implementation Strategy#
Follow these principles when building SYSRTC-based timing solutions:
Clock source selection:
Use XTAL 32 kHz for highest accuracy; choose RC 32 kHz when minimizing power or startup time is the priority.Group allocation:
Assign separate groups to independent timing functions to avoid resource conflicts and simplify debugging.Interrupt management:
Keep callbacks short—clear flags, update state, and defer work to the main loop or real-time operating system (RTOS) to reduce latency and jitter.Resource optimization:
Enable only the channels and features you need (compare/capture, general-purpose input/output (GPIO) routing, run-in-debug) to save power.Error handling:
Check return values (sl_status_t) for every API call and handle errors immediately (log, retry, or reinitialize).
Tip: For periodic schedules, program the next compare value inside the callback before returning to maintain accurate cadence.
Performance Considerations#
Evaluate these trade-offs to balance accuracy, power, and startup behavior in your SYSRTC design.
Timing accuracy:
Use XTAL 32 kHz for highest precision (typically around ±20 ppm).
RC 32 kHz is lower precision (around ±2%) and may drift with voltage/temperature.Power consumption:
RC generally draws less power than XTAL, useful for ultra-low-power profiles.Startup time:
XTAL requires a stabilization period after enable.
RC starts immediately (or near-instant), helpful for rapid sleep–wake cycles.Temperature stability:
XTAL maintains better stability over temperature than RC; budget drift if using RC for long intervals.
Tip: If long-term accuracy matters with RC, consider periodic calibration against a known reference, or schedule shorter wake intervals to correct accumulated error.
Basic Periodic Timer Operation#
This scenario demonstrates how to create a one-second periodic timer using Group 0 and Compare Channel 0.
#include "sl_si91x_sysrtc.h"
#include "sl_si91x_sysrtc_config.h"
static volatile uint32_t timer_ticks = 0;
static uint32_t callback_ctx = 0; // user context passed to the callback
// SYSRTC callback: SDK passes back the user context pointer (no event flags)
static void timer_callback(void *ctx)
{
(void)ctx; // not used here; could point to a state struct if needed
sl_status_t st;
uint32_t now;
timer_ticks++;
// Read current count
st = sl_si91x_sysrtc_get_count(&now);
if (st != SL_STATUS_OK) {
// optional: record error and stop/reinit
return;
}
// Re-arm next compare for 1 second later (32.768 kHz -> 32768 ticks)
// For RC 32 kHz, use 32000 or your configured compare interval.
uint32_t interval = 32768; // 1 s @ 32.768 kHz XTAL
st = sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0,
SL_SYSRTC_CHANNEL_0,
now + interval);
// Optional: handle st if needed
}
void setup_periodic_timer(void)
{
sl_status_t st;
// 1) Configure LF clock (32 kHz XTAL shown)
sl_sysrtc_clock_config_t clk_config = {
.clock_source = CLK_32KHZ_XTAL, // from sl_si91x_sysrtc_config.h
.division_factor = 0 // no divide (32.768 kHz tick)
};
st = sl_si91x_sysrtc_configure_clock(&clk_config);
if (st != SL_STATUS_OK) return;
// 2) Initialize SYSRTC
sl_sysrtc_config_t sysrtc_config = {
.enable_debug_run = false
// add other fields if present in your config typedef
};
st = sl_si91x_sysrtc_init(&sysrtc_config);
if (st != SL_STATUS_OK) return;
// 3) Configure Group 0 with Compare Channel 0
sl_sysrtc_group_config_t group_config = {
.compare_channel0_enable = true,
.compare_channel1_enable = false,
.capture_channel0_enable = false
};
st = sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_0, &group_config);
if (st != SL_STATUS_OK) return;
// 4) Register callback and enable only the needed interrupts
sl_sysrtc_interrupt_enables_t int_enable = {
.group0_overflow_interrupt_is_enabled = false,
.group0_compare0_interrupt_is_enabled = true,
.group0_compare1_interrupt_is_enabled = false,
.group0_capture0_interrupt_is_enabled = false,
.group1_overflow_interrupt_is_enabled = false,
.group1_compare0_interrupt_is_enabled = false,
.group1_compare1_interrupt_is_enabled = false,
.group1_capture0_interrupt_is_enabled = false
};
st = sl_si91x_sysrtc_register_callback(timer_callback, &callback_ctx,
SL_SYSRTC_GROUP_0, &int_enable);
if (st != SL_STATUS_OK) return;
// 5) Program initial compare and start
// Seed the first event 1 second from *now*
uint32_t now = 0;
st = sl_si91x_sysrtc_get_count(&now);
if (st != SL_STATUS_OK) return;
st = sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0,
SL_SYSRTC_CHANNEL_0,
now + 32768); // 1 s @ 32.768 kHz
if (st != SL_STATUS_OK) return;
sl_si91x_sysrtc_start();
}
Multi-Group Timing System#
This scenario shows how to use multiple groups simultaneously. Group 0 provides one-second timing, and Group 1 provides 100-millisecond timing.
#include "sl_si91x_sysrtc.h"
static volatile uint32_t group0_ticks = 0;
static volatile uint32_t group1_ticks = 0;
static uint32_t group0_flags = 0;
static uint32_t group1_flags = 0;
void group0_callback(void *callback_flag) {
uint32_t flags = *(uint32_t*)callback_flag;
if (flags & SYSRTC_GROUP0_COMPARE0_IF) {
group0_ticks++;
printf("Group 0: %lu seconds\n", group0_ticks);
// Set next compare value (1 second)
uint32_t next_compare = 32768;
sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0,
SL_SYSRTC_CHANNEL_0, next_compare);
}
}
void group1_callback(void *callback_flag) {
uint32_t flags = *(uint32_t*)callback_flag;
if (flags & SYSRTC_GROUP1_COMPARE0_IF) {
group1_ticks++;
printf("Group 1: %lu x 100ms\n", group1_ticks);
// Set next compare value (100ms)
uint32_t next_compare = 3277; // 100ms at 32 kHz
sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_1,
SL_SYSRTC_CHANNEL_0, next_compare);
}
}
void setup_multi_group_timing(void) {
// Clock configuration
sl_sysrtc_clock_config_t clk_config = {
.clock_source = CLK_32KHZ_XTAL,
.division_factor = 0
};
sl_si91x_sysrtc_configure_clock(&clk_config);
// Initialize SYSRTC
sl_sysrtc_config_t sysrtc_config = { .enable_debug_run = false };
sl_si91x_sysrtc_init(&sysrtc_config);
// Configure Group 0 (1 second timing)
sl_sysrtc_group_config_t group0_config = {
.compare_channel0_enable = true,
.compare_channel1_enable = false,
.capture_channel0_enable = false
};
sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_0, &group0_config);
// Configure Group 1 (100ms timing)
sl_sysrtc_group_config_t group1_config = {
.compare_channel0_enable = true,
.compare_channel1_enable = false,
.capture_channel0_enable = false
};
sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_1, &group1_config);
// Register callbacks for both groups
sl_sysrtc_interrupt_enables_t int_enable0 = {
.group0_compare0_interrupt_is_enabled = true
};
sl_si91x_sysrtc_register_callback(group0_callback, &group0_flags,
SL_SYSRTC_GROUP_0, &int_enable0);
sl_sysrtc_interrupt_enables_t int_enable1 = {
.group1_compare0_interrupt_is_enabled = true
};
sl_si91x_sysrtc_register_callback(group1_callback, &group1_flags,
SL_SYSRTC_GROUP_1, &int_enable1);
// Set initial compare values and start
sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0,
SL_SYSRTC_CHANNEL_0, 32768);
sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_1,
SL_SYSRTC_CHANNEL_0, 3277);
sl_si91x_sysrtc_start();
}GPIO Waveform Generation#
This scenario explains how to generate a square wave with a 50% duty cycle on a general-purpose input/output (GPIO) pin using Compare Channel 0.
#include "sl_si91x_sysrtc.h"
static volatile uint32_t group0_ticks = 0;
static volatile uint32_t group1_ticks = 0;
static volatile uint32_t isr_flags = 0; // the driver ORs bits into this via the pointer we pass
static void sysrtc_callback(void *flags_ptr)
{
uint32_t flags = *(uint32_t *)flags_ptr;
uint32_t now;
// Clear our local copy after sampling; the driver will keep OR-ing into the pointed variable
*(uint32_t *)flags_ptr = 0;
// Read current counter once for re-arming
if (sl_si91x_sysrtc_get_count(&now) != SL_STATUS_OK) {
return;
}
// Group 0, Compare 0 -> 1 s @ 32 kHz
if (flags & SYSRTC_GRP0_IF_CMP0) {
group0_ticks++;
uint32_t next = now + 32768U; // 1.000 s
(void)sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0, SL_SYSRTC_CHANNEL_0, next);
}
// Group 1, Compare 0 -> 100 ms @ 32 kHz (~3276.8 ticks)
if (flags & SYSRTC_GRP1_IF_CMP0IF) {
group1_ticks++;
uint32_t next = now + 3277U; // ~0.100 s
(void)sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_1, SL_SYSRTC_CHANNEL_0, next);
}
}
void setup_multi_group_timing(void)
{
// 1) Clock: 32 kHz (example uses XTAL; choose the source your board uses)
sl_sysrtc_clock_config_t clk_cfg = {
.clock_source = RSI_SYSRTC_CLK_32kHz_Xtal, // or RSI_SYSRTC_CLK_32kHz_RC / RO / 1kHz
.division_factor = 0
};
(void)sl_si91x_sysrtc_configure_clock(&clk_cfg); // check return in production
sl_sysrtc_config_t init_cfg = { .enable_debug_run = false };
(void)sl_si91x_sysrtc_init(&init_cfg);
// 2) Configure both groups with Compare0 enabled
sl_sysrtc_group_config_t g0 = {
.counter_direction_is_up = true,
.compare_channel0_is_enabled = true,
.compare_channel1_is_enabled = false,
.capture_channel0_is_enabled = false,
.p_compare_channel0_config = NULL,
.p_compare_channel1_config = NULL,
.p_capture_channel0_config = NULL
};
(void)sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_0, &g0);
sl_sysrtc_group_config_t g1 = g0; // same shape; only group number differs
(void)sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_1, &g1);
// 3) Register ONE callback and enable interrupts.
// NOTE: The SDK only allows a single registered callback at a time.
sl_sysrtc_interrupt_enables_t ie = {
.group0_compare0_interrupt_is_enabled = true,
.group1_compare0_interrupt_is_enabled = true,
// other bits false
};
(void)sl_si91x_sysrtc_register_callback(sysrtc_callback, (void *)&isr_flags,
SL_SYSRTC_GROUP_0, &ie);
// ↑ The API takes a group_number; the implementation enables masks for that group,
// but the flags structure allows you to request bits from both groups.
// Because only one callback is supported, we enable via the single call here.
// 4) Seed initial compares relative to current count and start
uint32_t now = 0;
(void)sl_si91x_sysrtc_get_count(&now);
(void)sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_0, SL_SYSRTC_CHANNEL_0, now + 32768U);
(void)sl_si91x_sysrtc_set_compare_value(SL_SYSRTC_GROUP_1, SL_SYSRTC_CHANNEL_0, now + 3277U);
sl_si91x_sysrtc_start();
}
Event Capture and Timestamping#
This scenario demonstrates how to capture external events and timestamp using Capture Channel 0.
#include "sl_si91x_sysrtc.h"
typedef struct {
uint32_t timestamp;
uint32_t event_count;
} event_data_t;
static event_data_t event_buffer[100];
static uint32_t event_index = 0;
static uint32_t callback_flags = 0;
static void capture_callback(void *callback_flag) {
uint32_t flags = *(uint32_t *)callback_flag;
if (flags & SYSRTC_GRP0_IF_CAP0) {
uint32_t timestamp = 0;
if (sl_si91x_sysrtc_get_capture_value(SL_SYSRTC_GROUP_0, ×tamp) == SL_STATUS_OK) {
if (event_index < 100) {
event_buffer[event_index].timestamp = timestamp;
event_buffer[event_index].event_count = event_index + 1;
event_index++;
printf("Event %lu captured at timestamp: %lu\n", event_index, timestamp);
}
}
}
}
void setup_event_capture(void) {
// 1) Configure LF clock (example: 32 kHz XTAL, div=0)
sl_sysrtc_clock_config_t clk_config = {
.clock_source = CLK_32KHZ_XTAL,
.division_factor = 0,
};
(void)sl_si91x_sysrtc_configure_clock(&clk_config);
// 2) Initialize SYSRTC (optionally disable run-in-debug)
sl_sysrtc_config_t sysrtc_config = {
.enable_debug_run = false,
};
(void)sl_si91x_sysrtc_init(&sysrtc_config);
// 3) Enable Group 0 with Capture Channel 0
sl_sysrtc_group_config_t group_cfg = {
.compare_channel0_enable = false,
.compare_channel1_enable = false,
.capture_channel0_enable = true,
};
(void)sl_si91x_sysrtc_configure_group(SL_SYSRTC_GROUP_0, &group_cfg);
// 4) Select GPIO mode and route capture input to a GPIO (Group 0)
sl_si91x_sysrtc_enable_input_output_gpio(true);
(void)sl_si91x_sysrtc_set_gpio_as_capture_input(SL_SYSRTC_GROUP_0);
// 5) Register callback and enable Group 0 capture0 interrupt
sl_sysrtc_interrupt_enables_t int_en = {0};
int_en.group0_capture0_interrupt_is_enabled = true;
(void)sl_si91x_sysrtc_register_callback(capture_callback, &callback_flags,
SL_SYSRTC_GROUP_0, &int_en);
// 6) Start the counter
sl_si91x_sysrtc_start();
}
Common Errors and Best Practices#
This section describes common implementation errors and how to avoid them.
Timing Accuracy#
Issue: Setting compare values as
current_time + period.Best Practice: Use absolute values and increment by period in the interrupt service routine (ISR).
Reason: Prevents timing drift caused by ISR latency.
Interrupt Handling#
Issue: Performing long operations in callback functions.
Best Practice: Keep callbacks short and use flags for processing in the main loop.
Reason: Prevents missed interrupts.
Resource Management#
Issue: Not checking return values from application programming interface (API) calls.
Best Practice: Always validate return values and handle errors.
Reason: Prevents silent failures and instability.
Testing Guidelines#
Testing ensures reliable SYSRTC operation across environments and use cases.
Measure timing accuracy with an oscilloscope or logic analyzer.
Verify interrupt timing under different system loads.
Test power consumption in sleep modes.
Validate multi-group operation with varying timing requirements.