Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

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 the RP2350 system clock frequency of 150 MHz, and the target frequency is set to 50 Hz. You can change both values if needed.

TOP and divider calculation

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 = 45 and DIV_FRAC = 13 means the divider is 45 + 0.8125, not 45.13.

Code

If you are using rp-hal, you set the integer and fractional parts separately, like this:

#![allow(unused)]
fn main() {
pwm.set_top(65483);
pwm.set_div_int(45);
pwm.set_div_frac(13);
}

If you are using embassy-rp, both parts are combined into a single divider field inside the Config struct. Nope, this is not a floating-point value. Internally, it uses a fixed-point number to represent the integer and fractional parts together. 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 = 46_874;
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 = 65483;
servo_config.divider = FixedU16::<U4>::from_num(45.8125);
// or
// servo_config.divider = fixed::types::U12F4::from_num(45.8125);
}