如何用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
相关产品推荐
相关产品推荐

