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

Сигнал зуммера с помощью ШИМ на Raspberry Pi Pico на embedded Rust

В этом упражнении мы сгенерируем звуковой сигнал с помощью зуммера. Идея похожа на мигание светодиодом, но вместо того, чтобы переключать вывод GPIO между HIGH и LOW, мы управляем скважностью ШИМ.

Будем многократно переключать скважность ШИМ между 50 процентами и 0 процентами с задержкой между переключениями. Когда скважность составляет 50 процентов, зуммер издаёт звук. Когда 0 процентов — звук прекращается. Повторение этого создаёт отчётливый сигнал.

Можно попробовать и без изменения частоты ШИМ. В этом примере мы задаём частоту ШИМ 440,0 Гц. Это соответствует музыкальной ноте a4 (ля первой октавы) и тональному сигналу телефонии (call progress tone), который используется в Северной Америке и большей части Европы. Знать что-либо о нотах для этого не нужно. Важно лишь то, что мы генерируем тон фиксированной частоты и включаем или выключаем звук, меняя скважность.

Создание проекта из шаблона

Начнём с создания нового проекта на основе фреймворка Embassy. Как обычно, создайте проект из шаблона с помощью cargo-generate:

cargo generate --git https://github.com/ImplFerris/pico2-template.git --tag v0.3.2

Когда появится запрос, задайте проекту имя, например «buzzer-beep», и выберите «embassy» как HAL. Включите логирование defmt, если у вас есть отладочный зонд, чтобы также видеть логи.

Дополнительные импорты

Как и раньше, импортируем трейт SetDutyCycle, а также типы Pwm и Config для настройки ШИМ.

#![allow(unused)]
fn main() {
use embassy_rp::pwm::{Config as PwmConfig, Pwm, SetDutyCycle};
}

Вычисление TOP

Значение TOP можно вычислить вручную или воспользоваться калькулятором из предыдущей главы. На этот раз пойдём другим путём.

Зафиксируем делитель на 64 и используем const fn, чтобы вычислить значение TOP. Поскольку мы не используем режим phase-correct и дробный делитель, расчёт простой.

#![allow(unused)]
fn main() {
const fn get_top(freq: f64, div_int: u8) -> u16 {
    assert!(div_int != 0, "Divider must not be 0");

    let result = 150_000_000. / (freq * div_int as f64);

    assert!(result >= 1.0, "Frequency too high");
    assert!(
        result <= 65535.0,
        "Frequency too low: TOP exceeds 65534 max"
    );

    result as u16 - 1
}

const PWM_DIV_INT: u8 = 64;
const PWM_TOP: u16 = get_top(440., PWM_DIV_INT);
}

Основная логика

Сначала настраиваем ШИМ с вычисленным значением TOP и фиксированным делителем. Затем создаём выход ШИМ для зуммера. Внутри цикла переключаем скважность между 50 процентами и 0 процентами с задержкой между переключениями, что даёт повторяющийся звуковой сигнал.

#![allow(unused)]
fn main() {
let mut pwm_config = PwmConfig::default();
pwm_config.top = PWM_TOP;
pwm_config.divider = PWM_DIV_INT.into();

let mut buzzer = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, pwm_config);

loop {
    buzzer
        .set_duty_cycle_percent(50)
        .expect("50 is valid duty percentage");
    Timer::after_millis(1000).await;

    buzzer
        .set_duty_cycle_percent(0)
        .expect("0 is valid duty percentage");
    Timer::after_millis(1000).await;
}
}

Если хотите, чтобы сигнал был короче или быстрее, измените значения задержек.

Клонируйте существующий проект

Можно клонировать (или посмотреть) проект, который я создал, и перейти в папку buzzer-beep.

git clone https://github.com/ImplFerris/pico2-embassy-projects
cd pico2-embassy-projects/buzzer-beep

Версия на rp-hal

Если хотите посмотреть тот же пример, реализованный с помощью rp-hal, его можно найти здесь.

git clone https://github.com/ImplFerris/pico2-rp-projects
cd pico2-rp-projects/buzzer-beep