Калькулятор TOP и делителя для целевой частоты
В MicroPython можно задать частоту ШИМ напрямую, без ручного расчёта значений TOP и делителя. Внутри MicroPython вычисляет эти значения по целевой частоте и системной частоте тактирования.
Я хотел проверить, есть ли что-то подобное в Rust. Обсуждая это в Matrix-чате rp-rs, 9names перенёс на Rust соответствующий код на C из MicroPython, который вычисляет значения TOP и делителя. Этот код принимает на вход целевую частоту и частоту источника тактирования и возвращает соответствующие значения TOP и делителя. Реализацию можно найти здесь.
Этот Rust-код можно использовать напрямую в своём проекте. Я скомпилировал тот же код в WASM и собрал вокруг него небольшую форму, чтобы вы могли попробовать его здесь.
По умолчанию частота источника тактирования равна системной частоте RP2350 — 150 МГц, а целевая частота — 50 Гц. При необходимости можно изменить оба значения.
Расчёт TOP и делителя
Результат
Note
Делитель показан в виде целой и дробной частей.
Дробная часть — это не десятичная дробь. Она представляет собой дробь с фиксированной точкой из 4 бит.
Эффективный делитель вычисляется так:
DIV = DIV_INT + (DIV_FRAC / 16)Например,
DIV_INT = 45иDIV_FRAC = 13означают, что делитель равен45 + 0.8125, а не45.13.
Код
Если вы используете rp-hal, целую и дробную части задают отдельно:
#![allow(unused)]
fn main() {
pwm.set_top(65483);
pwm.set_div_int(45);
pwm.set_div_frac(13);
}
Если вы используете embassy-rp, обе части объединяются в одном поле делителя внутри структуры Config. Это не значение с плавающей запятой: внутри используется число с фиксированной точкой, которое хранит целую и дробную части вместе. Если вы не знакомы с числами с фиксированной точкой, я написал отдельную статью, где они подробно разобраны, — её можно прочитать здесь.
Если нужен только целый делитель, можно просто передать значение типа u8:
#![allow(unused)]
fn main() {
let mut servo_config: PwmConfig = Default::default();
servo_config.top = 46_874;
servo_config.divider = 64.into();
}
Если нужна и дробная часть, необходимо добавить крейт fixed в зависимости и задать делитель с помощью типа с фиксированной точкой:
#![allow(unused)]
fn main() {
let mut servo_config: PwmConfig = Default::default();
servo_config.top = 65483;
servo_config.divider = FixedU16::<U4>::from_num(45.8125);
// или
// servo_config.divider = fixed::types::U12F4::from_num(45.8125);
}