SPWM - Software PWM for Embedded Systems
A no_std Rust library for generating software-based Pulse Width Modulation (PWM) signals on microcontrollers and
embedded systems. This crate provides a flexible, interrupt-driven PWM implementation that doesn't require dedicated
hardware PWM peripherals.
Features
no_stdcompatible - Works in embedded environments without the standard library- Multiple independent channels - Configure up to N channels (compile-time constant)
- Thread-safe - Uses atomic operations for safe access from interrupt contexts
- Type-safe builder pattern - Compile-time guarantees for proper channel configuration
- Flexible callbacks - Register callbacks for state changes and period completion
- Dynamic updates - Change frequency and duty cycle at runtime
Basic Usage
Add this to your Cargo.toml:
[]
= "0.1"
Creating a Simple PWM Channel
use ;
// Create SPWM manager with hardware timer frequency of 100 kHz
// and space for 4 channels
let mut spwm = new;
// Create a channel with 1 kHz frequency and 50% duty cycle
let channel = spwm
.create_channel
.freq_hz
.duty_cycle
.on_off_callback
.period_callback
.build?;
let channel_id = spwm.register_channel?;
// Enable the channel to start PWM generation
spwm.get_channel.unwrap.enable?;
In Your Timer Interrupt Handler
Requirements
- Hardware timer that can interrupt at a consistent frequency
- Timer frequency must be at least 100x the desired PWM channel frequency to achieve 1% duty cycle resolution capabilities
- Callbacks must be short and non-blocking (they run in interrupt context)
Example
- STM32 Nucleo-F302R8 board example: 4-channel software PWM output
That example configures 4 channels:
- PC9: 10 Hz, 50% duty cycle
- PC8: 50 Hz, 10% duty cycle
- PC6: 500 Hz, 50% duty cycle
- PC5: 250 Hz, 64% duty cycle
Oscillogram that shows PC9 and PC8 output waveforms:

Simple LED PWM control example:
use ;
static mut LED_STATE: bool = false;
let mut pwm = new;
let channel = pwm
.create_channel
.freq_hz // 100 Hz PWM frequency
.duty_cycle // 25% brightness
.on_off_callback
.period_callback
.build?;
let id = pwm.register_channel?;
pwm.get_channel.unwrap.enable?;