Configurable Timer (CT) Usage Scenarios#
The Configurable Timer (CT) in the Silicon Labs SiWx917 enables a wide range of timing and signal-generation applications. You can use it for simple periodic interrupts, precise pulse-width modulation (PWM) waveforms, event capture, or advanced real-time control with direct memory access (DMA) and interrupt-driven operation.
This section provides practical examples and implementation steps to help you apply CT features in your SiWx917-based applications.
Usage Scenarios#
This section describes common and advanced use cases of the Configurable Timer, including:
Basic timing using periodic interrupts
PWM generation for waveform output
Input capture for signal measurement
DMA-assisted event-driven timing
Each scenario includes the purpose, configuration steps, and code examples.
Operational Modes#
The Configurable Timer can operate in two main modes:
Counter mode: Provides standard timing functionality such as periodic interrupts, pulse counting, or delay generation.
Signal mode: Enables PWM generation, waveform output, and input capture operations.
Choose the mode that best suits your application’s timing requirements and complexity.
Key Principles for Successful CT Usage#
To use the Configurable Timer effectively, keep these principles in mind:
Independent counters: Each counter operates independently and can be used for separate timing tasks.
Event-driven operation: CT supports trigger-based operations, ideal for synchronized or periodic events.
DMA integration: Use DMA for jitter-free waveform updates or precise capture timing.
Interrupt efficiency: Keep interrupt service routine (ISR) logic lightweight and move extensive processing to the main loop.
Synchronization: Use synchronization features for phase-aligned or multi-channel PWM applications.
Common Usage Patterns#
The following sections describe basic and advanced CT usage patterns with detailed examples.
Example 1: Periodic Interrupt (Peak Reached) – Basic Timer Operation#
Use CT to generate periodic interrupts at fixed intervals. This is ideal for scheduling tasks such as LED blinking, sensor sampling, or periodic data logging.
Use Case#
Generate a 1 kHz interrupt (1 ms interval) for system timekeeping.
Implementation Steps#
Calculate match value using
get_match_value()for a 1 ms interval.Enable peak interrupt flag for the selected counter.
Start the counter with a software trigger.
Handle interrupts in the callback function by setting a flag for the main loop.
Example Code#
#include "sl_si91x_config_timer.h"
// Global flag for main loop processing
volatile bool timer_interrupt_flag = false;
// Interrupt callback function
void on_timer_callback(sl_config_timer_interrupt_flags_t flags) {
if (flags.is_counter0_hit_peak_interrupt_enabled) {
timer_interrupt_flag = true;
}
}
// Main timer setup function
sl_status_t setup_periodic_timer(void) {
sl_status_t status;
uint16_t match_value;
// Initialize CT peripheral
sl_si91x_config_timer_init();
// Calculate match value for 1ms period (1000μs)
status = sl_si91x_config_timer_get_match_value(1000, &match_value);
if (status != SL_STATUS_OK) {
return status;
}
// Configure counter for periodic operation
sl_config_timer_config_t ct_config = {
.is_counter_mode_32bit_enabled = SL_COUNTER_16BIT,
.counter0_direction = SL_COUNTER0_UP,
.is_counter0_periodic_enabled = true,
.is_counter0_sync_trigger_enabled = false
};
status = sl_si91x_config_timer_set_configuration(&ct_config);
if (status != SL_STATUS_OK) {
return status;
}
// Set the match count
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_0, match_value);
if (status != SL_STATUS_OK) {
return status;
}
// Configure interrupt flags
sl_config_timer_interrupt_flags_t interrupt_flags = {
.is_counter0_hit_peak_interrupt_enabled = true
};
// Register callback
status = sl_si91x_config_timer_register_callback(on_timer_callback, &interrupt_flags);
if (status != SL_STATUS_OK) {
return status;
}
// Start the counter
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_0);
if (status != SL_STATUS_OK) {
return status;
}
return SL_STATUS_OK;
}
// Main loop processing
void main_loop(void) {
while (1) {
if (timer_interrupt_flag) {
timer_interrupt_flag = false;
// Perform periodic tasks here
printf("1ms timer tick\n");
}
// Other application code
}
}Example 2: PWM Generation – Output Compare Mode#
In PWM mode, CT generates periodic digital signals with adjustable frequency and duty cycle.
This mode is ideal for motor control, LED dimming**, power regulation, or any application that requires precise waveform generation.
Use Case#
Generate a 10 kHz PWM signal on OUT0 with a 50% duty cycle and a synchronized 5 kHz PWM on OUT1 with a 25% duty cycle.
Implementation Steps#
Enable the Output Compare Unit (OCU) outputs for both counters.
Configure the toggle mode to define the PWM signal’s high and low durations.
Set compare values relative to the match values to control the duty cycle.
Start both counters simultaneously to ensure synchronized PWM output.
Example Code#
#include "sl_si91x_config_timer.h"
// PWM callback function
void on_pwm_callback(sl_config_timer_interrupt_flags_t flags) {
// Handle PWM-related events if needed
}
// PWM setup function
sl_status_t setup_dual_pwm(void) {
sl_status_t status;
uint16_t match_value_10khz, match_value_5khz;
// Initialize CT peripheral
sl_si91x_config_timer_init();
// Calculate match values for 10kHz and 5kHz periods
// 10kHz = 100μs period, 5kHz = 200μs period
status = sl_si91x_config_timer_get_match_value(100, &match_value_10khz);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_get_match_value(200, &match_value_5khz);
if (status != SL_STATUS_OK) return status;
// Configure both counters for periodic operation
sl_config_timer_config_t ct_config = {
.is_counter_mode_32bit_enabled = SL_COUNTER_16BIT,
.counter0_direction = SL_COUNTER0_UP,
.counter1_direction = SL_COUNTER1_UP,
.is_counter0_periodic_enabled = true,
.is_counter1_periodic_enabled = true,
.is_counter0_sync_trigger_enabled = true,
.is_counter1_sync_trigger_enabled = true
};
status = sl_si91x_config_timer_set_configuration(&ct_config);
if (status != SL_STATUS_OK) return status;
// Configure OCU for both counters
sl_config_timer_ocu_config_t ocu_config = {
.is_counter0_ocu_output_enabled = true,
.is_counter1_ocu_output_enabled = true,
.is_counter0_toggle_output_high_enabled = true,
.is_counter0_toggle_output_low_enabled = true,
.is_counter1_toggle_output_high_enabled = true,
.is_counter1_toggle_output_low_enabled = true
};
status = sl_si91x_config_timer_set_ocu_configuration(&ocu_config);
if (status != SL_STATUS_OK) return status;
// Set match counts
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_0, match_value_10khz);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_1, match_value_5khz);
if (status != SL_STATUS_OK) return status;
// Configure OCU control for Counter0 (50% duty cycle)
sl_config_timer_ocu_control_t ocu_control_0 = {
.is_counter_number_1 = SL_COUNTER_0,
.is_dma_state_enabled = false,
.params = &ocu_params,
.callback = &on_pwm_callback
};
// Set compare value for 50% duty cycle (half of match value)
ocu_params.CompareVal1_0 = match_value_10khz / 2;
status = sl_si91x_config_timer_set_ocu_control(&ocu_control_0);
if (status != SL_STATUS_OK) return status;
// Configure OCU control for Counter1 (25% duty cycle)
sl_config_timer_ocu_control_t ocu_control_1 = {
.is_counter_number_1 = SL_COUNTER_1,
.is_dma_state_enabled = false,
.params = &ocu_params_1,
.callback = &on_pwm_callback
};
// Set compare value for 25% duty cycle (quarter of match value)
ocu_params_1.CompareVal1_0 = match_value_5khz / 4;
status = sl_si91x_config_timer_set_ocu_control(&ocu_control_1);
if (status != SL_STATUS_OK) return status;
// Start both counters
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_0);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_1);
if (status != SL_STATUS_OK) return status;
return SL_STATUS_OK;
}Example 3: PWM with DMA – Advanced PWM Generation#
You can use DMA with the CT to automatically update compare values during PWM operation. This enables smooth duty cycle transitions without CPU involvement, ideal for applications such as LED breathing effects or gradual motor speed control.
Use Case#
Generate a PWM waveform that smoothly varies its duty cycle from 10% to 90% and back, creating a soft transition pattern.
Implementation Steps#
Precompute duty cycle values — Create an array of compare values representing the desired duty-cycle transitions (for example, 10% → 90% → 10%).
Enable the OCU with DMA support for automatic compare value updates.
Configure DMA channels to transfer the precomputed values from memory to the CT compare registers.
Handle DMA completion events to restart or reverse the duty cycle sequence for continuous operation.
Example Code#
#include "sl_si91x_config_timer.h"
#include "sl_si91x_dma.h"
// DMA transfer configuration
extern sl_dma_xfer_t ct_dma_transfer_channel_0;
extern sl_dma_xfer_t ct_dma_transfer_channel_1;
// Global variables for DMA operation
volatile uint8_t ct_dma_transfer_flag_channel_0 = 0;
volatile uint8_t ct_dma_transfer_flag_channel_1 = 0;
volatile uint32_t ct_dma_transfer_index_channel_0 = 0;
volatile uint32_t ct_dma_transfer_index_channel_1 = 0;
// Breathing effect compare values (10% to 90% duty cycle)
uint16_t breathing_compare_values[100];
// DMA completion callback
void on_dma_callback(sl_config_timer_interrupt_flags_t flags) {
if (flags.is_counter0_hit_peak_interrupt_enabled) {
ct_dma_transfer_flag_channel_0 = 1;
}
if (flags.is_counter1_hit_peak_interrupt_enabled) {
ct_dma_transfer_flag_channel_1 = 1;
}
}
// Initialize breathing effect compare values
void init_breathing_compare_values(uint16_t match_value) {
for (int i = 0; i < 100; i++) {
// Create smooth sine wave-like pattern
float duty_cycle = 0.1f + 0.4f * (1.0f + sin(2.0f * M_PI * i / 100.0f));
breathing_compare_values[i] = (uint16_t)(match_value * duty_cycle);
}
}
// DMA-assisted PWM setup
sl_status_t setup_breathing_pwm(void) {
sl_status_t status;
uint16_t match_value;
// Initialize CT peripheral
sl_si91x_config_timer_init();
// Calculate match value for 1kHz period (1000μs)
status = sl_si91x_config_timer_get_match_value(1000, &match_value);
if (status != SL_STATUS_OK) return status;
// Initialize breathing effect compare values
init_breathing_compare_values(match_value);
// Configure both counters for periodic operation
sl_config_timer_config_t ct_config = {
.is_counter_mode_32bit_enabled = SL_COUNTER_16BIT,
.counter0_direction = SL_COUNTER0_UP,
.counter1_direction = SL_COUNTER1_UP,
.is_counter0_periodic_enabled = true,
.is_counter1_periodic_enabled = true,
.is_counter0_sync_trigger_enabled = true,
.is_counter1_sync_trigger_enabled = true
};
status = sl_si91x_config_timer_set_configuration(&ct_config);
if (status != SL_STATUS_OK) return status;
// Configure OCU with DMA support
sl_config_timer_ocu_config_t ocu_config = {
.is_counter0_ocu_output_enabled = true,
.is_counter1_ocu_output_enabled = true,
.is_counter0_ocu_dma_enabled = true,
.is_counter1_ocu_dma_enabled = true,
.is_counter0_toggle_output_high_enabled = true,
.is_counter0_toggle_output_low_enabled = true,
.is_counter1_toggle_output_high_enabled = true,
.is_counter1_toggle_output_low_enabled = true
};
status = sl_si91x_config_timer_set_ocu_configuration(&ocu_config);
if (status != SL_STATUS_OK) return status;
// Set match counts
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_0, match_value);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_1, match_value);
if (status != SL_STATUS_OK) return status;
// Configure DMA for Counter0
status = sl_si91x_config_timer_set_dma_configuration(
breathing_compare_values,
CT_DMA_CHANNEL_0
);
if (status != SL_STATUS_OK) return status;
// Configure DMA for Counter1
status = sl_si91x_config_timer_set_dma_configuration(
breathing_compare_values,
CT_DMA_CHANNEL_8
);
if (status != SL_STATUS_OK) return status;
// Register callback for DMA completion
sl_config_timer_interrupt_flags_t interrupt_flags = {
.is_counter0_hit_peak_interrupt_enabled = true,
.is_counter1_hit_peak_interrupt_enabled = true
};
status = sl_si91x_config_timer_register_callback(on_dma_callback, &interrupt_flags);
if (status != SL_STATUS_OK) return status;
// Start DMA transfers
status = sl_si91x_config_timer_dma_transfer(
breathing_compare_values,
CT_DMA_CHANNEL_0,
&ct_dma_transfer_channel_0
);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_dma_transfer(
breathing_compare_values,
CT_DMA_CHANNEL_8,
&ct_dma_transfer_channel_1
);
if (status != SL_STATUS_OK) return status;
// Start both counters
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_0);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_1);
if (status != SL_STATUS_OK) return status;
return SL_STATUS_OK;
}
// Main loop for DMA management
void main_loop_dma(void) {
while (1) {
// Handle Counter0 DMA completion
if (ct_dma_transfer_flag_channel_0) {
ct_dma_transfer_flag_channel_0 = 0;
ct_dma_transfer_index_channel_0++;
// Requeue DMA transfer for continuous operation
if (ct_dma_transfer_index_channel_0 >= 100) {
ct_dma_transfer_index_channel_0 = 0;
}
sl_si91x_config_timer_dma_transfer(
&breathing_compare_values[ct_dma_transfer_index_channel_0],
CT_DMA_CHANNEL_0,
&ct_dma_transfer_channel_0
);
}
// Handle Counter1 DMA completion
if (ct_dma_transfer_flag_channel_1) {
ct_dma_transfer_flag_channel_1 = 0;
ct_dma_transfer_index_channel_1++;
// Requeue DMA transfer for continuous operation
if (ct_dma_transfer_index_channel_1 >= 100) {
ct_dma_transfer_index_channel_1 = 0;
}
sl_si91x_config_timer_dma_transfer(
&breathing_compare_values[ct_dma_transfer_index_channel_1],
CT_DMA_CHANNEL_8,
&ct_dma_transfer_channel_1
);
}
// Other application code
}
}Example 4: Input Capture (Input Capture Unit) – Event Measurement#
The Input Capture Unit (ICU) mode allows the Configurable Timer (CT) to measure the timing characteristics of external signals, such as pulse width, period, or frequency.
This mode is ideal for applications like sensor input measurement, communication signal timing, and frequency analysis.
Use Case#
Measure the pulse width of an external signal connected to IN0, using automatic capture and interrupt-driven event notifications.
Implementation Steps#
Configure capture triggers: Enable software or synchronization triggers to detect signal edges.
Select event types: Define the start and capture events (for exmaple, rising or falling edges).
Enable interrupts: Turn on capture event interrupts for real-time notification.
Process results: Retrieve captured values in the interrupt callback and compute timing (for exmaple, pulse width or frequency).
Example Code#
#include "sl_si91x_config_timer.h"
// Global variables for capture data
volatile uint16_t captured_value = 0;
volatile bool capture_ready = false;
// Capture callback function
void on_capture_callback(sl_config_timer_interrupt_flags_t flags) {
if (flags.is_counter0_event_interrupt_enabled) {
capture_ready = true;
}
}
// Input capture setup function
sl_status_t setup_input_capture(void) {
sl_status_t status;
// Initialize CT peripheral
sl_si91x_config_timer_init();
// Configure Counter0 for input capture
sl_config_timer_config_t ct_config = {
.is_counter_mode_32bit_enabled = SL_COUNTER_16BIT,
.counter0_direction = SL_COUNTER0_UP,
.is_counter0_periodic_enabled = false, // One-shot mode for capture
.is_counter0_trigger_enabled = true, // Enable software trigger
.is_counter0_sync_trigger_enabled = false
};
status = sl_si91x_config_timer_set_configuration(&ct_config);
if (status != SL_STATUS_OK) return status;
// Configure action events for capture
sl_config_action_event_t action_event = {
.action = CAPTURE,
.and_event_counter0 = SL_EVENT0_RISING_EDGE_AND_EVENT,
.or_event_counter0 = SL_NO_EVENT,
.and_event_valid_bits_counter0 = 16,
.or_event_valid_bits_counter0 = 0
};
status = sl_si91x_config_timer_configure_action_event(SL_COUNTER_0, &action_event);
if (status != SL_STATUS_OK) return status;
// Configure interrupt flags for capture events
sl_config_timer_interrupt_flags_t interrupt_flags = {
.is_counter0_event_interrupt_enabled = true
};
// Register callback
status = sl_si91x_config_timer_register_callback(on_capture_callback, &interrupt_flags);
if (status != SL_STATUS_OK) return status;
// Start the counter
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_0);
if (status != SL_STATUS_OK) return status;
return SL_STATUS_OK;
}
// Function to read captured value and calculate pulse width
uint32_t read_captured_pulse_width(void) {
uint16_t capture_value;
sl_status_t status;
if (!capture_ready) {
return 0; // No capture available
}
// Read the captured value
status = sl_si91x_config_timer_read_capture(SL_COUNTER_0, &capture_value);
if (status != SL_STATUS_OK) {
return 0;
}
// Convert capture value to microseconds
// Assuming 32MHz clock: 1 tick = 1/32μs
uint32_t pulse_width_us = (uint32_t)capture_value * 1000 / 32;
capture_ready = false;
return pulse_width_us;
}
// Main loop for capture processing
void main_loop_capture(void) {
while (1) {
if (capture_ready) {
uint32_t pulse_width = read_captured_pulse_width();
if (pulse_width > 0) {
printf("Pulse width: %lu μs\n", pulse_width);
}
}
// Other application code
}
}Advanced Usage Patterns#
You can extend the CT functionality in SiWx917 devices to support complex timing requirements.
The following patterns demonstrate advanced configurations for specialized applications.
Cascading Timers – Extended Period Operation#
Use cascading timers when your application requires timing periods longer than what a single 16-bit counter can provide.
Use Case:
Generate long-duration timing intervals (for example, minutes or hours) for applications such as power management cycles, sensor sampling, or periodic reporting.
Implementation Approach:
Configure Counter0 as a periodic timer that overflows after a fixed interval.
Increment a software counter in the Counter0 interrupt service routine.
Trigger an action or event when the software counter reaches the desired threshold.
Tip: Cascading counters effectively extend timing beyond hardware limits, offering 32-bit or higher equivalent timing resolution.
Adaptive Timing – Dynamic Period Adjustment#
Adaptive timing allows you to change timer parameters in real time based on external conditions or system state.
Use Case:
Adjust PWM frequency or timer intervals dynamically — for example, modifying motor speed, sensor polling rate, or communication timing based on temperature or load.
Implementation Approach:
Continuously monitor external or internal conditions (for example, sensor readings, load feedback).
Recalculate timing parameters such as match or compare values based on new inputs.
Update CT configurations dynamically using
sl_si91x_config_timer_set_configuration().For smooth transitions without glitches, use DMA to automate parameter updates.
Tip: Adaptive timing is ideal for feedback-based systems that require real-time response with minimal CPU overhead.
Synchronized Timers – Coordinated Operation#
Use synchronized timers when multiple CT counters must operate with precise timing alignment or phase relationships.
Use Case:
Generate multi-phase PWM signals for motor control, coordinate sensor triggers, or align outputs across multiple peripherals.
Implementation Approach:
Configure two or more counters with identical base parameters (clock source, frequency, mode).
Use synchronization triggers to start or reset all counters simultaneously.
Maintain the desired phase relationship by adjusting match values or start offsets.
Verify synchronization accuracy using an oscilloscope or logic analyzer.
Tip: Synchronization ensures deterministic multi-channel timing — essential for applications such as brushless motor control or coordinated actuator operation.
Complete Real-World Example: Smart Motor Control System#
This example demonstrates how to combine multiple CT capabilities of the SiWx917 device to build a smart motor control system.
It integrates PWM generation, input capture for encoder feedback, and DMA-assisted duty cycle adjustments to achieve smooth, efficient, and responsive motor control.
System Requirements#
Generate a 20 kHz PWM output signal for motor speed control.
Capture encoder feedback pulses using the Input Capture Unit (ICU).
Implement smooth speed ramping via DMA-driven duty cycle transitions.
Provide a fast response to emergency stop conditions.
Example Code#
#include "sl_si91x_config_timer.h"
#include "sl_si91x_dma.h"
// Motor control state
typedef struct {
uint16_t current_speed; // Current speed (0-100%)
uint16_t target_speed; // Target speed (0-100%)
uint16_t ramp_rate; // Speed change rate
bool emergency_stop; // Emergency stop flag
uint32_t encoder_count; // Encoder pulse count
} motor_control_t;
motor_control_t motor_state = {0};
// Speed ramp compare values (0% to 100% duty cycle)
uint16_t speed_ramp_values[101];
// Initialize speed ramp values
void init_speed_ramp_values(uint16_t match_value) {
for (int i = 0; i <= 100; i++) {
speed_ramp_values[i] = (uint16_t)(match_value * i / 100);
}
}
// Motor control callback
void on_motor_control_callback(sl_config_timer_interrupt_flags_t flags) {
if (flags.is_counter0_hit_peak_interrupt_enabled) {
// PWM period complete - update speed if needed
if (motor_state.current_speed != motor_state.target_speed) {
if (motor_state.current_speed < motor_state.target_speed) {
motor_state.current_speed += motor_state.ramp_rate;
if (motor_state.current_speed > motor_state.target_speed) {
motor_state.current_speed = motor_state.target_speed;
}
} else {
motor_state.current_speed -= motor_state.ramp_rate;
if (motor_state.current_speed < motor_state.target_speed) {
motor_state.current_speed = motor_state.target_speed;
}
}
// Update PWM duty cycle via DMA
sl_si91x_config_timer_dma_transfer(
&speed_ramp_values[motor_state.current_speed],
CT_DMA_CHANNEL_0,
&ct_dma_transfer_channel_0
);
}
}
if (flags.is_counter1_event_interrupt_enabled) {
// Encoder pulse captured
motor_state.encoder_count++;
}
}
// Setup motor control system
sl_status_t setup_motor_control(void) {
sl_status_t status;
uint16_t match_value;
// Initialize CT peripheral
sl_si91x_config_timer_init();
// Calculate match value for 20kHz PWM (50μs period)
status = sl_si91x_config_timer_get_match_value(50, &match_value);
if (status != SL_STATUS_OK) return status;
// Initialize speed ramp values
init_speed_ramp_values(match_value);
// Configure Counter0 for PWM generation
sl_config_timer_config_t ct_config = {
.is_counter_mode_32bit_enabled = SL_COUNTER_16BIT,
.counter0_direction = SL_COUNTER0_UP,
.counter1_direction = SL_COUNTER1_UP,
.is_counter0_periodic_enabled = true,
.is_counter1_periodic_enabled = false,
.is_counter0_sync_trigger_enabled = true,
.is_counter1_trigger_enabled = true
};
status = sl_si91x_config_timer_set_configuration(&ct_config);
if (status != SL_STATUS_OK) return status;
// Configure OCU for PWM output
sl_config_timer_ocu_config_t ocu_config = {
.is_counter0_ocu_output_enabled = true,
.is_counter0_ocu_dma_enabled = true,
.is_counter0_toggle_output_high_enabled = true,
.is_counter0_toggle_output_low_enabled = true
};
status = sl_si91x_config_timer_set_ocu_configuration(&ocu_config);
if (status != SL_STATUS_OK) return status;
// Set match counts
status = sl_si91x_config_timer_set_match_count(SL_COUNTER_0, match_value);
if (status != SL_STATUS_OK) return status;
// Configure DMA for Counter0
status = sl_si91x_config_timer_set_dma_configuration(
speed_ramp_values,
CT_DMA_CHANNEL_0
);
if (status != SL_STATUS_OK) return status;
// Configure action events for Counter1 (encoder capture)
sl_config_action_event_t action_event = {
.action = CAPTURE,
.and_event_counter1 = SL_EVENT0_RISING_EDGE_AND_EVENT,
.or_event_counter1 = SL_NO_EVENT,
.and_event_valid_bits_counter1 = 16,
.or_event_valid_bits_counter1 = 0
};
status = sl_si91x_config_timer_configure_action_event(SL_COUNTER_1, &action_event);
if (status != SL_STATUS_OK) return status;
// Configure interrupt flags
sl_config_timer_interrupt_flags_t interrupt_flags = {
.is_counter0_hit_peak_interrupt_enabled = true,
.is_counter1_event_interrupt_enabled = true
};
// Register callback
status = sl_si91x_config_timer_register_callback(on_motor_control_callback, &interrupt_flags);
if (status != SL_STATUS_OK) return status;
// Start DMA transfer for initial speed
status = sl_si91x_config_timer_dma_transfer(
&speed_ramp_values[0],
CT_DMA_CHANNEL_0,
&ct_dma_transfer_channel_0
);
if (status != SL_STATUS_OK) return status;
// Start both counters
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_0);
if (status != SL_STATUS_OK) return status;
status = sl_si91x_config_timer_start_on_software_trigger(SL_COUNTER_1);
if (status != SL_STATUS_OK) return status;
return SL_STATUS_OK;
}
// Motor control functions
void set_motor_speed(uint16_t speed_percent) {
if (speed_percent > 100) speed_percent = 100;
motor_state.target_speed = speed_percent;
}
void emergency_stop(void) {
motor_state.emergency_stop = true;
motor_state.target_speed = 0;
motor_state.ramp_rate = 10; // Fast ramp down
}
void resume_normal_operation(void) {
motor_state.emergency_stop = false;
motor_state.ramp_rate = 1; // Normal ramp rate
}
// Main control loop
void motor_control_loop(void) {
while (1) {
// Check for emergency stop
if (motor_state.emergency_stop) {
// Handle emergency stop logic
if (motor_state.current_speed == 0) {
motor_state.emergency_stop = false;
}
}
// Update motor state based on encoder feedback
if (motor_state.encoder_count > 0) {
// Process encoder data
motor_state.encoder_count = 0;
}
// Other motor control logic
}
}Common Mistakes to Avoid#
When working with the CT on the SiWx917, a few common oversights can cause unexpected behavior. Use the following checklist to help prevent configuration and runtime issues.
Avoid these Pitfalls#
Ignoring API return values: Always verify function call results to detect configuration errors early.
Using mismatched parameters: Ensure timer mode, counter width, and clock settings are consistent across initialization and usage.
Adding complex logic to ISRs: Keep interrupt service routines short; handle heavy processing in the main loop.
Incorrect DMA buffer handling: Maintain buffer alignment and size, and avoid modifying buffers during active DMA transfers.
Assuming wrong clock frequencies: Confirm the selected clock source and divider values match your intended timing precision.
Tip: Logging initialization and runtime results can help identify misconfigurations before testing on hardware.
Testing Your Timer Setup#
Thorough testing verifies correct configuration and reliable timing performance.
Use the following procedures to validate each functional area of your timer setup.
Basic Functionality Test#
Verify that timer interrupts occur at expected intervals.
Check that the counter direction (up, down, or up–down) matches your configuration.
Confirm match value calculations align with your intended timing period.
Observe timer behavior during start, stop, and reset operations.
PWM Output Test#
Use an oscilloscope or logic analyzer to verify output frequency and duty cycle.
Confirm pin assignments and ensure output signals appear on the correct GPIO.
Check toggle and polarity behavior to confirm correct waveform shape.
If DMA is used, verify continuous updates without visible jitter.
Input Capture Test#
Apply known test signals (for example, square wave or pulse train) and verify captured timing values.
Test edge detection sensitivity (rising, falling, or both).
Confirm that capture interrupts are generated and serviced correctly.
Compare captured data against actual signal measurements for accuracy.
DMA Operation Test#
Verify smooth transitions between compare values during PWM or timed updates.
Check proper DMA completion handling and ensure requeueing logic works as intended.
Monitor for buffer overruns or underruns, especially at high data rates.
Ensure DMA triggers are correctly linked to the CT peripheral.
Note: Use Simplicity Studio’s Energy Profiler or external test tools to analyze timing jitter and CPU load when using DMA.
Example Projects#
You can explore the following reference examples in the WiSeConnect SDK to learn best practices and review working configurations:
sl_si91x_ct_ocu_non_dma
Demonstrates Output Compare Unit (OCU) operation without DMA.sl_si91x_ct_icu_ocu_with_dma
Shows combined Input Capture Unit (ICU) and OCU functionality with DMA for smooth signal updates.