From 958b4eaf51f31eda5320ebf015df74490b8c4173 Mon Sep 17 00:00:00 2001 From: CorvusPrudens Date: Thu, 24 Oct 2024 12:46:34 -0600 Subject: [PATCH] Provided for_each audio API --- Cargo.toml | 47 ++++++++++++++++-------- examples/passthru.rs | 15 +------- examples/usb_midi.rs | 11 ++---- examples/volume.rs | 26 +++++-------- src/audio.rs | 87 ++++++++++++++++++++++++++++++-------------- src/flash.rs | 10 ++--- src/hid.rs | 9 +---- 7 files changed, 113 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5557823..5cfbbc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ description = "Hardware Abstraction Layer implementation for Daisy boards" keywords = ["cortex-m", "stm32h7xx", "stm32h750", "hal", "daisy"] readme = "README.md" name = "libdaisy" -version = "0.1.0" +version = "0.2.0" license = "MIT" repository = "https://github.com/mtthw-meyer/libdaisy-rust.git" documentation = "https://docs.rs/libdaisy" @@ -14,20 +14,32 @@ exclude = [".gitignore"] [dependencies] cfg-if = "1" cortex-m = "0.7" -cortex-m-log = { version = "0.8", features = ["itm", "semihosting", "log-integration"], optional = true } +cortex-m-log = { version = "0.8", features = [ + "itm", + "semihosting", + "log-integration", +], optional = true } cortex-m-rtic = "1" -cortex-m-semihosting = { version = "0.5", optional = true } +cortex-m-semihosting = { version = "0.5", optional = true } debouncr = "0.2.2" -lazy_static = { version = "1.4.0", features = ["spin_no_std"], optional = true } +lazy_static = { version = "1.4.0", features = ["spin_no_std"], optional = true } log = "0.4" micromath = "2" panic-halt = "0.2.0" -panic-itm = { version = "0.4", optional = true } +panic-itm = { version = "0.4", optional = true } panic-rtt-target = { version = "0.1.3", optional = true } -panic-semihosting = { version = "0.6", optional = true } +panic-semihosting = { version = "0.6", optional = true } rtt-target = { version = "0.5.0", optional = true } stm32-fmc = "0.3.0" -stm32h7xx-hal = { version = "0.16.0", features = ["stm32h750v","rt","fmc", "xspi", "sdmmc", "sdmmc-fatfs", "usb_hs"] } +stm32h7xx-hal = { version = "0.16.0", features = [ + "stm32h750v", + "rt", + "fmc", + "xspi", + "sdmmc", + "sdmmc-fatfs", + "usb_hs", +] } [features] default = [] @@ -35,7 +47,12 @@ default = [] log-itm = ["panic-itm", "lazy_static", "cortex-m-log"] log-none = [] log-rtt = ["rtt-target", "panic-rtt-target"] -log-semihosting = ["panic-semihosting", "lazy_static", "cortex-m-log", "cortex-m-semihosting"] +log-semihosting = [ + "panic-semihosting", + "lazy_static", + "cortex-m-log", + "cortex-m-semihosting", +] # this lets you use `cargo fix`! #[[bin]] @@ -44,18 +61,18 @@ log-semihosting = ["panic-semihosting", "lazy_static", "cortex-m-log", "cortex-m #bench = false [profile.dev] -codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size in flash +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size in flash incremental = false -opt-level = "s" # optimize for binary size +opt-level = "s" # optimize for binary size [profile.release] codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size in flash -lto = true # better optimizations -opt-level = "s" # optimize for binary size +debug = true # symbols are nice and they don't increase the size in flash +lto = true # better optimizations +opt-level = "s" # optimize for binary size -[dev_dependencies] +[dev-dependencies] embedded-sdmmc = "0.4" libm = "0.2" num_enum = { version = "0.5", default-features = false } diff --git a/examples/passthru.rs b/examples/passthru.rs index acc47c0..b77c16f 100644 --- a/examples/passthru.rs +++ b/examples/passthru.rs @@ -17,7 +17,6 @@ mod app { #[local] struct Local { audio: audio::Audio, - buffer: audio::AudioBuffer, } #[init] @@ -32,15 +31,12 @@ mod app { let ccdr = system::System::init_clocks(device.PWR, device.RCC, &device.SYSCFG); let system = libdaisy::system_init!(core, device, ccdr, BLOCK_SIZE); - let buffer = [(0.0, 0.0); audio::BLOCK_SIZE_MAX]; - info!("Startup done!!"); ( Shared {}, Local { audio: system.audio, - buffer, }, init::Monotonics(), ) @@ -56,17 +52,10 @@ mod app { } // Interrupt handler for audio - #[task(binds = DMA1_STR1, local = [audio, buffer], priority = 8)] + #[task(binds = DMA1_STR1, local = [audio], priority = 8)] fn audio_handler(ctx: audio_handler::Context) { let audio = ctx.local.audio; - let buffer = ctx.local.buffer; - if audio.get_stereo(buffer) { - for (left, right) in &buffer.as_slice()[..BLOCK_SIZE] { - let _ = audio.push_stereo((*left, *right)); - } - } else { - info!("Error reading data!"); - } + audio.for_each(|left, right| (left, right)); } } diff --git a/examples/usb_midi.rs b/examples/usb_midi.rs index f3a1f58..ea27997 100644 --- a/examples/usb_midi.rs +++ b/examples/usb_midi.rs @@ -56,14 +56,6 @@ mod app { let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run"); ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::Hsi48); - /* - unsafe { - let pwr = &*stm32::PWR::ptr(); - pwr.cr3.modify(|_, w| w.usbregen().set_bit()); - while pwr.cr3.read().usb33rdy().bit_is_clear() {} - } - */ - let mut timer2 = device.TIM2.timer( MilliSeconds::from_ticks(200).into_rate(), ccdr.peripheral.TIM2, @@ -79,6 +71,7 @@ mod app { let gpio = gpio::GPIO::init( gpioc.pc7, + gpiog.pg3, Some(gpiob.pb12), Some(gpioc.pc11), Some(gpioc.pc10), @@ -110,6 +103,8 @@ mod app { Some(gpioa.pa2), Some(gpiob.pb14), Some(gpiob.pb15), + None, + None, ); let (pin_dm, pin_dp) = { (gpioa.pa11.into_alternate(), gpioa.pa12.into_alternate()) }; diff --git a/examples/volume.rs b/examples/volume.rs index f80328b..992c2b5 100644 --- a/examples/volume.rs +++ b/examples/volume.rs @@ -23,7 +23,6 @@ mod app { #[local] struct Local { audio: audio::Audio, - buffer: AudioBuffer, adc1: adc::Adc, timer2: Timer, } @@ -35,7 +34,6 @@ mod app { let device = ctx.device; let ccdr = system::System::init_clocks(device.PWR, device.RCC, &device.SYSCFG); let mut system = libdaisy::system_init!(core, device, ccdr); - let buffer = [(0.0, 0.0); audio::BLOCK_SIZE_MAX]; info!("Enable adc1"); let mut adc1 = system.adc1.enable(); @@ -64,7 +62,6 @@ mod app { Shared { control1 }, Local { audio: system.audio, - buffer, adc1, timer2, }, @@ -82,21 +79,16 @@ mod app { } // Interrupt handler for audio - #[task(binds = DMA1_STR1, local = [audio, buffer], shared = [control1], priority = 8)] + #[task(binds = DMA1_STR1, local = [audio], shared = [control1], priority = 8)] fn audio_handler(mut ctx: audio_handler::Context) { - let audio_handler::LocalResources { audio, buffer } = ctx.local; - - if audio.get_stereo(buffer) { - for (left, right) in buffer.iter_mut() { - ctx.shared.control1.lock(|c| { - let volume = c.get_value(); - info!("{}", volume); - *left *= volume; - *right *= volume; - audio.push_stereo((*left, *right)).unwrap(); - }); - } - } + let audio = ctx.local.audio; + + ctx.shared.control1.lock(|c| { + let volume = c.get_value(); + info!("{}", volume); + + audio.for_each(|left, right| (left * volume, right * volume)); + }); } #[task(binds = TIM2, local = [timer2, adc1], shared = [control1])] diff --git a/src/audio.rs b/src/audio.rs index 8468ccf..d151da4 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,5 +1,6 @@ //! Audio module. Handles audio startup and I/O. //! As well as converting between the S24 input and f32 for processing. +use core::convert::Infallible; use cortex_m::prelude::_embedded_hal_blocking_i2c_Write; use log::info; use stm32h7xx_hal::{ @@ -261,8 +262,14 @@ impl Audio { }); let max_transfer_size = block_size * 2; - let input = Input::new(unsafe { &mut RX_BUFFER }, max_transfer_size); - let output = Output::new(unsafe { &mut TX_BUFFER }, max_transfer_size); + let input = Input::new( + unsafe { &*core::ptr::addr_of!(RX_BUFFER) }, + max_transfer_size, + ); + let output = Output::new( + unsafe { &mut *core::ptr::addr_of_mut!(TX_BUFFER) }, + max_transfer_size, + ); info!( "Setup up Audio DMA: input: {:?}, output: {:?}", @@ -360,20 +367,23 @@ impl Audio { sai.enable_dma(SaiChannel::ChannelB); }); - output_stream.start(|sai1_rb| { + // There is no need to wait in this configuration. + output_stream.start(|_sai1_rb| { sai.enable_dma(SaiChannel::ChannelA); - - // wait until sai1's fifo starts to receive data - info!("Sai1 fifo waiting to receive data."); - while sai1_rb.cha().sr.read().flvl().is_empty() {} - info!("Audio started!"); - sai.enable(); - sai.try_send(0, 0).unwrap(); }); + info!("Audio started!"); + sai.enable(); + let max_transfer_size = block_size * 2; - let input = Input::new(unsafe { &mut RX_BUFFER }, max_transfer_size); - let output = Output::new(unsafe { &mut TX_BUFFER }, max_transfer_size); + let input = Input::new( + unsafe { &*core::ptr::addr_of!(RX_BUFFER) }, + max_transfer_size, + ); + let output = Output::new( + unsafe { &mut *core::ptr::addr_of_mut!(TX_BUFFER) }, + max_transfer_size, + ); info!( "Setup up Audio DMA: input: {:?}, output: {:?}", @@ -431,21 +441,6 @@ impl Audio { } } - /// Directly pass received audio to output without any processing. - pub fn passthru(&mut self) { - // Copy data - if self.read() { - let mut index = 0; - let mut out_index = self.output.index; - while index < self.max_transfer_size { - self.output.buffer[out_index] = self.input.buffer[index + self.input.index]; - self.output.buffer[out_index + 1] = self.input.buffer[index + self.input.index + 1]; - index += 2; - out_index += 2; - } - } - } - /// Gets the audio input from the DMA memory and writes it to buffer pub fn get_stereo(&mut self, buffer: &mut AudioBuffer) -> bool { if self.read() { @@ -471,6 +466,44 @@ impl Audio { None } + /// Process audio frame-by-frame. + #[inline] + pub fn for_each(&mut self, mut process: F) + where + F: FnMut(f32, f32) -> (f32, f32), + { + self.try_for_each::<_, Infallible>(|left, right| Ok(process(left, right))) + .unwrap() + } + + /// Process audio frame-by-frame. + /// + /// If the process closure returns an error, + /// it's bubbled up to the callsite of this method. + pub fn try_for_each(&mut self, mut process: F) -> Result<(), E> + where + F: FnMut(f32, f32) -> Result<(f32, f32), E>, + { + if self.read() { + let input = self.input.buffer + [self.input.index..self.input.index + self.max_transfer_size] + .chunks_exact(2); + + let output = self.output.buffer + [self.output.index..self.output.index + self.max_transfer_size] + .chunks_exact_mut(2); + + for (input, output) in input.zip(output) { + let (left, right) = + process(S24(input[0] as i32).into(), S24(input[1] as i32).into())?; + output[0] = S24::from(left).into(); + output[1] = S24::from(right).into(); + } + } + + Ok(()) + } + /// Push data to the DMA buffer for output /// Call this once per sample per call to [get_stereo()](Audio#get_stereo) #[allow(clippy::result_unit_err)] diff --git a/src/flash.rs b/src/flash.rs index 4c05cf3..778377f 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -195,8 +195,8 @@ impl Flash { /// - Erasing sets all the bits in the given area to `1`. /// - The memory array of the IS25LP064A/032A is organized into uniform 4 /// Kbyte sectors or - /// 32/64 Kbyte uniform blocks (a block consists of eight/sixteen adjacent - /// sectors respectively). + /// 32/64 Kbyte uniform blocks (a block consists of eight/sixteen adjacent + /// sectors respectively). pub fn erase(&mut self, op: FlashErase) -> NBFlashResult<()> { match self.state { FlashState::Erasing(e) => { @@ -266,9 +266,9 @@ impl Flash { /// to a 1. /// - The starting byte can be anywhere within the page (256 byte chunk). /// When the end of the - /// page is reached, the address will wrap around to the beginning of the - /// same page. If the data to be programmed are less than a full page, - /// the data of all other bytes on the same page will remain unchanged. + /// page is reached, the address will wrap around to the beginning of the + /// same page. If the data to be programmed are less than a full page, + /// the data of all other bytes on the same page will remain unchanged. pub fn program(&mut self, address: u32, data: &[u8]) -> NBFlashResult<()> { let prog = |flash: &mut Self, chunk_index: u32| -> NBFlashResult<()> { if let Some(chunk) = data.chunks(32).nth(chunk_index as usize) { diff --git a/src/hid.rs b/src/hid.rs index 6be4e6d..101fe97 100644 --- a/src/hid.rs +++ b/src/hid.rs @@ -273,13 +273,8 @@ where /// Set the brightness of the LED from 0.0 to 1.0. pub fn set_brightness(&mut self, value: f32) { - let value = if value > 1.0 { - 1.0 - } else if value < 0.0 { - 0.0 - } else { - value - }; + let value = value.clamp(0., 1.); + match self.invert { // Bias for slower transitions in the low brightness range // TODO configurable?