The SPWM block is a highly flexible Timer/Counter/PWM subsystem designed to support a wide range of timing, waveform generation, and event-capture use cases. Each SPWM instance contains multiple independent groups, where each group can be configured to operate in Timer, Counter, Capture, PWM, PWM with Dead-Time, PWM with Pseudo-Random modulation, Shift Register, or Quadrature Decoder mode.
The driver provides a unified configuration interface while also exposing low-level hardware features such as trigger routing, interrupt control, semaphores, and descriptor-based command sequencing. This allows both straightforward software-driven operation and advanced hardware-automated control flows with minimal CPU intervention.
Features:
- Timer and counter functionality with configurable count direction
- Capture mode for precise measurement of external signal timing
- PWM waveform generation with dual compare outputs
- Dead-time insertion for complementary PWM outputs
- Pseudo-random PWM generation using LFSR-based modulation
- Shift Register mode for serial or bitstream output generation
- Quadrature decoder support for rotary encoder feedback
- Descriptor engine for autonomous configuration and execution sequencing
- Flexible trigger input and trigger output routing
- Interrupt generation for terminal count and compare events
- Multiple SPWM instances with independent, parallel group operation
- Hardware semaphore support for cross-group synchronization
- Note
- SPWM APIs are divided into two categories:
- High-level APIs: Uses a unified configuration structure and group-level control APIs to configure and operate SPWM groups in a safe and consistent manner. This model is intended for most application-level use cases.
- Low-level APIs: Provides direct access to triggers, interrupts, semaphores, and descriptor execution for advanced timing control and hardware automation. This model is intended for system-level or performance-critical applications.
- Warning
- SPWM groups operate as tightly coupled hardware state machines. Changing operating mode, clocking, or output configuration while a group is enabled or while a descriptor chain is executing may result in unpredictable output behavior. Software should always disable the affected group using spwm_disable and ensure descriptor execution is complete before reconfiguration. Avoid mixing software-driven register updates with trigger-based or descriptor-based control on the same group.
Configuration Considerations
To initialize and operate the SPWM driver:
- Call spwm_init to initialize the SPWM instance and internal driver state.
- Configure the required clock divider using spwm_clkdiv_config.
- Enable the clock divider using spwm_clkdiv_enable.
- Populate a spwm_config_t structure according to the selected operating mode and application requirements.
- Apply the configuration to a specific SPWM group using spwm_configure.
- Optionally configure trigger inputs using spwm_set_trig_in or spwm_set_trig_in_all.
- Optionally configure trigger outputs using spwm_set_trig_out and semaphore routing using spwm_set_sema_sel.
- Enable required interrupts using spwm_enable_interrupt or register callbacks using spwm_register_callback.
- Enable the SPWM group using spwm_enable to start operation.
- Control execution using software triggers (spwm_trigger), hardware triggers, or descriptor-based execution.
- Disable the group using spwm_disable before modifying configuration or changing operating mode.
For advanced use cases, descriptor chains can be created and executed using the ::spwm_dsc_chain_* APIs to automate configuration updates, synchronize multiple SPWM groups, and minimize CPU involvement.
spwm_init
Initializes the SPWM hardware block and internal driver state for the selected SPWM instance. This function must be called once before configuring clocks, groups, interrupts, or descriptors.
LOG_ERROR(LOG_MOD_SPWM, "SPWM init failed.\n");
}
spwm_deinit
Deinitializes the SPWM instance and releases all associated hardware resources. All SPWM groups are disabled and the block is returned to a reset-safe state.
LOG_ERROR(LOG_MOD_SPWM, "SPWM deinit failed.\n");
}
spwm_configure_clock
Configures and enables a clock divider for SPWM operation. The divider output can be shared across multiple SPWM groups and determines the base timing resolution for timer, capture, and PWM modes.
LOG_ERROR(LOG_MOD_SPWM, "Clock divider disable failed.\n");
}
LOG_ERROR(LOG_MOD_SPWM, "Clock divider config failed.\n");
}
LOG_ERROR(LOG_MOD_SPWM, "Clock divider enable failed.\n");
}
spwm_set_clkdiv
Selects the clock divider source for a specific SPWM group. Each group can independently select a divider based on its required timing or PWM frequency.
LOG_ERROR(LOG_MOD_SPWM, "Clock divider select failed.\n");
}
spwm_configure
Applies a unified configuration to an SPWM group using spwm_config_t.
This API supports all SPWM operating modes, including:
- Timer and counter modes
- Capture modes
- PWM and PWM with dead-time
- Pseudo-random PWM
The configuration programs counter behavior, period and compare values, trigger routing, output control, and all mode-specific parameters in a single atomic operation.
memset(&cfg, 0, sizeof(cfg));
cfg.clkdiv_sel = 0;
cfg.one_shot = false;
cfg.period = 1000;
cfg.period_buff = 1000;
cfg.cc0 = 500;
cfg.cc0_buff = 500;
cfg.cfg.timer.auto_reload_cc0 = true;
LOG_ERROR(LOG_MOD_SPWM, "SPWM configure failed.\n");
}
spwm_configure_interrupts
Configures interrupt handling for an SPWM group. This includes registering an instance-level ISR callback, clearing pending interrupt state, and enabling selected interrupt sources such as terminal count, compare match, or capture events.
LOG_ERROR(LOG_MOD_SPWM, "Callback register failed.\n");
}
LOG_ERROR(LOG_MOD_SPWM, "Interrupt enable failed.\n");
}
spwm_enable
Enables an SPWM group and starts hardware operation based on the configured mode. Depending on configuration, this may start a timer, PWM generator, capture engine, or descriptor-controlled sequence.
LOG_ERROR(LOG_MOD_SPWM, "SPWM enable failed.\n");
}
spwm_disable
Disables an SPWM group and halts all activity. Output signals transition to the configured disable state and counters are stopped.
LOG_ERROR(LOG_MOD_SPWM, "SPWM disable failed.\n");
}
spwm_descriptor_chaining
Supports descriptor-based command chaining for autonomous SPWM operation.
The SPWM descriptor engine enables hardware-driven sequencing of configuration updates and synchronization events without CPU intervention. Descriptor chains are commonly used for PWM ramping, burst generation, synchronized updates, and deterministic timing control across one or more SPWM groups.
A typical descriptor workflow includes:
- Allocating descriptor SRAM
- Creating and initializing a descriptor chain
- Appending configuration (CFG) descriptors
- Starting execution on a descriptor instance
- Cleaning up descriptor resources after completion
uint16_t dmem_handle;
if (pwm_dsc_mem_alloc(2048, &dmem_handle) != PWM_DSC_OK) {
LOG_ERROR(LOG_MOD_SPWM, "Descriptor memory allocation failed.\n");
}
pwm_dsc_chain_t *chain = pwm_dsc_chain_create();
if (!chain) {
LOG_ERROR(LOG_MOD_SPWM, "Descriptor chain create failed.\n");
}
if (pwm_dsc_chain_init_for_handle(SPWM_INST, chain, dmem_handle) != PWM_DSC_OK) {
LOG_ERROR(LOG_MOD_SPWM, "Descriptor chain init failed.\n");
}
uint32_t cfg_vals[2] = {
500,
1000
};
if (pwm_dsc_chain_add_cfg(chain,
SPWM_GRP,
PWM_DSC_REG_CC0_BUFF,
cfg_vals,
2,
false) != PWM_DSC_OK) {
LOG_ERROR(LOG_MOD_SPWM, "Descriptor CFG add failed.\n");
}
if (pwm_dsc_chain_start(SPWM_INST, chain, 0, true) != PWM_DSC_OK) {
LOG_ERROR(LOG_MOD_SPWM, "Descriptor chain start failed.\n");
}
pwm_dsc_chain_destroy(chain);
pwm_dsc_mem_free(dmem_handle);