From ca5867ba36a6742f7464c0a186464338eb16f604 Mon Sep 17 00:00:00 2001 From: Edward Shen Date: Fri, 16 May 2025 23:53:53 -0700 Subject: [PATCH] Add music on button press --- src/main.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index dfcf4e9..6fe9971 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,10 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_futures::select::{Either, select}; use embassy_rp::bind_interrupts; -use embassy_rp::config::Config; use embassy_rp::gpio::{self, Input}; -use embassy_rp::peripherals::{DMA_CH0, PIO0}; +use embassy_rp::peripherals::{DMA_CH0, PIN_20, PIO0, PWM_SLICE2}; use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pwm::{self, Pwm, SetDutyCycle}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::{Channel, Sender}; use embassy_time::{Duration, Instant, Timer}; @@ -81,7 +81,7 @@ async fn button( loop { info!("Waiting for falling edge"); input.wait_for_falling_edge().await; - sender.send(ChannelMessage::Refilled).await; + let _ = sender.try_send(ChannelMessage::Refilled); input.wait_for_high().await; } } @@ -112,7 +112,7 @@ async fn main(spawner: Spawner) { // Timer::after_secs(1).await; // } - let p = embassy_rp::init(Config::default()); + let mut p = embassy_rp::init(embassy_rp::config::Config::default()); let fw = include_bytes!("../embassy/cyw43-firmware/43439A0.bin"); let clm = include_bytes!("../embassy/cyw43-firmware/43439A0_clm.bin"); @@ -152,10 +152,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(heartbeat(control))); unwrap!(spawner.spawn(button( - { - let input = Input::new(p.PIN_15, gpio::Pull::Up); - input - }, + Input::new(p.PIN_15, gpio::Pull::Up), CHANNEL.sender() ))); @@ -178,7 +175,10 @@ async fn main(spawner: Spawner) { info!("Water refilled received"); water_state.refill(); set_leds(water_state, &mut yellow_led, &mut red_led); + play_music(&mut p.PWM_SLICE2, &mut p.PIN_20).await; timer = water_state.timer(); + // only permit one message at a time + CHANNEL.clear(); } } } @@ -200,3 +200,103 @@ fn set_leds(water_state: WaterState, yellow_led: &mut Output, red_led: &mut Outp } } } + +async fn play_music(slice: &mut PWM_SLICE2, pin: &mut PIN_20) { + const SIXTHTEEN_NOTE_MILLIS: u64 = 100; + + #[derive(PartialEq, Eq, Clone, Copy)] + #[repr(u16)] + enum Note { + Rest = 0, + C4 = 262, + D4 = 294, + E4 = 330, + F4 = 349, + G4 = 392, + A5 = 440, + #[allow(dead_code)] + B5 = 494, + C5 = 524, + } + #[expect(clippy::enum_glob_use)] + use Note::*; + + #[repr(u8)] + enum Duration { + Sixthteenth = 1, + Eighth = 2, + DottedEighth = 3, + Quarter = 4, + Half = 8, + } + #[expect(clippy::enum_glob_use)] + use Duration::*; + + const SONG: [(Note, Duration); 36] = [ + (C4, DottedEighth), + (D4, Sixthteenth), + (E4, DottedEighth), + (F4, Sixthteenth), + (G4, Quarter), + (C5, Quarter), + // + (A5, Quarter), + (C5, Quarter), + (G4, Half), + // + (F4, Quarter), + (F4, Eighth), + (G4, Eighth), + (E4, Quarter), + (E4, Quarter), + // + (D4, Quarter), + (D4, Eighth), + (E4, Eighth), + (C4, Half), + // + (C4, DottedEighth), + (D4, Sixthteenth), + (E4, DottedEighth), + (F4, Sixthteenth), + (G4, Quarter), + (C5, Quarter), + // + (A5, Quarter), + (C5, Quarter), + (G4, Half), + // + (C5, Quarter), + (Rest, Eighth), + (A5, Eighth), + (G4, DottedEighth), + (G4, Sixthteenth), + (F4, Quarter), + // + (E4, Quarter), + (D4, Quarter), + (C4, Half), + ]; + + for (note, duration) in SONG { + let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); + let divider = 16; + #[expect(clippy::cast_possible_truncation)] + let period = clock_freq_hz.saturating_div(note as u32 * u32::from(divider)) as u16 - 1; + + let mut c = pwm::Config::default(); + c.top = period; + c.divider = divider.into(); + + let mut pwm = Pwm::new_output_a(&mut *slice, &mut *pin, c.clone()); + + if note == Rest { + let _ = pwm.set_duty_cycle_fully_off(); + } else { + let _ = pwm.set_duty_cycle_percent(50); + } + Timer::after_millis(duration as u64 * SIXTHTEEN_NOTE_MILLIS).await; + let _ = pwm.set_duty_cycle_fully_off(); + Timer::after_millis(20).await; + } +}