From 4c63f77c243164117820ed851efad61718c5c1d6 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Wed, 1 Oct 2025 13:36:25 -0600 Subject: [PATCH] WIP --- kernel/src/audio.rs | 143 ++++++++++++++++++++++++------- kernel/src/main.rs | 24 ++++-- user-apps/wav_player/src/main.rs | 11 ++- 3 files changed, 139 insertions(+), 39 deletions(-) diff --git a/kernel/src/audio.rs b/kernel/src/audio.rs index 30c75e0..b5fc298 100644 --- a/kernel/src/audio.rs +++ b/kernel/src/audio.rs @@ -1,17 +1,17 @@ -use core::{ - sync::atomic::{AtomicBool, Ordering}, - time::Duration, -}; +use crate::Audio; +use core::sync::atomic::{AtomicBool, Ordering}; use embassy_rp::{ Peri, - pio::Pio, - pio_programs::pwm::{PioPwm, PioPwmProgram}, - pwm::{Config, Pwm, SetDutyCycle}, + dma::{AnyChannel, Channel}, + gpio::Level, + pio::{ + Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, + ShiftDirection, StateMachine, program::pio_asm, + }, + pio_programs::clock_divider::calculate_pio_clock_divider, }; use embassy_time::Timer; -use crate::{Audio, Irqs}; - const AUDIO_BUFFER_LEN: usize = 1024; const _: () = assert!(AUDIO_BUFFER_LEN == abi_sys::AUDIO_BUFFER_LEN); pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN]; @@ -19,34 +19,119 @@ static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN]; pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true); -pub const SAMPLE_RATE_HZ: u32 = 22_050; +pub const SAMPLE_RATE_HZ: u32 = 8_000; #[embassy_executor::task] -pub async fn audio_handler(audio: Audio) { - let var_name = Pio::new(audio.pio_left, Irqs); - let Pio { - mut common, sm0, .. - } = var_name; +pub async fn audio_handler(mut audio: Audio) { + let prg = PioPwmAudioProgram8Bit::new(&mut audio.pio); + defmt::info!("loaded prg"); - let prg = PioPwmProgram::new(&mut common); - let mut pwm_pio = PioPwm::new(&mut common, sm0, audio.left, &prg); - - let period = Duration::from_nanos(1_000_000_000 / SAMPLE_RATE_HZ as u64); - pwm_pio.set_period(period); + let mut pwm_pio = PioPwmAudio::new(audio.dma, &mut audio.pio, audio.sm0, audio.left, &prg); + defmt::info!("cfgd sm"); + pwm_pio.configure(SAMPLE_RATE_HZ); pwm_pio.start(); - - let sample_interval = 1_000_000 / SAMPLE_RATE_HZ as u64; // in µs ≈ 45 µs - loop { - for &sample in unsafe { &AUDIO_BUFFER }.iter() { - let period_ns = period.as_nanos() as u32; - let duty_ns = period_ns * (sample as u32) / 255; - pwm_pio.write(Duration::from_nanos(duty_ns as u64)); - Timer::after_micros(sample_interval).await; // sample interval = 1 / sample rate - } + pwm_pio.write(unsafe { &AUDIO_BUFFER_1 }).await; unsafe { core::mem::swap(&mut AUDIO_BUFFER, &mut AUDIO_BUFFER_1) }; AUDIO_BUFFER_READY.store(true, Ordering::Release) } } + +struct PioPwmAudioProgram8Bit<'d, PIO: Instance>(LoadedProgram<'d, PIO>); + +impl<'d, PIO: Instance> PioPwmAudioProgram8Bit<'d, PIO> { + fn new(common: &mut Common<'d, PIO>) -> Self { + let prg = pio_asm!( + "out x, 8", // samples << + "out y, 8", // samples max >> + "loop_high:", + "set pins, 1", // keep pin high + "jmp x-- loop_high", // decrement X until 0 + "loop_low:", + "set pins, 0", // keep pin low + "jmp y-- loop_low", // decrement Y until 0 + ); + + let prg = common.load_program(&prg.program); + + Self(prg) + } +} + +struct PioPwmAudio<'d, PIO: Instance, const SM: usize> { + dma: Peri<'d, AnyChannel>, + cfg: Config<'d, PIO>, + sm: StateMachine<'d, PIO, SM>, +} + +impl<'d, PIO: Instance, const SM: usize> PioPwmAudio<'d, PIO, SM> { + fn new( + dma: Peri<'d, impl Channel>, + pio: &mut Common<'d, PIO>, + mut sm: StateMachine<'d, PIO, SM>, + pin: Peri<'d, impl PioPin>, + prg: &PioPwmAudioProgram8Bit<'d, PIO>, + ) -> Self { + let pin = pio.make_pio_pin(pin); + sm.set_pins(Level::High, &[&pin]); + sm.set_pin_dirs(Direction::Out, &[&pin]); + + let mut cfg = Config::default(); + cfg.set_set_pins(&[&pin]); + cfg.fifo_join = FifoJoin::TxOnly; + let mut shift_cfg = ShiftConfig::default(); + shift_cfg.auto_fill = true; + cfg.shift_out = shift_cfg; + cfg.use_program(&prg.0, &[]); + sm.set_config(&cfg); + + Self { + dma: dma.into(), + cfg, + sm, + } + } + + fn configure(&mut self, sample_rate: u32) { + let cycles_per_sample = u8::MAX as u32 + 2; // X_max + Y_max + movs + let target_pio_hz = cycles_per_sample * sample_rate; // ~11.3 MHz + + let divider = calculate_pio_clock_divider(target_pio_hz); + self.cfg.clock_divider = divider; + self.sm.set_clock_divider(divider); + } + + async fn write(&mut self, buf: &[u8]) { + let mut packed_buf = [0_u32; AUDIO_BUFFER_LEN / 4]; + + for (packed_sample, sample) in packed_buf.iter_mut().zip(buf.chunks(2)) { + *packed_sample = pack_two_samples(sample[0], sample[1]); + } + + self.sm + .tx() + .dma_push(self.dma.reborrow(), &packed_buf, false) + .await + } + + fn start(&mut self) { + self.sm.set_enable(true); + } + + fn stop(&mut self) { + self.sm.set_enable(false); + } +} + +fn pack_two_samples(s1: u8, s2: u8) -> u32 { + let x = ((s1 as u16) << 8 | (255 - s1) as u16); // original + let y = ((s2 as u16) << 8 | (255 - s2) as u16); + + // Scale to full 16-bit for higher volume: + let x_scaled = ((x as u32) << 8) | (x as u32 >> 8); + let y_scaled = ((y as u32) << 8) | (y as u32 >> 8); + + (x_scaled << 16) | y_scaled +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 48fb3d6..aedc590 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -49,11 +49,10 @@ use embassy_rp::{ i2c::{self, I2c}, multicore::{Stack, spawn_core1}, peripherals::{ - DMA_CH0, DMA_CH1, I2C1, PIN_6, PIN_7, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, PIN_15, - PIN_16, PIN_17, PIN_18, PIN_19, PIN_22, PIN_26, PIN_27, PIO0, PIO1, PWM_SLICE5, SPI0, SPI1, - USB, + DMA_CH0, DMA_CH1, DMA_CH3, I2C1, PIN_6, PIN_7, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, + PIN_15, PIN_16, PIN_17, PIN_18, PIN_19, PIN_22, PIN_26, PIN_27, PIO0, SPI0, SPI1, USB, }, - pio, + pio::{self, Common, Pio, StateMachine}, spi::{self, Spi}, usb as embassy_rp_usb, }; @@ -110,9 +109,15 @@ async fn main(_spawner: Spawner) { data: p.PIN_14, reset: p.PIN_15, }; + let Pio { + common, sm0, sm1, .. + } = Pio::new(p.PIO0, Irqs); + let audio = Audio { - pio_left: p.PIO0, - pio_right: p.PIO1, + dma: p.DMA_CH3, + pio: common, + sm0, + sm1, left: p.PIN_26, right: p.PIN_27, }; @@ -182,8 +187,10 @@ struct Display { reset: Peri<'static, PIN_15>, } struct Audio { - pio_left: Peri<'static, PIO0>, - pio_right: Peri<'static, PIO1>, + dma: Peri<'static, DMA_CH3>, + pio: Common<'static, PIO0>, + sm0: StateMachine<'static, PIO0, 0>, + sm1: StateMachine<'static, PIO0, 1>, left: Peri<'static, PIN_26>, right: Peri<'static, PIN_27>, } @@ -250,7 +257,6 @@ async fn kernel_task( usb: Peri<'static, USB>, ) { setup_mcu(mcu).await; - Timer::after_millis(250).await; setup_display(display, spawner).await; setup_sd(sd).await; diff --git a/user-apps/wav_player/src/main.rs b/user-apps/wav_player/src/main.rs index 3a970b5..3dafe70 100644 --- a/user-apps/wav_player/src/main.rs +++ b/user-apps/wav_player/src/main.rs @@ -41,9 +41,17 @@ pub fn main() { wav.restart().unwrap() } - wav.read(&mut buf).unwrap(); + let read = wav.read(&mut buf).unwrap(); send_audio_buffer(&buf); } + + let event = get_key(); + if event.state != KeyState::Idle { + match event.key { + KeyCode::Esc => return, + _ => (), + } + } } } @@ -64,6 +72,7 @@ impl File { impl PlatformFile for File { fn read(&mut self, buf: &mut [u8]) -> Result { let read = read_file(&self.file, self.current_pos, buf); + self.current_pos += read; Ok(read) }