TOP and Divider Finder for the Target Frequency
In MicroPython, you can set the PWM frequency directly without manually calculating the TOP and divider values. Internally, MicroPython computes these values from the target frequency and the system clock by searching for a valid TOP and divider combination.
In embassy-rp, this logic is not provided automatically. You must compute the TOP and divider values yourself and supply them explicitly in the PWM configuration.
I wanted to see if there was something similar available in Rust. While discussing this in the rp-rs Matrix chat, 9names ported the relevant C code from MicroPython that calculates TOP and divider values into Rust. This code takes the target frequency and source clock frequency as input and gives us the corresponding TOP and divider values. You can find that implementation here.
You can use that Rust code directly in your own project. I compiled the same code to WASM and built a small form around it so that you can try it out here.
By default, the source clock frequency is set to 125 MHz, which is the typical clk_sys configuration on Raspberry Pi Pico boards, and the target frequency is set to 50 Hz. If your application reconfigures clk_sys, you must use the updated clock value here. You can change both values if needed.
TOP and divider calculation
Result
Note
The divider is shown as an integer part and a fractional part.
The fractional value is not a decimal fraction. It represents a 4-bit fixed-point fraction.
The effective divider is:
DIV = DIV_INT + (DIV_FRAC / 16)For example,
DIV_INT = 45andDIV_FRAC = 13means the divider is45 + 0.8125, not45.13.
Code
In embassy-rp, the integer and fractional divider parts are combined into a single fixed-point value inside the Config struct. This is not a floating-point number. Internally, embassy-rp uses the same U12F4 fixed-point format as the RP2040 PWM hardware. If you are not familiar with fixed-point numbers, I have a separate blog post explaining them in detail, which you can read here:
If you only need an integer divider, you can simply convert a u8 value:
#![allow(unused)]
fn main() {
let mut servo_config: PwmConfig = Default::default();
servo_config.top = 39_061;
servo_config.divider = 64.into();
}
If you also want a fractional part, you need to add the “fixed” crate as a dependency and construct the divider using a fixed-point type:
#![allow(unused)]
fn main() {
let mut servo_config: PwmConfig = Default::default();
servo_config.top = 65465;
servo_config.divider = FixedU16::<U4>::from_num(38.1875);
// or
// servo_config.divider = fixed::types::U12F4::from_num(38.1875);
}