PWM
micro/pwm provides pulse-width modulation for motor control, LED dimming, and tone generation.
Basic Usage
use micro/pwm
use micro/gpio
use std/time
fn main() -> never {
let mut ch = pwm.channel(0)
.pin(gpio.pin(25))
.init()?
// Fade LED up then down
loop {
for duty in 0..=100 {
ch.set_duty_percent(duty)
time.sleep_ms(10)
}
for i in 0..=100 {
ch.set_duty_percent(100 - i)
time.sleep_ms(10)
}
}
}
Initialization
let mut ch = pwm.channel(0)
.pin(gpio.pin(25)) // output pin
.freq(1000) // frequency in Hz (optional, target default if omitted)
.init()?
The terminal init() is fallible. After initialization, duty-cycle updates are infallible.
Setting Duty Cycle
ch.set_duty_percent(50) // 50% duty (0..=100)
ch.set_duty(2048) // raw 16-bit duty value
ch.off() // 0% duty (output low)
ch.full() // 100% duty (output high)
The default full-scale is a 16-bit duty range (0..=65535), matching the standard RP2040 PWM wrap value. set_duty_percent maps 0-100 to that range.
LED Fade Example
From examples/rp2040/fade_led:
use micro/pwm
use micro/gpio
use std/time
const LED_PIN: u32 = 25
fn main() -> never {
let mut led = pwm.channel(0)
.pin(gpio.pin(LED_PIN))
.init()
let mut rising = true
let mut level: u16 = 0
const BRIGHT_STEP: u16 = 512
const BRIGHT_MAX: u16 = 65535
const BRIGHT_TURN: u16 = 65535
loop {
match rising {
true when level >= BRIGHT_TURN => {
level = BRIGHT_MAX
rising = false
}
true => level += BRIGHT_STEP
false when level <= BRIGHT_STEP => {
level = 0
rising = true
}
_ => level -= BRIGHT_STEP
}
led.set_duty(level)
time.sleep_ms(10)
}
}
Do and Don’t
// Do: use set_duty_percent for simple 0-100% control
ch.set_duty_percent(75)
// Do: use off() and full() for clean extremes
ch.off()
ch.full()
// Avoid: computing raw duty values with magic numbers
// ch.set_duty(49151) // what is this?
// Prefer:
ch.set_duty_percent(75)
// Avoid: reinitializing PWM in a loop; configure once at startup