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 и делителя для целевой частоты

В 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);
}