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:

  1. Clock source selection:
    Use XTAL 32 kHz for highest accuracy; choose RC 32 kHz when minimizing power or startup time is the priority.

  2. Group allocation:
    Assign separate groups to independent timing functions to avoid resource conflicts and simplify debugging.

  3. 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.

  4. Resource optimization:
    Enable only the channels and features you need (compare/capture, general-purpose input/output (GPIO) routing, run-in-debug) to save power.

  5. 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, &timestamp) == 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.