You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C/Rust实现树莓派Pico蜂鸣器的音符播放功能?

树莓派Pico蜂鸣器音符播放实现(C/Rust版本)

C语言实现(基于Raspberry Pi Pico SDK)

前置准备

  • 已安装Pico SDK并配置好开发环境
  • 蜂鸣器连接至GPIO15,板载LED对应GPIO25

代码实现

#include "pico/stdlib.h"
#include "hardware/pwm.h"

// 音符-频率映射表
typedef struct {
    const char* note;
    uint32_t freq;
} Tone;

Tone tones[] = {
    {"C5", 523},
    {"CS5", 554},
    {"D5", 587},
    {"DS5", 622},
    {"E5", 659},
    {"F5", 698},
    {"FS5", 740},
    {"G5", 784},
    {"GS5", 831},
    {"A5", 880},
    {"AS5", 932},
    {"B5", 988},
    {NULL, 0} // 结束标记
};

// 歌曲音符序列
const char* song[] = {"E5", "G5", "A5", "P", "E5", "G5", "B5", "A5", "P", "E5", "G5", "A5", "P", "G5", "E5", NULL};

// GPIO定义
#define BUZZER_PIN 15
#define LED_PIN 25

// 根据音符名称获取对应频率
uint32_t get_freq(const char* note) {
    for (int i = 0; tones[i].note != NULL; i++) {
        if (strcmp(tones[i].note, note) == 0) {
            return tones[i].freq;
        }
    }
    return 0;
}

// 播放指定频率的音符
void playtone(uint32_t frequency) {
    pwm_set_gpio_level(BUZZER_PIN, 1000); // 设置占空比,对应Python的duty_u16(1000)
    // 计算PWM周期(系统时钟125MHz)
    pwm_set_wrap(pwm_gpio_to_slice_num(BUZZER_PIN), 125000000 / frequency - 1);
    gpio_put(LED_PIN, !gpio_get(LED_PIN)); // 切换LED状态
}

// 停止蜂鸣器发声
void bequiet() {
    pwm_set_gpio_level(BUZZER_PIN, 0);
}

// 播放完整歌曲
void playsong(const char** mysong) {
    for (int i = 0; mysong[i] != NULL; i++) {
        if (strcmp(mysong[i], "P") == 0) {
            bequiet();
        } else {
            uint32_t freq = get_freq(mysong[i]);
            freq != 0 ? playtone(freq) : bequiet();
        }
        sleep_ms(300); // 每个音符/停顿持续0.3秒
    }
    bequiet();
}

int main() {
    // 初始化LED GPIO
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    
    // 初始化蜂鸣器PWM
    gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
    uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
    pwm_set_clkdiv(slice_num, 1.0f); // 使用125MHz系统时钟
    pwm_set_enabled(slice_num, true);

    playsong(song);
    
    while (1) {
        sleep_ms(1000); // 播放结束后保持空闲
    }
}

关键说明

  • 利用Pico SDK的hardware/pwm库实现PWM控制,通过pwm_set_wrap计算周期来设置目标频率
  • pwm_set_gpio_level对应Python的duty_u16,通过调整占空比控制蜂鸣器发声状态
  • sleep_ms实现和原Python代码一致的0.3秒间隔逻辑

Rust语言实现(基于rp2040-hal)

前置准备

  • 已安装Rust环境及probe-rs工具链
  • 在Cargo.toml中添加依赖:
[dependencies]
rp2040-hal = "0.10.0"
embedded-hal = "1.0.0"
panic-halt = "0.2.0"

代码实现

use embedded_hal::digital::v2::OutputPin;
use embedded_hal::pwm::SetDutyCycle;
use panic_halt as _;
use rp2040_hal::{
    clocks::{init_clocks_and_plls, Clock},
    pac,
    pwm::{Pwm0, PwmSlice, ValidPwmPin},
    sio::Sio,
    watchdog::Watchdog,
};

// 音符-频率映射表
const TONES: &[(&str, u32)] = &[
    ("C5", 523),
    ("CS5", 554),
    ("D5", 587),
    ("DS5", 622),
    ("E5", 659),
    ("F5", 698),
    ("FS5", 740),
    ("G5", 784),
    ("GS5", 831),
    ("A5", 880),
    ("AS5", 932),
    ("B5", 988),
];

// 歌曲音符序列
const SONG: &[&str] = &["E5", "G5", "A5", "P", "E5", "G5", "B5", "A5", "P", "E5", "G5", "A5", "P", "G5", "E5"];

// 根据音符名称获取对应频率
fn get_freq(note: &str) -> Option<u32> {
    TONES.iter().find(|&&(n, _)| n == note).map(|&(_, f)| f)
}

// 播放指定频率的音符
fn playtone<T: ValidPwmPin>(pwm: &mut PwmSlice<T>, frequency: u32, led: &mut impl OutputPin) {
    const CLOCK_FREQ: u32 = 125_000_000;
    pwm.set_wrap(CLOCK_FREQ / frequency); // 设置PWM周期
    pwm.set_duty_cycle(1000); // 设置占空比
    let _ = led.toggle(); // 切换LED状态
}

// 停止蜂鸣器发声
fn bequiet<T: ValidPwmPin>(pwm: &mut PwmSlice<T>) {
    pwm.set_duty_cycle(0);
}

// 播放完整歌曲
fn playsong<T: ValidPwmPin>(pwm: &mut PwmSlice<T>, song: &[&str], led: &mut impl OutputPin) {
    for note in song {
        if *note == "P" {
            bequiet(pwm);
        } else {
            if let Some(freq) = get_freq(note) {
                playtone(pwm, freq, led);
            } else {
                bequiet(pwm);
            }
        }
        // 延迟300ms
        cortex_m::asm::delay(CLOCK_FREQ / 1000 * 300);
    }
    bequiet(pwm);
}

#[rp2040_hal::entry]
fn main() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();
    let mut watchdog = Watchdog::new(pac.WATCHDOG);
    let sio = Sio::new(pac.SIO);

    // 初始化系统时钟
    let clocks = init_clocks_and_plls(
        rp2040_hal::XOSC_CRYSTAL_FREQ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    // 初始化GPIO
    let pins = rp2040_hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let mut led = pins.gpio25.into_push_pull_output();
    let buzzer_pin = pins.gpio15.into_function::<rp2040_hal::gpio::FunctionPwm>();

    // 初始化PWM
    let mut pwm = Pwm0::new(pac.PWM0, &mut pac.RESETS);
    let mut buzzer_pwm = pwm.channel_a;
    buzzer_pwm.set_pin(buzzer_pin);
    buzzer_pwm.enable();

    // 播放歌曲
    playsong(&mut buzzer_pwm, SONG, &mut led);

    loop {
        cortex_m::asm::wfi(); // 播放结束后进入低功耗空闲状态
    }
}

关键说明

  • 基于rp2040-hal封装的PWM接口,通过set_wrap设置频率,set_duty_cycle控制占空比
  • 用cortex_m::asm::delay实现精确的毫秒延迟,和原Python代码的sleep(0.3)逻辑一致
  • 板载LED通过toggle方法切换状态,完全匹配原Python代码的行为

内容的提问来源于stack exchange,提问作者NaniNoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 15:55:27