diff --git a/Cargo.toml b/Cargo.toml index ef0eb4b..6242dc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stm32_i2s_v12x" version = "0.2.0" -authors = ["Sam Crow "] +authors = ["Sam Crow ", "Amaury Abrial aka Yruama_Lairba ! { - let cp = CorePeripherals::take().unwrap(); - let dp = Peripherals::take().unwrap(); - // RTT for logging - rtt_target::rtt_init_print!(); - - let rcc = dp.RCC.constrain(); - // SPI1/I2S1 is on APB2 - let clocks = rcc - .cfgr - .sysclk(100.mhz()) - .i2s_apb1_clk(38400.khz()) - .i2s_apb2_clk(38400.khz()) - .freeze(); - - // Use systick to run periodically - let mut systick = Timer::syst(cp.SYST, 1000.hz(), clocks); - - let gpioa = dp.GPIOA.split(); - let _i2s_pins = ( - gpioa.pa4.into_alternate_af5(), - gpioa.pa5.into_alternate_af5(), - gpioa.pa7.into_alternate_af5(), - ); - let mut sync_pin = gpioa.pa1.into_push_pull_output(); - sync_pin.set_low().unwrap(); - - // Access the RCC registers directly to enable SPI1 - unsafe { - let rcc_registers = stm32f4xx_hal::pac::RCC::ptr(); - (*rcc_registers).apb2enr.modify(|_, w| w.spi1en().enabled()); - } - - let mut i2s = I2s::new(I2s1Substitute); - - loop { - for &sample_rate in SAMPLE_RATES.iter() { - let config = MasterConfig::with_sample_rate( - clocks.i2s_apb2_clk().unwrap().0, - sample_rate, - Data16Frame16, - FrameFormat::PhilipsI2s, - Polarity::IdleHigh, - MasterClock::Disable, - ); - let mut configured_i2s = i2s.configure_master_transmit(config); - - configured_i2s.enable(); - configured_i2s.transmit_blocking(&TEST_SAMPLES); - nb::block!(configured_i2s.disable()).unwrap(); - - i2s = configured_i2s.deconfigure(); - - nb::block!(systick.wait()).unwrap(); - } - sync_pin.toggle().unwrap(); - } -} - -struct I2s1Substitute; - -unsafe impl Instance for I2s1Substitute { - const REGISTERS: *mut RegisterBlock = stm32f4xx_hal::pac::SPI1::ptr() as *mut RegisterBlock; -} diff --git a/examples/master_transmit_d16f32.rs b/examples/master_transmit_d16f32.rs deleted file mode 100644 index 561fa6f..0000000 --- a/examples/master_transmit_d16f32.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! -//! Periodically transmits a sequence of 16-bit samples (extended into 32-bit frames) -//! using SPI1/I2S1 on an STM32F412 -//! -//! Pins: -//! * PA4, AF5 - I2S1_WS -//! * PA5, AF5 - I2S1_CK -//! * PA7, AF5 - I2S1_SD -//! -//! To compile: -//! RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --example master_transmit_d16f32 --target thumbv7em-none-eabihf --release -//! -//! This uses some unsafe code so that it can depend on a version of stm32f4xx-hal that does not -//! depend on stm32_i2s. -//! - -#![no_std] -#![no_main] - -extern crate cortex_m_rt; -extern crate nb; -extern crate panic_rtt_target; -extern crate rtt_target; -extern crate stm32_i2s_v12x; -extern crate stm32f4xx_hal; - -use stm32f4xx_hal::hal::prelude::*; -use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; -use stm32f4xx_hal::prelude::*; -use stm32f4xx_hal::timer::Timer; - -use stm32_i2s_v12x::format::{Data16Frame32, FrameFormat}; -use stm32_i2s_v12x::{I2s, Instance, MasterClock, MasterConfig, Polarity, RegisterBlock}; - -/// 16-bit samples to transmit -const TEST_SAMPLES: [i16; 12] = [ - 0x0000, - 0x0000, - 0xaa55_u16 as i16, - 0x55aa_u16 as i16, - 0x0000_u16 as i16, - 0xffff_u16 as i16, - 0x1010_u16 as i16, - 0xaaaa_u16 as i16, - 0x5555_u16 as i16, - 0xe621_u16 as i16, - 0x0000, - 0x0000, -]; - -/// Sample rates to test -const SAMPLE_RATES: [u32; 8] = [8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000]; - -#[cortex_m_rt::entry] -fn main() -> ! { - let cp = CorePeripherals::take().unwrap(); - let dp = Peripherals::take().unwrap(); - // RTT for logging - rtt_target::rtt_init_print!(); - - let rcc = dp.RCC.constrain(); - // SPI1/I2S1 is on APB2 - let clocks = rcc - .cfgr - .sysclk(100.mhz()) - .i2s_apb1_clk(76800.khz()) - .i2s_apb2_clk(76800.khz()) - .freeze(); - - // Use systick to run periodically - let mut systick = Timer::syst(cp.SYST, 1000.hz(), clocks); - - let gpioa = dp.GPIOA.split(); - let _i2s_pins = ( - gpioa.pa4.into_alternate_af5(), - gpioa.pa5.into_alternate_af5(), - gpioa.pa7.into_alternate_af5(), - ); - let mut sync_pin = gpioa.pa1.into_push_pull_output(); - sync_pin.set_low().unwrap(); - - // Access the RCC registers directly to enable SPI1 - unsafe { - let rcc_registers = stm32f4xx_hal::pac::RCC::ptr(); - (*rcc_registers).apb2enr.modify(|_, w| w.spi1en().enabled()); - } - - let mut i2s = I2s::new(I2s1Substitute); - - loop { - for &sample_rate in SAMPLE_RATES.iter() { - let config = MasterConfig::with_sample_rate( - clocks.i2s_apb2_clk().unwrap().0, - sample_rate, - Data16Frame32, - FrameFormat::PhilipsI2s, - Polarity::IdleHigh, - MasterClock::Disable, - ); - let mut configured_i2s = i2s.configure_master_transmit(config); - - configured_i2s.enable(); - configured_i2s.transmit_blocking(&TEST_SAMPLES); - nb::block!(configured_i2s.disable()).unwrap(); - - i2s = configured_i2s.deconfigure(); - - nb::block!(systick.wait()).unwrap(); - } - sync_pin.toggle().unwrap(); - } -} - -struct I2s1Substitute; - -unsafe impl Instance for I2s1Substitute { - const REGISTERS: *mut RegisterBlock = stm32f4xx_hal::pac::SPI1::ptr() as *mut RegisterBlock; -} diff --git a/examples/master_transmit_d24f32.rs b/examples/master_transmit_d24f32.rs deleted file mode 100644 index ddd4695..0000000 --- a/examples/master_transmit_d24f32.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! -//! Periodically transmits a sequence of 24-bit samples (extended into 32-bit frames) -//! using SPI1/I2S1 on an STM32F412 -//! -//! Pins: -//! * PA4, AF5 - I2S1_WS -//! * PA5, AF5 - I2S1_CK -//! * PA7, AF5 - I2S1_SD -//! -//! To compile: -//! RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --example master_transmit_d24f32 --target thumbv7em-none-eabihf --release -//! -//! This uses some unsafe code so that it can depend on a version of stm32f4xx-hal that does not -//! depend on stm32_i2s. -//! - -#![no_std] -#![no_main] - -extern crate cortex_m_rt; -extern crate nb; -extern crate panic_rtt_target; -extern crate rtt_target; -extern crate stm32_i2s_v12x; -extern crate stm32f4xx_hal; - -use stm32f4xx_hal::hal::prelude::*; -use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; -use stm32f4xx_hal::prelude::*; -use stm32f4xx_hal::timer::Timer; - -use stm32_i2s_v12x::format::{Data24Frame32, FrameFormat}; -use stm32_i2s_v12x::{I2s, Instance, MasterClock, MasterConfig, Polarity, RegisterBlock}; - -/// 16-bit samples to transmit -const TEST_SAMPLES: [i32; 12] = [ - 0x00_0000, - 0x00_0000, - 0x20_aa55_u32 as i32, - 0x26_55aa_u32 as i32, - 0x01_0000_u32 as i32, - 0x99_ffff_u32 as i32, - 0xe9_1010_u32 as i32, - 0xf3_aaaa_u32 as i32, - 0xcd_5555_u32 as i32, - 0xe9_e621_u32 as i32, - 0x00_0000, - 0x00_0000, -]; - -/// Sample rates to test -const SAMPLE_RATES: [u32; 8] = [8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000]; - -#[cortex_m_rt::entry] -fn main() -> ! { - let cp = CorePeripherals::take().unwrap(); - let dp = Peripherals::take().unwrap(); - // RTT for logging - rtt_target::rtt_init_print!(); - - let rcc = dp.RCC.constrain(); - // SPI1/I2S1 is on APB2 - let clocks = rcc - .cfgr - .sysclk(100.mhz()) - .i2s_apb1_clk(76800.khz()) - .i2s_apb2_clk(76800.khz()) - .freeze(); - - // Use systick to run periodically - let mut systick = Timer::syst(cp.SYST, 1000.hz(), clocks); - - let gpioa = dp.GPIOA.split(); - let _i2s_pins = ( - gpioa.pa4.into_alternate_af5(), - gpioa.pa5.into_alternate_af5(), - gpioa.pa7.into_alternate_af5(), - ); - let mut sync_pin = gpioa.pa1.into_push_pull_output(); - sync_pin.set_low().unwrap(); - - // Access the RCC registers directly to enable SPI1 - unsafe { - let rcc_registers = stm32f4xx_hal::pac::RCC::ptr(); - (*rcc_registers).apb2enr.modify(|_, w| w.spi1en().enabled()); - } - - let mut i2s = I2s::new(I2s1Substitute); - - loop { - for &sample_rate in SAMPLE_RATES.iter() { - let config = MasterConfig::with_sample_rate( - clocks.i2s_apb2_clk().unwrap().0, - sample_rate, - Data24Frame32, - FrameFormat::PhilipsI2s, - Polarity::IdleHigh, - MasterClock::Disable, - ); - let mut configured_i2s = i2s.configure_master_transmit(config); - - configured_i2s.enable(); - configured_i2s.transmit_blocking(&TEST_SAMPLES); - nb::block!(configured_i2s.disable()).unwrap(); - - i2s = configured_i2s.deconfigure(); - - nb::block!(systick.wait()).unwrap(); - } - sync_pin.toggle().unwrap(); - } -} - -struct I2s1Substitute; - -unsafe impl Instance for I2s1Substitute { - const REGISTERS: *mut RegisterBlock = stm32f4xx_hal::pac::SPI1::ptr() as *mut RegisterBlock; -} diff --git a/examples/master_transmit_d32f32.rs b/examples/master_transmit_d32f32.rs deleted file mode 100644 index e40fb30..0000000 --- a/examples/master_transmit_d32f32.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! -//! Periodically transmits a sequence of 32-bit samples -//! using SPI1/I2S1 on an STM32F412 -//! -//! Pins: -//! * PA4, AF5 - I2S1_WS -//! * PA5, AF5 - I2S1_CK -//! * PA7, AF5 - I2S1_SD -//! -//! To compile: -//! RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --example master_transmit_d32f32 --target thumbv7em-none-eabihf --release -//! -//! This uses some unsafe code so that it can depend on a version of stm32f4xx-hal that does not -//! depend on stm32_i2s. -//! - -#![no_std] -#![no_main] - -extern crate cortex_m_rt; -extern crate nb; -extern crate panic_rtt_target; -extern crate rtt_target; -extern crate stm32_i2s_v12x; -extern crate stm32f4xx_hal; - -use stm32f4xx_hal::hal::prelude::*; -use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; -use stm32f4xx_hal::prelude::*; -use stm32f4xx_hal::timer::Timer; - -use stm32_i2s_v12x::format::{Data32Frame32, FrameFormat}; -use stm32_i2s_v12x::{I2s, Instance, MasterClock, MasterConfig, Polarity, RegisterBlock}; - -/// 16-bit samples to transmit -const TEST_SAMPLES: [i32; 12] = [ - 0x0000_0000, - 0x0000_0000, - 0xe926_aa55_u32 as i32, - 0x3726_55aa_u32 as i32, - 0xbc01_0000_u32 as i32, - 0xba99_ffff_u32 as i32, - 0x19e9_1010_u32 as i32, - 0x27f3_aaaa_u32 as i32, - 0x12cd_5555_u32 as i32, - 0xf7e9_e621_u32 as i32, - 0x0000_0000, - 0x0000_0000, -]; - -/// Sample rates to test -const SAMPLE_RATES: [u32; 8] = [8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000]; - -#[cortex_m_rt::entry] -fn main() -> ! { - let cp = CorePeripherals::take().unwrap(); - let dp = Peripherals::take().unwrap(); - // RTT for logging - rtt_target::rtt_init_print!(); - - let rcc = dp.RCC.constrain(); - // SPI1/I2S1 is on APB2 - let clocks = rcc - .cfgr - .sysclk(100.mhz()) - .i2s_apb1_clk(76800.khz()) - .i2s_apb2_clk(76800.khz()) - .freeze(); - - // Use systick to run periodically - let mut systick = Timer::syst(cp.SYST, 1000.hz(), clocks); - - let gpioa = dp.GPIOA.split(); - let _i2s_pins = ( - gpioa.pa4.into_alternate_af5(), - gpioa.pa5.into_alternate_af5(), - gpioa.pa7.into_alternate_af5(), - ); - let mut sync_pin = gpioa.pa1.into_push_pull_output(); - sync_pin.set_low().unwrap(); - - // Access the RCC registers directly to enable SPI1 - unsafe { - let rcc_registers = stm32f4xx_hal::pac::RCC::ptr(); - (*rcc_registers).apb2enr.modify(|_, w| w.spi1en().enabled()); - } - - let mut i2s = I2s::new(I2s1Substitute); - - loop { - for &sample_rate in SAMPLE_RATES.iter() { - let config = MasterConfig::with_sample_rate( - clocks.i2s_apb2_clk().unwrap().0, - sample_rate, - Data32Frame32, - FrameFormat::PhilipsI2s, - Polarity::IdleHigh, - MasterClock::Disable, - ); - let mut configured_i2s = i2s.configure_master_transmit(config); - - configured_i2s.enable(); - configured_i2s.transmit_blocking(&TEST_SAMPLES); - nb::block!(configured_i2s.disable()).unwrap(); - - i2s = configured_i2s.deconfigure(); - - nb::block!(systick.wait()).unwrap(); - } - sync_pin.toggle().unwrap(); - } -} - -struct I2s1Substitute; - -unsafe impl Instance for I2s1Substitute { - const REGISTERS: *mut RegisterBlock = stm32f4xx_hal::pac::SPI1::ptr() as *mut RegisterBlock; -} diff --git a/examples/master_transmit_with_master_clock.rs b/examples/master_transmit_with_master_clock.rs deleted file mode 100644 index a16a354..0000000 --- a/examples/master_transmit_with_master_clock.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! -//! Periodically transmits a sequence of 16-bit samples using SPI1/I2S1 on an STM32F412, -//! with a master clock output -//! -//! Pins: -//! * PA1 - Synchronization (toggles when the sample rate resets) -//! * PA4, AF5 - I2S1_WS -//! * PA5, AF5 - I2S1_CK -//! * PA7, AF5 - I2S1_SD -//! * PC4, AF5 - I2S1_MCK -//! -//! To compile: -//! RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --example master_transmit_d16f16 --target thumbv7em-none-eabihf --release -//! -//! This uses some unsafe code so that it can depend on a version of stm32f4xx-hal that does not -//! depend on stm32_i2s. -//! - -#![no_std] -#![no_main] - -extern crate cortex_m_rt; -extern crate nb; -extern crate panic_rtt_target; -extern crate rtt_target; -extern crate stm32_i2s_v12x; -extern crate stm32f4xx_hal; - -use stm32f4xx_hal::hal::prelude::*; -use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; -use stm32f4xx_hal::prelude::*; -use stm32f4xx_hal::timer::Timer; - -use stm32_i2s_v12x::format::{Data16Frame16, FrameFormat}; -use stm32_i2s_v12x::{I2s, Instance, MasterClock, MasterConfig, Polarity, RegisterBlock}; - -/// 16-bit samples to transmit -const TEST_SAMPLES: [i16; 12] = [ - 0x0000, - 0x0000, - 0xaa55_u16 as i16, - 0x55aa_u16 as i16, - 0x0000_u16 as i16, - 0xffff_u16 as i16, - 0x1010_u16 as i16, - 0xaaaa_u16 as i16, - 0x5555_u16 as i16, - 0xe621_u16 as i16, - 0x0000, - 0x0000, -]; - -/// Sample rates to test -const SAMPLE_RATES: [u32; 6] = [8000, 16000, 22050, 32000, 44100, 48000]; - -#[cortex_m_rt::entry] -fn main() -> ! { - let cp = CorePeripherals::take().unwrap(); - let dp = Peripherals::take().unwrap(); - // RTT for logging - rtt_target::rtt_init_print!(); - - let rcc = dp.RCC.constrain(); - // SPI1/I2S1 is on APB2 - let i2s_in_frequency = 200.mhz(); // 200 MHz when master clock output is enabled - let clocks = rcc - .cfgr - .i2s_apb1_clk(i2s_in_frequency) - .i2s_apb2_clk(i2s_in_frequency) - .freeze(); - - // Use systick to run periodically - let mut systick = Timer::syst(cp.SYST, 1000.hz(), clocks); - - let gpioa = dp.GPIOA.split(); - let gpioc = dp.GPIOC.split(); - let _i2s_pins = ( - gpioa.pa4.into_alternate_af5(), - gpioa.pa5.into_alternate_af5(), - gpioa.pa7.into_alternate_af5(), - gpioc.pc4.into_alternate_af5(), - ); - let mut sync_pin = gpioa.pa1.into_push_pull_output(); - sync_pin.set_low().unwrap(); - - // Access the RCC registers directly to enable SPI1 - unsafe { - let rcc_registers = stm32f4xx_hal::pac::RCC::ptr(); - (*rcc_registers).apb2enr.modify(|_, w| w.spi1en().enabled()); - } - - let mut i2s = I2s::new(I2s1Substitute); - - loop { - for &sample_rate in SAMPLE_RATES.iter() { - let config = MasterConfig::with_sample_rate( - clocks.i2s_apb2_clk().unwrap().0, - sample_rate, - Data16Frame16, - FrameFormat::PhilipsI2s, - Polarity::IdleLow, - MasterClock::Enable, - ); - let mut configured_i2s = i2s.configure_master_transmit(config); - - configured_i2s.enable(); - configured_i2s.transmit_blocking(&TEST_SAMPLES); - nb::block!(configured_i2s.disable()).unwrap(); - - i2s = configured_i2s.deconfigure(); - - nb::block!(systick.wait()).unwrap(); - } - sync_pin.toggle().unwrap(); - } -} - -struct I2s1Substitute; - -unsafe impl Instance for I2s1Substitute { - const REGISTERS: *mut RegisterBlock = stm32f4xx_hal::pac::SPI1::ptr() as *mut RegisterBlock; -} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index ca25ea1..0000000 --- a/src/config.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! I2S configuration - -use core::convert::TryInto; -use core::ops::Range; - -use super::format::{DataFormat, FrameFormat}; -use super::pac::spi1::i2scfgr::CHLEN_A; -use crate::Polarity; - -/// Allowed values for I2S clock division -const DIVISION_RANGE: Range = 4..512; - -/// Master clock enable flag -#[derive(Debug, Clone)] -pub enum MasterClock { - /// Master clock output enabled - Enable, - /// Master clock output disabled - Disable, -} - -/// Configuration for master mode -#[derive(Debug, Clone)] -pub struct MasterConfig { - /// The clock division between the input clock and bit clock output - /// - /// This value is placed into the I2S prescaler register, with the least significant bit in - /// ODD and the remaining bits in I2SDIV. - /// - /// Invariant: This value is in the range `[4, 511]` - pub(crate) division: u16, - /// The data format used in memory - pub(crate) data_format: F, - /// The frame format used to transmit bits over wires - pub(crate) frame_format: FrameFormat, - /// The clock polarity - pub(crate) polarity: Polarity, - /// Enable master clock output (256 times the frequency of the word select output) - pub(crate) master_clock: MasterClock, -} - -impl MasterConfig -where - F: DataFormat, -{ - /// Creates a configuration with a manually specified division from the input clock to the - /// output bit clock - /// - /// # Panics - /// - /// This function panics if division is outside the range `[4, 511]`. - pub fn with_division( - division: u16, - data_format: F, - frame_format: FrameFormat, - polarity: Polarity, - master_clock: MasterClock, - ) -> Self { - if !DIVISION_RANGE.contains(&division) { - panic!( - "I2S clock division {} outside allowed range {}..{}", - division, DIVISION_RANGE.start, DIVISION_RANGE.end - ); - } - MasterConfig { - division, - data_format, - frame_format, - polarity, - master_clock, - } - } - - /// Creates a configuration with automatic division based on the input clock frequency and - /// desired sample rate - /// - /// frequency_in: The frequency of the I2S clock entering the I2S peripheral, in hertz - /// - /// sample_rate: The desired audio sample rate (for each channel) in samples per second - /// - /// # Panics - /// - /// This function panics if the calculated division is outside the range `[4, 511]`. - pub fn with_sample_rate( - frequency_in: u32, - sample_rate: u32, - data_format: F, - frame_format: FrameFormat, - polarity: Polarity, - master_clock: MasterClock, - ) -> Self { - let bits_per_sample: u32 = match F::CHLEN { - CHLEN_A::SIXTEENBIT => 16, - CHLEN_A::THIRTYTWOBIT => 32, - }; - // Extra division when master clock output is enabled - let master_clock_division: u32 = if matches!(master_clock, MasterClock::Enable) { - match F::CHLEN { - CHLEN_A::SIXTEENBIT => 8, - CHLEN_A::THIRTYTWOBIT => 4, - } - } else { - 1 - }; - - // Calculate division based on input frequency and sample rate - // Calculate actual bit rate - let bit_rate = sample_rate * 2 * bits_per_sample; - // sample_rate = frequency_in / ((bits_per_sample * 2) * ((2 * I2SDIV) + ODD) * master_clock_division)) - // substitute division = (2 * I2SDIV) + ODD - // sample_rate = frequency_in / ((bits_per_sample * 2) * division * master_clock_division) - // (bits_per_sample * 2) * division * master_clock_division = frequency_in / sample_rate - // division = frequency_in / (sample_rate * bits_per_sample * 2 * master_clock_division) - let division = frequency_in / (bit_rate * master_clock_division); - - // Division +/- 1 may result in a more accurate sample rate. Test the three options. - let division_options: [u32; 3] = [ - division.saturating_sub(1), - division, - division.saturating_add(1), - ]; - let best_division: u32 = division_options - .iter() - .cloned() - .filter(is_valid_division) - .min_by_key(|possible_division| { - // Calculate the real sample rate - let real_sample_rate = frequency_in - / (bits_per_sample * 2 * *possible_division * master_clock_division); - i32::abs((real_sample_rate as i32) - (sample_rate as i32)) - }) - .expect("Couldn't find a valid I2S division value"); - - Self::with_division( - best_division.try_into().unwrap(), - data_format, - frame_format, - polarity, - master_clock, - ) - } -} - -/// Returns true if the provided value is in the allowed range of division values -fn is_valid_division(division: &u32) -> bool { - *division >= u32::from(DIVISION_RANGE.start) && *division < u32::from(DIVISION_RANGE.end) -} - -/// Configuration for slave mode -#[derive(Debug, Clone)] -pub struct SlaveConfig { - /// The data format used in memory - pub(crate) data_format: F, - /// The frame format used to transmit bits over wires - pub(crate) frame_format: FrameFormat, - /// The clock polarity - pub(crate) polarity: Polarity, -} - -impl SlaveConfig { - pub fn new(data_format: F, frame_format: FrameFormat, polarity: Polarity) -> Self { - SlaveConfig { - data_format, - frame_format, - polarity, - } - } -} diff --git a/src/driver.rs b/src/driver.rs new file mode 100644 index 0000000..65dd9c2 --- /dev/null +++ b/src/driver.rs @@ -0,0 +1,817 @@ +//! Types definitions for I2S driver. +//! +//! API of this module provides thin abstractions that try to give access to relevant hardware +//! details while preventing irrelevant or meaningless operation. This allow precise and concise +//! control of a SPI/I2S peripheral. It's meant for advanced usage, for example with interrupt or +//! DMA. The job is mainly done by [`I2sDriver`], a type that wrap an [`I2sPeripheral`] to control +//! it. +//! +//! # Configure and instantiate driver. +//! +//! [`I2sDriverConfig`] is used to create configuration of the i2s driver: +//! ```no_run +//! # use stm32_i2s_v12x::driver::*; +//! let driver_config = I2sDriverConfig::new_master() +//! .receive() +//! .standard(Philips) +//! .data_format(DataFormat::Data16Channel32) +//! .master_clock(true) +//! .request_frequency(48_000); +//! ``` +//! Then you can instantiate the driver around an `I2sPeripheral`: +//! ```ignore +//! // instantiate from configuration +//! let driver = driver_config.i2s_driver(i2s_peripheral); +//! +//! // alternate way +//! let driver = I2sDriver::new(i2s_peripheral, driver_config); +//! ``` +//! +//! # Usage +//! +//! `I2sDriver` actually give direct access to hardware, there isn't concept of audio data with it, +//! it's up to the user to reconstruct this information by controlling the hardware and using +//! available informations. +//! +//! Pseudocode example when driver is configured to receive 16 bit audio data: +//! ```ignore +//! let status = driver.status(); +//! if status.rxne() { +//! let data = driver.read_data_register(); +//! match status.chside() { +//! Channel::Left => /* `data` contains left channel audio data */, +//! Channel::Right => /* `data` contains right channel audio data */, +//! } +//! } +//! ``` +use core::marker::PhantomData; + +use crate::pac::spi1::RegisterBlock; +use crate::pac::spi1::{i2spr, sr}; +use crate::I2sPeripheral; + +pub use crate::marker::{self, *}; + +/// The channel associated with a sample +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Channel { + /// Left channel + Left, + /// Right channel + Right, +} + +/// Content of the status register. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` +pub struct Status { + value: sr::R, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, +} + +impl Status { + /// Get the BSY flag. If `true` the I2s device is busy communicating. + pub fn bsy(&self) -> bool { + self.value.bsy().bit() + } +} + +impl Status +where + STD: marker::ChannelFlag, +{ + /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. + /// + /// This flag is updated when TXE or RXNE flags are set. This flag is meaningless and therefore + /// not reliable is case of error. This flag is not available in PCM standard because it's also + /// meaningless in this case. + pub fn chside(&self) -> Channel { + match self.value.chside().bit() { + false => Channel::Left, + true => Channel::Right, + } + } +} + +impl Status { + /// Get the FRE flag. If `true` a frame error occurred. + /// + /// This flag is set by hardware when the WS line change at an unexpected moment. Usually, this + /// indicate a synchronisation issue. This flag can only be set in Slave mode and therefore can + /// only be read in this mode. + /// + /// This flag is cleared when reading the status register. + pub fn fre(&self) -> bool { + self.value.fre().bit() + } +} + +impl Status { + /// Get the OVR flag. If `true` an overrun error occurred. + /// + /// This flag is set when data are received and the previous data have not yet been read. As a + /// result, the incoming data are lost. Since this flag can happen only in Receive mode, it can + /// only be read in this mode. + /// + /// This flag is cleared by a read operation on the data register followed by a read to the + /// status register. + pub fn ovr(&self) -> bool { + self.value.ovr().bit() + } + + /// Get the RXNE flag. If `true` a valid received data is present in the Rx buffer. + /// + /// This flag can only happen in reception mode and therefore can only be read in this mode. + /// + /// This flag is cleared when the data register is read. + pub fn rxne(&self) -> bool { + self.value.rxne().bit() + } +} + +impl Status { + /// Get the TXE flag. If `true` the Tx buffer is empty and the next data can be loaded into it. + /// + /// This flag can only happen in transmission mode and therefore can only be read in this mode. + /// + /// This flag is cleared by writing into the data register or by disabling the I2s peripheral. + pub fn txe(&self) -> bool { + self.value.txe().bit() + } +} + +impl Status { + /// Get the UDR flag. If `true` an underrun error occurred. + /// + /// This flag is set when the first clock for data transmission appears while the software has + /// not yet loaded any value into the data register. This flag can only be set in Slave + /// Transmit mode and therefore can only be read in this mode. + /// + /// This flag is cleared by reading the status register. + pub fn udr(&self) -> bool { + self.value.udr().bit() + } +} + +#[derive(Debug, Clone, Copy)] +enum SlaveOrMaster { + Slave, + Master, +} + +#[derive(Debug, Clone, Copy)] +enum TransmitOrReceive { + Transmit, + Receive, +} + +/// Various ways to specify sampling frequency. +#[derive(Debug, Clone, Copy)] +enum Frequency { + Prescaler(bool, u8), + Request(u32), + Require(u32), +} + +/// Those thing are not part of the public API but appear on public trait. +pub(crate) mod private { + #[derive(Debug, Clone, Copy)] + /// I2s standard selection. + pub enum I2sStandard { + /// Philips I2S + Philips, + /// MSB Justified + Msb, + /// LSB Justified + Lsb, + /// PCM with short frame synchronisation. + PcmShortSync, + /// PCM with long frame synchronisation. + PcmLongSync, + } +} +pub(crate) use private::I2sStandard; + +/// Steady state clock polarity +#[derive(Debug, Clone, Copy)] +pub enum ClockPolarity { + /// Clock low when idle + IdleLow, + /// Clock high when idle + IdleHigh, +} + +/// Data length to be transferred and channel length +#[derive(Debug, Clone, Copy)] +pub enum DataFormat { + /// 16 bit data length on 16 bit wide channel + Data16Channel16, + /// 16 bit data length on 32 bit wide channel + Data16Channel32, + /// 24 bit data length on 32 bit wide channel + Data24Channel32, + /// 32 bit data length on 32 bit wide channel + Data32Channel32, +} + +impl Default for DataFormat { + fn default() -> Self { + DataFormat::Data16Channel16 + } +} + +#[derive(Debug, Clone, Copy)] +/// I2s driver configuration. Can be used as an i2s driver builder. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` +/// +/// **Note:** because of it's typestate, methods of this type don't change variable content, they +/// return a new value instead. +pub struct I2sDriverConfig { + slave_or_master: SlaveOrMaster, + transmit_or_receive: TransmitOrReceive, + standard: I2sStandard, + clock_polarity: ClockPolarity, + data_format: DataFormat, + master_clock: bool, + frequency: Frequency, + + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, +} + +impl I2sDriverConfig { + /// Create a new default slave configuration. + pub fn new_slave() -> Self { + Self { + slave_or_master: SlaveOrMaster::Slave, + transmit_or_receive: TransmitOrReceive::Transmit, + standard: I2sStandard::Philips, + clock_polarity: ClockPolarity::IdleLow, + data_format: Default::default(), + master_clock: false, + frequency: Frequency::Prescaler(false, 0b10), + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } +} + +impl I2sDriverConfig { + /// Create a new default master configuration. + pub fn new_master() -> Self { + Self { + slave_or_master: SlaveOrMaster::Master, + transmit_or_receive: TransmitOrReceive::Transmit, + standard: I2sStandard::Philips, + clock_polarity: ClockPolarity::IdleLow, + data_format: Default::default(), + master_clock: false, + frequency: Frequency::Prescaler(false, 0b10), + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } +} + +/// rounding division +fn div_round(n: u32, d: u32) -> u32 { + (n + (d >> 1)) / d +} + +// unsafe, div should be greater or equal to 2 +fn _set_prescaler(w: &mut i2spr::W, odd: bool, div: u8) { + w.odd().bit(odd); + unsafe { w.i2sdiv().bits(div) }; +} + +// Note, calculation details: +// Fs = i2s_clock / [256 * ((2 * div) + odd)] when master clock is enabled +// Fs = i2s_clock / [(channel_length * 2) * ((2 * div) + odd)]` when master clock is disabled +// channel_length is 16 or 32 +// +// can be rewritten as +// Fs = i2s_clock / (coef * division) +// where coef is a constant equal to 256, 64 or 32 depending channel length and master clock +// and where division = (2 * div) + odd +// +// Equation can be rewritten as +// division = i2s_clock/ (coef * Fs) +// +// note: division = (2 * div) + odd = (div << 1) + odd +// in other word, from bits point of view, division[8:1] = div[7:0] and division[0] = odd +fn _set_request_frequency( + w: &mut i2spr::W, + i2s_clock: u32, + request_freq: u32, + mclk: bool, + data_format: DataFormat, +) { + let coef = _coef(mclk, data_format); + let division = div_round(i2s_clock, coef * request_freq); + let (odd, div) = if division < 4 { + (false, 2) + } else if division > 511 { + (true, 255) + } else { + ((division & 1) == 1, (division >> 1) as u8) + }; + _set_prescaler(w, odd, div); +} + +// see _set_request_frequency for explanation +fn _set_require_frequency( + w: &mut i2spr::W, + i2s_clock: u32, + request_freq: u32, + mclk: bool, + data_format: DataFormat, +) { + let coef = _coef(mclk, data_format); + let division = i2s_clock / (coef * request_freq); + let rem = i2s_clock / (coef * request_freq); + if rem == 0 && division >= 4 && division <= 511 { + let odd = (division & 1) == 1; + let div = (division >> 1) as u8; + _set_prescaler(w, odd, div); + } else { + panic!("Cannot reach exactly the required frequency") + }; +} + +// see _set_request_frequency for explanation +fn _coef(mclk: bool, data_format: DataFormat) -> u32 { + if mclk { + return 256; + } + if let DataFormat::Data16Channel16 = data_format { + 32 + } else { + 64 + } +} + +impl I2sDriverConfig { + /// Instantiate the driver by wrapping the given [`I2sPeripheral`]. + /// + /// # Panics + /// + /// This method panics if an exact frequency is required and that frequency cannot be set. + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { + let driver = I2sDriver:: { + i2s_peripheral, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + }; + driver.registers().cr1.reset(); // ensure SPI is disabled + driver.registers().cr2.reset(); // disable interrupt and DMA request + driver.registers().i2scfgr.write(|w| { + w.i2smod().i2smode(); + match (self.slave_or_master, self.transmit_or_receive) { + (SlaveOrMaster::Slave, TransmitOrReceive::Transmit) => w.i2scfg().slave_tx(), + (SlaveOrMaster::Slave, TransmitOrReceive::Receive) => w.i2scfg().slave_rx(), + (SlaveOrMaster::Master, TransmitOrReceive::Transmit) => w.i2scfg().master_tx(), + (SlaveOrMaster::Master, TransmitOrReceive::Receive) => w.i2scfg().master_rx(), + }; + match self.standard { + I2sStandard::Philips => w.i2sstd().philips(), + I2sStandard::Msb => w.i2sstd().msb(), + I2sStandard::Lsb => w.i2sstd().lsb(), + I2sStandard::PcmShortSync => w.i2sstd().pcm().pcmsync().short(), + I2sStandard::PcmLongSync => w.i2sstd().pcm().pcmsync().long(), + }; + match self.data_format { + DataFormat::Data16Channel16 => w.datlen().sixteen_bit().chlen().sixteen_bit(), + DataFormat::Data16Channel32 => w.datlen().sixteen_bit().chlen().thirty_two_bit(), + DataFormat::Data24Channel32 => { + w.datlen().twenty_four_bit().chlen().thirty_two_bit() + } + DataFormat::Data32Channel32 => w.datlen().thirty_two_bit().chlen().thirty_two_bit(), + }; + w + }); + driver.registers().i2spr.write(|w| { + w.mckoe().bit(self.master_clock); + match self.frequency { + Frequency::Prescaler(odd, div) => _set_prescaler(w, odd, div), + Frequency::Request(freq) => _set_request_frequency( + w, + driver.i2s_peripheral.i2s_freq(), + freq, + self.master_clock, + self.data_format, + ), + Frequency::Require(freq) => _set_require_frequency( + w, + driver.i2s_peripheral.i2s_freq(), + freq, + self.master_clock, + self.data_format, + ), + } + w + }); + driver + } +} + +impl Default for I2sDriverConfig { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl I2sDriverConfig { + /// Configure driver in transmit mode + pub fn transmit(self) -> I2sDriverConfig { + I2sDriverConfig:: { + slave_or_master: self.slave_or_master, + transmit_or_receive: TransmitOrReceive::Transmit, + standard: self.standard, + clock_polarity: self.clock_polarity, + data_format: self.data_format, + master_clock: self.master_clock, + frequency: self.frequency, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } + /// Configure driver in receive mode + pub fn receive(self) -> I2sDriverConfig { + I2sDriverConfig:: { + slave_or_master: self.slave_or_master, + transmit_or_receive: TransmitOrReceive::Receive, + standard: self.standard, + clock_polarity: self.clock_polarity, + data_format: self.data_format, + master_clock: self.master_clock, + frequency: self.frequency, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } + /// Select the I2s standard to use + #[allow(non_camel_case_types)] + pub fn standard(self, _standard: NEW_STD) -> I2sDriverConfig + where + NEW_STD: marker::I2sStandard, + { + I2sDriverConfig:: { + slave_or_master: self.slave_or_master, + transmit_or_receive: self.transmit_or_receive, + standard: NEW_STD::VALUE, + clock_polarity: self.clock_polarity, + data_format: self.data_format, + master_clock: self.master_clock, + frequency: self.frequency, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } + /// Select steady state clock polarity + // datasheet don't precise how it affect I2s operation. In particular, this may meaningless for + // slave operation. + pub fn clock_polarity(mut self, polarity: ClockPolarity) -> Self { + self.clock_polarity = polarity; + self + } + + /// Select data format + pub fn data_format(mut self, format: DataFormat) -> Self { + self.data_format = format; + self + } + + /// Convert to a slave configuration. This delete Master Only Settings. + pub fn to_slave(self) -> I2sDriverConfig { + let Self { + transmit_or_receive, + standard, + clock_polarity, + data_format, + .. + } = self; + I2sDriverConfig:: { + slave_or_master: SlaveOrMaster::Slave, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock: false, + frequency: Frequency::Prescaler(false, 0b10), + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } + + /// Convert to a master configuration. + pub fn to_master(self) -> I2sDriverConfig { + let Self { + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + .. + } = self; + I2sDriverConfig:: { + slave_or_master: SlaveOrMaster::Master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } +} + +impl I2sDriverConfig { + /// Enable/Disable Master Clock. Affect the effective sampling rate. + /// + /// This can be only set and only have meaning for Master mode. + pub fn master_clock(mut self, enable: bool) -> Self { + self.master_clock = enable; + self + } + + /// Configure audio frequency by setting the prescaler with an odd factor and a divider. + /// + /// The effective sampling frequency is: + /// - `i2s_clock / [256 * ((2 * div) + odd)]` when master clock is enabled + /// - `i2s_clock / [(channel_length * 2) * ((2 * div) + odd)]` when master clock is disabled + /// + /// `i2s_clock` is I2S clock source frequency, and `channel_length` is width in bits of the + /// channel (see [DataFormat]) + /// + /// This setting only have meaning and can be only set for master. + /// + /// # Panics + /// + /// `div` must be at least 2, otherwise the method panics. + pub fn prescaler(mut self, odd: bool, div: u8) -> Self { + #[allow(clippy::manual_range_contains)] + if div < 2 { + panic!("div is less than 2, forbidden value") + } + self.frequency = Frequency::Prescaler(odd, div); + self + } + + /// Request an audio sampling frequency. The effective audio sampling frequency may differ. + pub fn request_frequency(mut self, freq: u32) -> Self { + self.frequency = Frequency::Request(freq); + self + } + + /// Require exactly this audio sampling frequency. + /// + /// If the required frequency can not bet set, Instantiate the driver will panics. + pub fn require_frequency(mut self, freq: u32) -> Self { + self.frequency = Frequency::Require(freq); + self + } +} + +/// Driver of a SPI peripheral in I2S mode. +/// +/// Meant for advanced usage, for example using interrupt or DMA. +pub struct I2sDriver { + i2s_peripheral: I, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, +} + +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Returns a reference to the register block + fn registers(&self) -> &RegisterBlock { + unsafe { &*(I::REGISTERS as *const RegisterBlock) } + } +} + +/// Constructors and Destructors +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Instantiate an i2s driver from an [`I2sPeripheral`] object and a configuration. + /// + /// # Panics + /// + /// This method panics if an exact frequency is required by the configuration and that + /// frequency can not be set. + pub fn new(i2s_peripheral: I, config: I2sDriverConfig) -> Self { + config.i2s_driver(i2s_peripheral) + } + + /// Destroy the driver, release the owned i2s device and reset it's configuration. + pub fn release(self) -> I { + let registers = self.registers(); + registers.cr1.reset(); + registers.cr2.reset(); + registers.i2scfgr.reset(); + registers.i2spr.reset(); + self.i2s_peripheral + } + + /// Consume the driver and create a new one with the given configuration. + #[allow(non_camel_case_types)] + pub fn reconfigure( + self, + config: I2sDriverConfig, + ) -> I2sDriver { + let i2s_peripheral = self.i2s_peripheral; + config.i2s_driver(i2s_peripheral) + } +} + +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Get a reference to the underlying i2s device + pub fn i2s_peripheral(&self) -> &I { + &self.i2s_peripheral + } + + /// Get a mutable reference to the underlying i2s device + pub fn i2s_peripheral_mut(&mut self) -> &mut I { + &mut self.i2s_peripheral + } + + /// Enable the I2S peripheral. + pub fn enable(&mut self) { + self.registers().i2scfgr.modify(|_, w| w.i2se().enabled()); + } + + /// Immediately Disable the I2S peripheral. + /// + /// It's up to the caller to not disable the peripheral in the middle of a frame. + pub fn disable(&mut self) { + self.registers().i2scfgr.modify(|_, w| w.i2se().disabled()); + } + + /// Return `true` if the level on the WS line is high. + pub fn ws_is_high(&self) -> bool { + self.i2s_peripheral.ws_is_high() + } + + /// Return `true` if the level on the WS line is low. + pub fn ws_is_low(&self) -> bool { + self.i2s_peripheral.ws_is_low() + } + + //TODO(maybe) method to get a handle to WS pin. It may useful for setting an interrupt on pin to + //synchronise I2s in slave mode +} + +/// Status +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Get the content of the status register. It's content may modified during the operation. + /// + /// When reading the status register, the hardware may reset some error flag of it. The way + /// each flag can be modified is documented on each [Status] flag getter. + pub fn status(&mut self) -> Status { + Status:: { + value: self.registers().sr.read(), + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, + } + } +} + +/// Transmit only methods +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Write a raw half word to the Tx buffer and delete the TXE flag in status register. + /// + /// It's up to the caller to write the content when it's empty. + pub fn write_data_register(&mut self, value: u16) { + self.registers().dr.write(|w| w.dr().bits(value)); + } + + /// When set to `true`, an interrupt is generated each time the Tx buffer is empty. + pub fn set_tx_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.txeie().bit(enabled)) + } + + /// When set to `true`, a dma request is generated each time the Tx buffer is empty. + pub fn set_tx_dma(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.txdmaen().bit(enabled)) + } +} + +/// Receive only methods +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Read a raw value from the Rx buffer and delete the RXNE flag in status register. + pub fn read_data_register(&mut self) -> u16 { + self.registers().dr.read().dr().bits() + } + + /// When set to `true`, an interrupt is generated each time the Rx buffer contains a new data. + pub fn set_rx_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.rxneie().bit(enabled)) + } + + /// When set to `true`, a dma request is generated each time the Rx buffer contains a new data. + pub fn set_rx_dma(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.rxdmaen().bit(enabled)) + } +} + +/// Error interrupt, Master Receive Mode. +impl I2sDriver +where + I: I2sPeripheral, +{ + /// When set to `true`, an interrupt is generated each time an error occurs. + /// + /// Not available for Master Transmit because no error can occur in this mode. + pub fn set_error_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.errie().bit(enabled)) + } +} + +/// Error interrupt, Slave Mode. +impl I2sDriver +where + I: I2sPeripheral, +{ + /// When set to `true`, an interrupt is generated each time an error occurs. + /// + /// Not available for Master Transmit because no error can occur in this mode. + pub fn set_error_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.errie().bit(enabled)) + } +} + +/// Sampling Rate +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Get the actual sample rate imposed by the driver. + /// + /// This allow to check deviation with a requested frequency. + pub fn sample_rate(&self) -> u32 { + let i2spr = self.registers().i2spr.read(); + let mckoe = i2spr.mckoe().bit(); + let odd = i2spr.odd().bit(); + let div = i2spr.i2sdiv().bits(); + let i2s_freq = self.i2s_peripheral.i2s_freq(); + if mckoe { + i2s_freq / (256 * ((2 * div as u32) + odd as u32)) + } else { + match self.registers().i2scfgr.read().chlen().bit() { + false => i2s_freq / ((16 * 2) * ((2 * div as u32) + odd as u32)), + true => i2s_freq / ((32 * 2) * ((2 * div as u32) + odd as u32)), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_div_round() { + let fracs = [(1, 2), (2, 2), (1, 3), (2, 3), (2, 4), (3, 5), (9, 2)]; + for (n, d) in fracs { + let res = div_round(n, d); + let check = f32::round((n as f32) / (d as f32)) as u32; + assert_eq!(res, check); + } + } +} diff --git a/src/ehal.rs b/src/ehal.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/format.rs b/src/format.rs deleted file mode 100644 index c9d3041..0000000 --- a/src/format.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! I2S frame formats - -mod data24frame32; - -use crate::pac::spi1::i2scfgr::{CHLEN_A, DATLEN_A}; -use crate::pac::spi1::RegisterBlock; - -pub use self::data24frame32::Data24Frame32; - -/// I2S communication frame format standards -#[derive(Debug, Clone)] -pub enum FrameFormat { - /// Philips I2S - PhilipsI2s, - /// Bits justified with the most significant bit aligned to the beginning of the frame, - /// potentially with unused bits at the end - MsbJustified, - /// Bits justified with the least significant bit aligned to the end of the frame, - /// potentially with unused bits at the beginning - LsbJustified, - /// Pulse code modulation - Pcm(FrameSync), -} - -/// PCM frame synchronization modes -#[derive(Debug, Clone)] -pub enum FrameSync { - /// WS pulses high just before the most significant bit of each sample - Short, - /// WS is high for the first 13 bits of each sample - Long, -} - -/// A supported audio sample format -/// -/// This trait provides the sample type only. -pub trait DataFormatType { - /// The type used to represent a sample in memory - type Sample: Copy; -} - -mod detail { - use super::{DataFormatType, FrameFormat}; - use crate::pac::spi1::i2scfgr::{CHLEN_A, DATLEN_A}; - use crate::pac::spi1::RegisterBlock; - /// A supported audio format (private implementation details) - pub trait DataFormatDetail: DataFormatType { - /// Size of audio samples in memory (DATLEN field of SPI_I2SCFGR) - const DATLEN: DATLEN_A; - /// Number of bits used on wire for each sample (CHLEN field of SPI_I2SCFGR) - const CHLEN: CHLEN_A; - /// Reads one sample from the data register and returns it as a frame value - /// - /// When using a 24-bit or 32-bit data format, this function blocks until the both parts - /// of the sample have been received. - fn read_sample( - format: &FrameFormat, - registers: &RegisterBlock, - ) -> ::Sample; - /// Writes one frame to the data register as a sample - /// - /// When this function is called, the TXE bit of the status register must be set. - /// - /// When using a 24-bit or 32-bit data format, this function blocks until the first part - /// of the sample has been transmitted. - fn write_sample( - format: &FrameFormat, - registers: &RegisterBlock, - sample: ::Sample, - ); - } -} -use self::detail::DataFormatDetail; - -/// A supported audio sample format -/// -/// This trait inherits from DataFormatType (indirectly) so that the Sample type is public -/// but the other trait items are private. -pub trait DataFormat: DataFormatDetail {} - -// Utility functions - -/// Writes a sample to the data register -fn write_one_step(registers: &RegisterBlock, value: u16) { - registers.dr.write(|w| w.dr().bits(value)); -} -/// Writes one sample to the data register, waits for the sample to be transmitted, -/// and writes the second sample to the data register -fn write_two_steps(registers: &RegisterBlock, values: [u16; 2]) { - registers.dr.write(|w| w.dr().bits(values[0])); - // Wait for the register to become empty again - while registers.sr.read().txe().is_not_empty() {} - registers.dr.write(|w| w.dr().bits(values[1])); -} - -/// Reads two consecutive samples from the data register, waiting for the second sample to appear -fn read_two_steps(registers: &RegisterBlock) -> [u16; 2] { - let value1 = registers.dr.read().dr().bits(); - while registers.sr.read().rxne().is_empty() {} - let value2 = registers.dr.read().dr().bits(); - [value1, value2] -} - -/// Reads a sample from the data register -fn read_one_step(registers: &RegisterBlock) -> u16 { - registers.dr.read().dr().bits() -} - -/// 16 bits transferred for each audio sample, represented in memory with 16 bits per sample -#[derive(Debug, Clone)] -pub struct Data16Frame16; - -impl DataFormat for Data16Frame16 {} -impl DataFormatType for Data16Frame16 { - type Sample = i16; -} -impl DataFormatDetail for Data16Frame16 { - const DATLEN: DATLEN_A = DATLEN_A::SIXTEENBIT; - const CHLEN: CHLEN_A = CHLEN_A::SIXTEENBIT; - - fn read_sample(_format: &FrameFormat, registers: &RegisterBlock) -> i16 { - // Just one 16-bit read - read_one_step(registers) as i16 - } - - fn write_sample(_format: &FrameFormat, registers: &RegisterBlock, sample: i16) { - // Just one 16-bit write - write_one_step(registers, sample as u16); - } -} - -/// 32 bits transferred for each audio sample, represented in memory with 16 bits per sample -/// -/// When receiving, the 16 least significant bits are ignored. When transmitting, the sample -/// is sent in the 16 most significant bits and the other 16 bits are sent as zeros. -#[derive(Debug, Clone)] -pub struct Data16Frame32; - -impl DataFormat for Data16Frame32 {} -impl DataFormatType for Data16Frame32 { - type Sample = i16; -} -impl DataFormatDetail for Data16Frame32 { - const DATLEN: DATLEN_A = DATLEN_A::SIXTEENBIT; - const CHLEN: CHLEN_A = CHLEN_A::THIRTYTWOBIT; - - fn read_sample(_format: &FrameFormat, registers: &RegisterBlock) -> i16 { - // Just one read - read_one_step(registers) as i16 - } - - fn write_sample(_format: &FrameFormat, registers: &RegisterBlock, sample: i16) { - // Just one write - write_one_step(registers, sample as u16); - } -} - -/// 32 bits in each audio sample, represented in memory with 32 bits per sample -#[derive(Debug, Clone)] -pub struct Data32Frame32; - -impl DataFormat for Data32Frame32 {} -impl DataFormatType for Data32Frame32 { - type Sample = i32; -} -impl DataFormatDetail for Data32Frame32 { - const DATLEN: DATLEN_A = DATLEN_A::THIRTYTWOBIT; - const CHLEN: CHLEN_A = CHLEN_A::THIRTYTWOBIT; - - fn read_sample(_format: &FrameFormat, registers: &RegisterBlock) -> i32 { - // Two reads, most significant half first - let [msbs, lsbs] = read_two_steps(registers); - ((u32::from(msbs) << 16) | u32::from(lsbs)) as i32 - } - - fn write_sample(_format: &FrameFormat, registers: &RegisterBlock, sample: i32) { - // Two writes, most significant half first - let msbs = ((sample as u32) >> 16) as u16; - let lsbs = sample as u16; - write_two_steps(registers, [msbs, lsbs]); - } -} diff --git a/src/format/data24frame32.rs b/src/format/data24frame32.rs deleted file mode 100644 index 296132b..0000000 --- a/src/format/data24frame32.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! 24-bit data format in a 32-bit frame - -use super::detail::DataFormatDetail; -use super::{read_two_steps, write_two_steps, DataFormat, DataFormatType, FrameFormat}; -use crate::pac::spi1::i2scfgr::{CHLEN_A, DATLEN_A}; -use crate::pac::spi1::RegisterBlock; - -/// 32 bits transferred for each audio sample, represented in memory with 24 bits per sample -/// -/// # Receiving -/// -/// When receiving, the 8 least significant bits are ignored. Each received sample will be sign- -/// extended to 32 bits. -/// -/// ## Example (I2S, MSB justified, or PCM) -/// -/// Bits on wire: `1000 1110 1010 1010 0011 0011 xxxx xxxx` -/// -/// Received 32-bit sample in memory: `1111 1111 1000 1110 1010 1010 0011 0011` (`0xff8eaa33`) -/// -/// ## Example (LSB justified) -/// -/// Bits on wire: `xxxx xxxx 1000 1110 1010 1010 0011 0011` -/// -/// Received 32-bit sample in memory: `1111 1111 1000 1110 1010 1010 0011 0011` (`0xff8eaa33`) -/// -/// # Transmitting -/// -/// When transmitting, the 8 most significant bits of each 32-bit sample are ignored. The final -/// 8 bits sent on the wire will all be zero. -/// -/// ## Example (I2S, MSB justified, or PCM) -/// -/// 32-bit sample in memory: `xxxx xxxx 1000 1110 1010 1010 0011 0011` (8 most significant bits -/// can be anything, other 24 bits are 0x8eaa33) -/// -/// Bits on wire: `1000 1110 1010 1010 0011 0011 0000 0000` -/// -/// ## Example (LSB justified) -/// -/// 32-bit sample in memory: `xxxx xxxx 1000 1110 1010 1010 0011 0011` (8 most significant bits -/// can be anything, other 24 bits are 0x8eaa33) -/// -/// Bits on wire: `0000 0000 1000 1110 1010 1010 0011 0011` -/// -/// -#[derive(Debug, Clone)] -pub struct Data24Frame32; - -impl DataFormat for Data24Frame32 {} -impl DataFormatType for Data24Frame32 { - type Sample = i32; -} -impl DataFormatDetail for Data24Frame32 { - const DATLEN: DATLEN_A = DATLEN_A::TWENTYFOURBIT; - const CHLEN: CHLEN_A = CHLEN_A::THIRTYTWOBIT; - - fn read_sample(format: &FrameFormat, registers: &RegisterBlock) -> i32 { - match format { - FrameFormat::LsbJustified => build_sample_lsb_justified(read_two_steps(registers)), - FrameFormat::PhilipsI2s | FrameFormat::MsbJustified | FrameFormat::Pcm(_) => { - build_sample_msb_justified(read_two_steps(registers)) - } - } - } - - fn write_sample(format: &FrameFormat, registers: &RegisterBlock, sample: i32) { - match format { - FrameFormat::LsbJustified => { - write_two_steps(registers, split_sample_lsb_justified(sample)); - } - FrameFormat::PhilipsI2s | FrameFormat::MsbJustified | FrameFormat::Pcm(_) => { - write_two_steps(registers, split_sample_msb_justified(sample)); - } - } - } -} - -/// Builds a sample from two data register reads for I2S, MSB justified, or PCM formats -fn build_sample_msb_justified(values: [u16; 2]) -> i32 { - // Read 1 has the two middle bytes, read 2 has the least significant byte followed by an unspecified value - let read1 = values[0]; - let read2 = values[1] & 0xff00; - let sample = (u32::from(read1) << 8) | (u32::from(read2) >> 8); - sign_extend_24_to_32(sample) as i32 -} - -/// Builds a sample from two data register reads for LSB justified format -fn build_sample_lsb_justified(values: [u16; 2]) -> i32 { - // Read 1 has the most significant bytes, read 2 has the other two bytes - let read1 = values[0] & 0x00ff; - let read2 = values[1]; - let sample = (u32::from(read1) << 16) | u32::from(read2); - sign_extend_24_to_32(sample) as i32 -} - -/// Sign-extends a 24-bit integer into 32 bits -fn sign_extend_24_to_32(value: u32) -> u32 { - if ((value >> 23) & 1) == 1 { - 0xff000000 | value - } else { - value - } -} - -/// Splits a 32-bit sample into two data register writes for I2S, MSB justified, or PCM formats -fn split_sample_msb_justified(sample: i32) -> [u16; 2] { - let sample = sample as u32; - // Write 1 has the two middle bytes, write 2 has the least significant byte followed by 0x00 - let write1 = (sample >> 8) as u16; - let write2 = ((sample & 0xff) << 8) as u16; - [write1, write2] -} - -/// Splits a 32-bit sample into two data register writes for LSB justified format -fn split_sample_lsb_justified(sample: i32) -> [u16; 2] { - let sample = sample as u32; - // Write 1 has 0x00 and the most significant byte, write 2 has the two least significant bytes - let write1 = ((sample >> 16) & 0xff) as u16; - let write2 = sample as u16; - [write1, write2] -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn build_msb() { - assert_eq!( - 0x003478ae_u32 as i32, - build_sample_msb_justified([0x3478, 0xae00]) - ); - assert_eq!( - 0xff8eaa33_u32 as i32, - build_sample_msb_justified([0x8eaa, 0x3300]) - ); - } - - #[test] - fn build_lsb() { - assert_eq!( - 0x003478ae_u32 as i32, - build_sample_lsb_justified([0x0034, 0x78ae]) - ); - assert_eq!( - 0xff8eaa33_u32 as i32, - build_sample_lsb_justified([0x008e, 0xaa33]) - ); - } - - #[test] - fn split_msb() { - assert_eq!( - [0x3478, 0xae00], - split_sample_msb_justified(0x003478ae_u32 as i32) - ); - assert_eq!( - [0x8eaa, 0x3300], - split_sample_msb_justified(0xff8eaa33_u32 as i32) - ); - assert_eq!( - [0x8eaa, 0x3300], - split_sample_msb_justified(0x008eaa33_u32 as i32) - ); - } - - #[test] - fn split_lsb() { - assert_eq!( - [0x0034, 0x78ae], - split_sample_lsb_justified(0x003478ae_u32 as i32) - ); - assert_eq!( - [0x008e, 0xaa33], - split_sample_lsb_justified(0xff8eaa33_u32 as i32) - ); - assert_eq!( - [0x008e, 0xaa33], - split_sample_lsb_justified(0x008eaa33_u32 as i32) - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index 2c75124..a921bf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,59 +1,53 @@ //! This library supports I2S communication for SPI version 1.2 (on STM32F1, STM32F2, STM32F4, //! STM32L0, and STM32L1 microcontrollers). //! -//! This library is normally used with a HAL library that provides a type that implements -//! [Instance](crate::Instance). An [I2s](crate::I2s) object can be created around the Instance -//! object and used for I2S. - +//! This library is normally used through a MCU HAL library providing types that implement +//! [`I2sPeripheral`]. [`I2sDriver`](driver::I2sDriver) or [`I2sTransfer`](transfer::I2sTransfer) +//! objects can be created around I2sPeripheral object and used for I2S communication. +//! +//! # For stm32 MCU HAL implementers +//! +//! To support I2s by using this library, HAL implementers must implements [`I2sPeripheral`] trait +//! and reexport this crate. It's also recommended to create some example. For reference, +//! implementation and examples are (or will be soon) available in stm32f4xx-hal. +//! +//! # For i2s users +//! +//! For fine control and advanced usage, look [driver] module. For quick and basic usage, look +//! [transfer] module. +//! +//! # Issues and limitations +//! +//! - In master mode, there is currently no way to reset clock phase. +//! - In master transmit mode, the CHSIDE flag appear to be sporadically wrong +//! +//! As consequence : +//! - for driver in master transmit, once driver has been disabled, it's may impossible to +//! reliably know what is the next part to transmit. +//! - for driver in master receive, this information can be recovered using CHSIDE flag. However, +//! this doesn't work with PCM standard. +//! - Once a transfer in master transmit mode have been disabled, it will work incorrectly until next +//! MCU reset. +//! - master receive transfer is not implemented for PCM. +//! +//! #![no_std] -extern crate nb; -extern crate vcell; - -mod config; -pub mod format; mod pac; -use core::convert::Infallible; -use core::marker::PhantomData; - -pub use self::config::{MasterConfig, SlaveConfig, MasterClock}; -pub use self::pac::spi1::RegisterBlock; -use crate::format::{DataFormat, FrameFormat, FrameSync}; -use crate::pac::spi1::i2scfgr::I2SCFG_A; - -/// Clock polarity -#[derive(Debug, Clone)] -pub enum Polarity { - /// Clock low when idle - IdleLow, - /// Clock high when idle - IdleHigh, -} - -/// The channel associated with a sample -#[derive(Debug, Clone, PartialEq)] -pub enum Channel { - /// Left channel (word select low) - Left, - /// Right channel (word select high) - Right, -} +pub mod driver; +pub mod marker; +pub mod transfer; -/// Events with associated interrupts that can be enabled -pub enum Event { - /// The transmit data register is empty, and a sample can be written - TransmitEmtpy, - /// The receive data register is not empty, and a sample can be read - ReceiveNotEmpty, - /// An error has occurred - Error, +mod sealed { + pub trait Sealed {} } -/// An SPI peripheral instance that can be used for I2C communication +/// An object composed of a SPI device that can be used for I2S communication. /// -/// This trait is meant to be implemented for a HAL-specific type that represent ownership of -/// the SPI peripheral (and any pins required by it, although that is entirely up to the HAL). +/// This trait is meant to be implemented on a type that represent a full SPI device, that means an +/// object composed of a SPI peripheral, pins used by it, and eventually a clock object (can be a +/// reference). /// /// # Safety /// @@ -61,605 +55,17 @@ pub enum Event { /// /// * The implementing type has ownership of the peripheral, preventing any other accesses to the /// register block. -/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed for as +/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed as /// long as ownership or a borrow of the implementing type is present. -pub unsafe trait Instance { +pub unsafe trait I2sPeripheral { /// Pointer to the SPI register block - const REGISTERS: *mut RegisterBlock; -} - -/// Interface to an SPI peripheral in I2S mode -/// -/// # Basic sequence of operations -/// -/// * Create an I2s object -/// * Configure it in the desired mode (master/slave, transmit/receive) -/// * Enable interrupts and DMA (optional) -/// * Enable -/// * Transmit or receive samples -/// * Disable -/// * Deconfigure the I2s, allowing it to be configured again differently -/// -/// # Example -/// -/// ```no_run -/// # use stm32_i2s::{I2s, Instance, MasterConfig, InitMode, Polarity, MasterClock}; -/// # use stm32_i2s::format::{Data16Frame16, FrameFormat}; -/// fn use_i2s(i2s: I2s) where I: Instance { -/// let config = MasterConfig::with_division( -/// 25, -/// Data16Frame16, -/// FrameFormat::PhilipsI2s, -/// Polarity::IdleHigh, -/// MasterClock::Disble, -/// ); -/// let mut i2s_configured = i2s.configure_master_receive(config); -/// let mut samples: [i16; 64] = [0; 64]; -/// i2s_configured.enable(); -/// i2s_configured.receive_blocking(&mut samples); -/// i2s_configured.disable(); -/// } -/// ``` -/// -pub struct I2s { - instance: I, - frame_format: FrameFormat, - _mode: PhantomData, -} - -/// Marker for initialization mode -pub struct InitMode; -/// Marker for transmit mode -/// -/// F is the data format -pub struct TransmitMode(F); -/// Marker for receive mode -/// -/// F is the data format -pub struct ReceiveMode(F); - -mod sealed { - pub trait Sealed {} -} -use self::sealed::Sealed; - -/// A mode in which the I2S is configured and may be enabled (transmit or receive) -pub trait ActiveMode: Sealed {} -impl Sealed for TransmitMode {} -impl ActiveMode for TransmitMode {} -impl Sealed for ReceiveMode {} -impl ActiveMode for ReceiveMode {} - -impl I2s -where - I: Instance, -{ - /// Returns a reference to the enclosed peripheral instance - pub fn instance(&self) -> &I { - &self.instance - } - /// Returns a mutable reference to the enclosed peripheral instance - pub fn instance_mut(&mut self) -> &mut I { - &mut self.instance - } - - /// Returns a reference to the register block - fn registers(&self) -> &RegisterBlock { - unsafe { &*I::REGISTERS } - } - - /// Enables the I2S peripheral - fn common_enable(&self) { - self.registers().i2scfgr.modify(|_, w| w.i2se().enabled()); - } - - /// Disables the I2S peripheral - fn common_disable(&self) { - self.registers().i2scfgr.modify(|_, w| w.i2se().disabled()); - } - - /// Resets the values of all control and configuration registers - fn reset_registers(&self) { - let registers = self.registers(); - registers.cr1.reset(); - registers.cr2.reset(); - registers.i2scfgr.reset(); - registers.i2spr.reset(); - } -} - -impl I2s -where - I: Instance, -{ - /// Creates a wrapper around an instance, but does not do any configuration - pub fn new(instance: I) -> Self { - I2s { - instance, - // Default frame format (the real value will be filled in during configuration) - frame_format: FrameFormat::PhilipsI2s, - _mode: PhantomData, - } - } - - /// Configures the SPI peripheral in master transmit mode - pub fn configure_master_transmit(self, config: MasterConfig) -> I2s> - where - F: DataFormat, - { - self.configure_clock_division(config.division, config.master_clock); - self.configure_i2s( - I2SCFG_A::MASTERTX, - config.data_format, - &config.frame_format, - config.polarity, - ); - I2s { - instance: self.instance, - frame_format: config.frame_format, - _mode: PhantomData, - } - } - - /// Configures the SPI peripheral in master receive mode - pub fn configure_master_receive(self, config: MasterConfig) -> I2s> - where - F: DataFormat, - { - self.configure_clock_division(config.division, config.master_clock); - self.configure_i2s( - I2SCFG_A::MASTERRX, - config.data_format, - &config.frame_format, - config.polarity, - ); - I2s { - instance: self.instance, - frame_format: config.frame_format, - _mode: PhantomData, - } - } - - /// Configures the SPI peripheral in slave transmit mode - pub fn configure_slave_transmit(self, config: SlaveConfig) -> I2s> - where - F: DataFormat, - { - self.configure_i2s( - I2SCFG_A::SLAVETX, - config.data_format, - &config.frame_format, - config.polarity, - ); - I2s { - instance: self.instance, - frame_format: config.frame_format, - _mode: PhantomData, - } - } - - /// Configures the SPI peripheral in slave receive mode - pub fn configure_slave_receive(self, config: SlaveConfig) -> I2s> - where - F: DataFormat, - { - self.configure_i2s( - I2SCFG_A::SLAVERX, - config.data_format, - &config.frame_format, - config.polarity, - ); - I2s { - instance: self.instance, - frame_format: config.frame_format, - _mode: PhantomData, - } - } - - /// Sets the SPI peripheral to I2S mode and applies other settings to the SPI_CR2 register - /// - /// This does not modify any other registers, so it preserves interrupts and DMA setup. - fn configure_i2s( - &self, - mode: I2SCFG_A, - _data_format: F, - frame_format: &FrameFormat, - polarity: Polarity, - ) where - F: DataFormat, - { - use self::pac::spi1::i2scfgr::{CKPOL_A, I2SSTD_A, PCMSYNC_A}; - let polarity = match polarity { - Polarity::IdleLow => CKPOL_A::IDLELOW, - Polarity::IdleHigh => CKPOL_A::IDLEHIGH, - }; - let (i2sstd, pcmsync) = match frame_format { - FrameFormat::PhilipsI2s => (I2SSTD_A::PHILIPS, PCMSYNC_A::SHORT), - FrameFormat::MsbJustified => (I2SSTD_A::MSB, PCMSYNC_A::SHORT), - FrameFormat::LsbJustified => (I2SSTD_A::LSB, PCMSYNC_A::SHORT), - FrameFormat::Pcm(FrameSync::Short) => (I2SSTD_A::PCM, PCMSYNC_A::SHORT), - FrameFormat::Pcm(FrameSync::Long) => (I2SSTD_A::PCM, PCMSYNC_A::LONG), - }; - self.registers().i2scfgr.write(|w| { - // Initially disabled (enable to actually start transferring data) - w.i2se() - .disabled() - .i2smod() - .i2smode() - .i2scfg() - .variant(mode) - .pcmsync() - .variant(pcmsync) - .i2sstd() - .variant(i2sstd) - .ckpol() - .variant(polarity) - .datlen() - .variant(F::DATLEN) - .chlen() - .variant(F::CHLEN) - }); - } - - fn configure_clock_division(&self, division: u16, master_clock: MasterClock) { - let master_clock_enable = matches!(master_clock, MasterClock::Enable); - - let spi = self.registers(); - let i2sdiv = division / 2; - let odd = division % 2; - assert!(i2sdiv >= 2 && i2sdiv <= 255); - spi.i2spr.write(|w| unsafe { - w.i2sdiv() - .bits(i2sdiv as u8) - .odd() - .bit(odd != 0) - .mckoe() - .bit(master_clock_enable) - }); - } -} - -/// # Transmit mode -/// -/// Both master and slave mode use the same functions to transmit. The only difference is where -/// the clock is generated. -/// -/// ## Slave transmit -/// -/// The I2S peripheral must be enabled and the first sample should be written to the transmit -/// register before the master starts sending clock and word select signals. -/// -/// ## Master transmit -/// -/// The first sample should be written to the transmit register just after the I2S peripheral is enabled. -/// Once the I2S peripheral is enabled, the first sample will be transmitted and the next sample -/// should be written to the transmit register. -/// -impl I2s> -where - I: Instance, - F: DataFormat, -{ - /// Returns the channel on which the next sample will be transmitted, or None if a previous - /// sample is still in the process of being transmitted - pub fn ready_to_transmit(&self) -> Option { - use self::pac::spi1::sr::CHSIDE_A; - let registers = self.registers(); - let sr = registers.sr.read(); - if sr.txe().is_empty() { - let channel = match sr.chside().variant() { - CHSIDE_A::LEFT => Channel::Left, - CHSIDE_A::RIGHT => Channel::Right, - }; - Some(channel) - } else { - // Not ready, channel not valid - None - } - } - - /// Writes a sample into the transmit buffer - /// - /// The I2S peripheral should normally be enabled before this function is called. However, - /// if the data format contains 16 bits, this function can be called once before enabling the - /// I2S to load the first sample. - /// - /// If the data format contains 24 or 32 bits, the sample will be split into two write - /// operations. This function will block until the second write has completed. - /// - pub fn transmit(&mut self, sample: F::Sample) -> nb::Result<(), Infallible> { - let registers = self.registers(); - let sr = registers.sr.read(); - if sr.txe().is_empty() { - F::write_sample(&self.frame_format, ®isters, sample); - Ok(()) - } else { - // Can't write yet - Err(nb::Error::WouldBlock) - } - } - - /// Transmits multiple samples, blocking until all samples have been transmitted - pub fn transmit_blocking(&mut self, samples: &[F::Sample]) { - for sample in samples { - nb::block!(self.transmit(*sample)).unwrap(); - } - } - - /// Writes a 16-bit value to the data register - /// - /// Like `transmit`, this function returns `Err(nb::Error::WouldBlock)` if the data register - /// contains a value that has not been transmitted yet. - /// - /// Unlike `transmit`, this function never blocks because it performs only one 16-bit write. - /// If the data format contains 24 or 32 bits, the calling code is responsible for dividing - /// each sample into two chunks and calling this function twice. Details about this can be found - /// in the microcontroller reference manual. - pub fn write_data_register(&mut self, value: u16) -> nb::Result<(), Infallible> { - let registers = self.registers(); - let sr = registers.sr.read(); - if sr.txe().is_empty() { - registers.dr.write(|w| w.dr().bits(value)); - Ok(()) - } else { - // Can't write yet - Err(nb::Error::WouldBlock) - } - } - - /// Checks for an error and clears the error flag - pub fn take_error(&mut self) -> Result<(), TransmitError> { - let spi = self.registers(); - // This read also clears the underrun flag - let sr = spi.sr.read(); - if sr.udr().is_underrun() { - Err(TransmitError::Underrun) - } else { - Ok(()) - } - } - - /// Enables or disables DMA requests for transmission - pub fn set_dma_enabled(&mut self, enabled: bool) { - self.registers().cr2.modify(|_, w| w.txdmaen().bit(enabled)); - } - - /// Enables the I2S peripheral - /// - /// In master mode, this will activate the word select and clock outputs and start sending - /// samples, with the left channel first. The first sample should be transmitted immediately - /// after enabling the I2S. - /// - /// In slave mode, this will cause the I2S peripheral to start responding to word select - /// and clock inputs from the master device. The first sample should be written to the data - /// register before the word select input goes low. - pub fn enable(&mut self) { - self.common_enable(); - } - - /// Disables the I2S peripheral - /// - /// To avoid stopping a transfer in the middle of a frame, this function returns WouldBlock - /// until the current transfer is finished. - pub fn disable(&mut self) -> nb::Result<(), Infallible> { - // "To switch off the I2S, by clearing I2SE, it is mandatory to wait for TXE = 1 and BSY = 0." - let sr = self.registers().sr.read(); - if sr.txe().is_empty() && sr.bsy().is_not_busy() { - self.common_disable(); - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - /// Returns the I2S to init mode, allowing it to be reconfigured - /// - /// This function resets all configuration options, including interrupts and DMA setup. - /// - /// If the I2S peripheral is enabled, this function will block until it has finished the - /// current transmission. - pub fn deconfigure(mut self) -> I2s { - nb::block!(self.disable()).unwrap(); - self.reset_registers(); - I2s { - instance: self.instance, - // Default frame format (the real value will be filled in during configuration) - frame_format: FrameFormat::PhilipsI2s, - _mode: PhantomData, - } - } -} - -/// # Receive mode -/// -/// Both master and slave mode use the same functions to receive. The only difference is where -/// the clock is generated. -/// -impl I2s> -where - I: Instance, - F: DataFormat, -{ - /// Enables the I2S peripheral - /// - /// In master mode, this will activate the word select and clock outputs and start receiving - /// samples, with the left channel first. The first sample will be available shortly - /// after enabling the I2S. - /// - /// In slave mode, this will cause the I2S peripheral to start responding to word select - /// and clock inputs from the master device. - pub fn enable(&mut self) { - self.common_enable(); - } - - /// If a sample has been read in and is ready to receive, this function returns the channel - /// it was received on. - pub fn sample_ready(&self) -> Option { - use crate::pac::spi1::sr::CHSIDE_A; - - let spi = self.registers(); - let sr = spi.sr.read(); - if sr.rxne().is_not_empty() { - let channel = match sr.chside().variant() { - CHSIDE_A::LEFT => Channel::Left, - CHSIDE_A::RIGHT => Channel::Right, - }; - Some(channel) - } else { - None - } - } - - /// Receives a sample from the data register, returning the sample and its associated channel - /// - /// If the data format contains 24 or 32 bits, the sample will be split into two read - /// operations. This function will block until the second read has completed. - pub fn receive(&mut self) -> nb::Result<(F::Sample, Channel), Infallible> { - match self.sample_ready() { - Some(channel) => { - let sample = F::read_sample(&self.frame_format, self.registers()); - Ok((sample, channel)) - } - None => Err(nb::Error::WouldBlock), - } - } - - /// Receives multiple samples, blocking until all samples have been received - /// - /// Samples from the left and right channels will be interleaved. - pub fn receive_blocking(&mut self, samples: &mut [F::Sample]) { - for sample_in_buffer in samples { - let (sample, _channel) = nb::block!(self.receive()).unwrap(); - *sample_in_buffer = sample; - } - } - - /// Reads a 16-bit value from the data register, returning the value and its associated channel - /// - /// Like `receive`, this function returns `Err(nb::Error::WouldBlock)` if the data register - /// does not contain a value. - /// - /// Unlike `receive`, this function never blocks because it performs only one 16-bit read. - /// If the data format contains 24 or 32 bits, the calling code is responsible for calling this - /// function twice and combining the two returned chunks into a sample. Details about this can - /// be found in the microcontroller reference manual. - pub fn read_data_register(&mut self) -> nb::Result<(u16, Channel), Infallible> { - match self.sample_ready() { - Some(channel) => { - let sample = self.registers().dr.read().dr().bits(); - Ok((sample, channel)) - } - None => Err(nb::Error::WouldBlock), - } - } - - /// Checks if an error has occurred, and clears the overrun error flag - pub fn take_error(&mut self) -> Result<(), ReceiveError> { - let spi = self.registers(); - let sr = spi.sr.read(); - let frame_error = sr.fre().is_error(); - let overrun = sr.ovr().is_overrun(); - if overrun { - // Clear flag by reading DR and then SR - let dr = spi.dr.read(); - let _ = spi.sr.read(); - if frame_error { - Err(ReceiveError::FrameAndOverrun(dr.dr().bits)) - } else { - Err(ReceiveError::Overrun(dr.dr().bits)) - } - } else if frame_error { - Err(ReceiveError::Frame) - } else { - Ok(()) - } - } - - /// Enables or disables DMA requests for reception - pub fn set_dma_enabled(&mut self, enabled: bool) { - self.registers().cr2.modify(|_, w| w.rxdmaen().bit(enabled)); - } - - /// Disables the I2S - /// - /// In master mode, this stops the clock, word select, and (if enabled) master clock outputs. - /// - /// Caution: Before disabling the I2S, a specific sequence of operations should be performed - /// so that the I2S peripheral does not stop in the middle of a frame. Refer to the target - /// microcontroller reference manual for more information. - pub fn disable(&mut self) { - self.common_disable(); - } - - /// Returns the I2S to init mode, allowing it to be reconfigured - /// - /// This function resets all configuration options, including interrupts and DMA setup. - /// - /// If the I2S peripheral is enabled, this function will disable it. - pub fn deconfigure(mut self) -> I2s { - self.disable(); - self.reset_registers(); - I2s { - instance: self.instance, - // Default frame format (the real value will be filled in during configuration) - frame_format: FrameFormat::PhilipsI2s, - _mode: PhantomData, - } - } -} - -/// # Common functions -/// -/// These interrupt functions can be used for transmission and reception. -impl I2s -where - I: Instance, - M: ActiveMode, -{ - /// Enables the interrupt signal output for an event - pub fn listen(&mut self, event: Event) { - self.registers().cr2.modify(|_, w| match event { - Event::TransmitEmtpy => w.txeie().not_masked(), - Event::ReceiveNotEmpty => w.rxneie().not_masked(), - Event::Error => w.errie().not_masked(), - }) - } - /// Disables the interrupt signal output for an event - pub fn unlisten(&mut self, event: Event) { - self.registers().cr2.modify(|_, w| match event { - Event::TransmitEmtpy => w.txeie().masked(), - Event::ReceiveNotEmpty => w.rxneie().masked(), - Event::Error => w.errie().masked(), - }) - } -} - -/// Errors that can occur when transmitting -#[derive(Debug)] -pub enum TransmitError { - /// The I2S peripheral needed to transmit a sample but no sample had been written - /// to the data register - /// - /// This indicates that at least one incorrect sample was transmitted - Underrun, -} - -/// Errors that can occur when receiving -#[derive(Debug)] -pub enum ReceiveError { - /// The I2S peripheral received a sample before software read the previous sample - /// - /// This indicates that at least one sample was lost. - /// - /// The enclosed value is the 16-bit value in the data register when overrun first happened. - /// Depending on the data format, this may be a full sample or just part of a sample. - /// The following samples have been discarded. - Overrun(u16), - /// The word select signal changed at an unexpected time (for slave mode only) - /// - /// If this error occurs, the I2S peripheral should be disabled and then re-enabled when - /// the word select signal is high. - Frame, - /// Both frame and overrun errors were detected - FrameAndOverrun(u16), + const REGISTERS: *const (); + /// Get I2s clock source frequency from the I2s device. + /// + /// Implementers are allowed to panic in case i2s source frequency is unavailable. + fn i2s_freq(&self) -> u32; + /// Return `true` if the level at WS pin is high. + fn ws_is_high(&self) -> bool; + /// Return `true` if the level at WS pin is low. + fn ws_is_low(&self) -> bool; } diff --git a/src/marker.rs b/src/marker.rs new file mode 100644 index 0000000..0e36028 --- /dev/null +++ b/src/marker.rs @@ -0,0 +1,144 @@ +//! Markers for [`I2sDriver`](crate::driver::I2sDriver), +//! [`I2sTransfer`](crate::transfer::I2sTransfer) and their respective configuration. +use crate::sealed::Sealed; + +/// Marker, indicated master mode. +#[derive(Debug, Clone, Copy)] +pub struct Master; + +/// Marker, indicate slave mode. +#[derive(Debug, Clone, Copy)] +pub struct Slave; + +/// Marker, indicated transmit mode. +#[derive(Debug, Clone, Copy)] +pub struct Transmit; + +/// Marker, indicate receive mode. +#[derive(Debug, Clone, Copy)] +pub struct Receive; + +/// Marker, indicate 16 bits data length on 16 bits wide channel. +#[derive(Debug, Clone, Copy)] +pub struct Data16Channel16; + +/// Marker, indicate 16 bits data length on 32 bits wide channel. +#[derive(Debug, Clone, Copy)] +pub struct Data16Channel32; + +/// Marker, indicate 24 bits data length on 32 bits wide channel. +#[derive(Debug, Clone, Copy)] +pub struct Data24Channel32; + +/// Marker, indicate 32 bits data length on 32 bits wide channel. +#[derive(Debug, Clone, Copy)] +pub struct Data32Channel32; + +/// Marker, indicate Philips I2S standard. +#[derive(Debug, Clone, Copy)] +pub struct Philips; + +/// Marker, indicate MSB Justified standard. +#[derive(Debug, Clone, Copy)] +pub struct Msb; + +/// Marker, indicate LSB Justified standard. +#[derive(Debug, Clone, Copy)] +pub struct Lsb; + +/// Marker, indicate PCM standard with short frame synchronisation. +#[derive(Debug, Clone, Copy)] +pub struct PcmShortSync; + +/// Marker, indicate PCM standard with long frame synchronisation. +#[derive(Debug, Clone, Copy)] +pub struct PcmLongSync; + +impl Sealed for Master {} +impl Sealed for Slave {} +impl Sealed for Transmit {} +impl Sealed for Receive {} +impl Sealed for Data16Channel16 {} +impl Sealed for Data16Channel32 {} +impl Sealed for Data24Channel32 {} +impl Sealed for Data32Channel32 {} +impl Sealed for Philips {} +impl Sealed for Msb {} +impl Sealed for Lsb {} +impl Sealed for PcmShortSync {} +impl Sealed for PcmLongSync {} + +/// Trait for marker indicating 16 bits data length, that is `Data16Channel16` and +/// `Data16Channel32` +pub trait Data16: Sealed {} +impl Data16 for Data16Channel16 {} +impl Data16 for Data16Channel32 {} + +/// Trait for marker indicating a DataFormat +pub trait DataFormat: Sealed { + /// Internal use only (used by configuration types). + const VALUE: crate::driver::DataFormat; + /// Audio frame representation from API point of view; + type AudioFrame: Default; +} + +macro_rules! impl_data_format{ + ($(($marker:ident,$audio_frame:ty)),*) => { + $( + impl DataFormat for $marker { + const VALUE: crate::driver::DataFormat = crate::driver::DataFormat::$marker; + type AudioFrame = $audio_frame; + } + )* + }; +} + +impl_data_format!( + (Data16Channel16, (i16, i16)), + (Data16Channel32, (i16, i16)), + (Data24Channel32, (i32, i32)), + (Data32Channel32, (i32, i32)) +); + +/// Trait for marker indicating a i2s standard. +pub trait I2sStandard: Sealed { + /// Internal use only (used by configuration types). + const VALUE: crate::driver::I2sStandard; + /// WS line level that make start the i2s device. `true` mean high level. + /// + /// Slave need to be enabled when WS line is **not** at this level. + const WS_START_LEVEL: bool; +} + +macro_rules! impl_i2s_standard{ + ($(($marker:ident,$ws_start_level:literal)),*) => { + $( + impl I2sStandard for $marker { + const VALUE: crate::driver::I2sStandard = crate::driver::I2sStandard::$marker; + const WS_START_LEVEL: bool = $ws_start_level; + } + )* + }; +} + +impl_i2s_standard!( + (Philips, false), + (Msb, true), + (Lsb, true), + (PcmShortSync, true), + (PcmLongSync, true) +); + +/// Marker trait for i2s standard where the CHSIDE status flag is a relevant channel indication +/// about data been received or to be transmitted. +pub trait ChannelFlag: I2sStandard + Sealed {} + +impl ChannelFlag for Philips {} +impl ChannelFlag for Msb {} +impl ChannelFlag for Lsb {} + +/// Marker trait for i2s standard where the CHSIDE status flag is meaningless. +pub trait NoChannelFlag: I2sStandard + Sealed {} + +impl NoChannelFlag for PcmShortSync {} +impl NoChannelFlag for PcmLongSync {} diff --git a/src/transfer.rs b/src/transfer.rs new file mode 100644 index 0000000..2c18c98 --- /dev/null +++ b/src/transfer.rs @@ -0,0 +1,1145 @@ +//! Abstraction to transfer I2S data. +//! +//! API of this module give abstractions allowing to transfer I2S audio data while hiding the +//! hardware details. This module also have basis to implement the upcoming embedded-hale I2s +//! trait. The job is mainly done by [`I2sTransfer`], a type that wrap an I2sPeripheral to control +//! it. +//! +//! At the moment, transfer is not implemented for 24 bits data and for PCM standard in master +//! receive mode. +//! +//! # Configure and instantiate transfer. +//! +//! [`I2sTransferConfig`] is used to create configuration of the i2s transfer: +//! ```no_run +//! # use stm32_i2s_v12x::transfer::*; +//! let transfer_config = I2sTransferConfig::new_master() +//! .receive() +//! .standard(Philips) +//! .data_format(Data16Channel32) +//! .master_clock(true) +//! .request_frequency(48_000); +//! ``` +//! Then you can instantiate the transfer around an `I2sPeripheral`: +//! ```ignore +//! // instantiate from configuration +//! let transfer = transfer_config.i2s_transfer(i2s_peripheral); +//! +//! // alternate way +//! let transfer = I2sTransfer::new(i2s_peripheral, transfer_config); +//! ``` +//! +//! # Transmitting data +//! +//! Transmitting data can be done with `write_iter` (blocking API) or `write` (non-blocking API) +//! +//! ```ignore +//! // Full scale sine wave spanning 32 samples. With a 48 kHz sampling rate this give a 1500 Hz +//! // signal. +//! const SINE_1500: [i16; 32] = [ +//! 0, 6392, 12539, 18204, 23169, 27244, 30272, 32137, 32767, 32137, 30272, 27244, 23169, +//! 18204, 12539, 6392, 0, -6392, -12539, -18204, -23169, -27244, -30272, -32137, -32767, +//! -32137, -30272, -27244, -23169, -18204, -12539, -6392, +//! ]; +//! +//! // Iterator generating audio data for 1 sec (at 48 kHz sampling rate) +//! let sine_1500_iter = SINE_1500 +//! .iter() +//! .map(|&x| (x, x)) +//! .cycle() +//! .take(48_000 as usize); +//! +//! // write_iter (blocking API) +//! i2s2_transfer.write_iter(sine_1500_iter.clone()); +//! +//! // equivalent using write (non-blocking); +//! for sample in sine_1500_iter.clone() { +//! block!(i2s3_transfer.write(sample)).ok(); +//! } +//! ``` +//! # Receiving data +//! +//! Receiving data can be done with `read_while` (blocking API) or `read` (non-blocking API). +//! ```ignore +//! // buffer to record 1 second of 8 bit mono data at 48 kHz +//! let mut buf = [0u8; 48000]; +//! +//! // peekable iterator +//! let mut buf_iter = buf.iter_mut().peekable(); +//! +//! // take left channel data and convert it into 8 bit data (blocking) +//! transfer.read_while(|s| { +//! if let Some(b) = buf_iter.next() { +//! *b = (s.0 >> 8) as u8; +//! } +//! buf_iter.peek().is_some() +//! }); +//! +//! // equivalent with using read (non-blocking API) +//! for sample in sine_1500_iter.clone() { +//! if let Some((l,_)) = block!(i2s3_transfer.read()) { +//! *sample = (l >> 8) as u8; +//! } +//! } +//! ``` +//! +//! # Transmit and receive at same time +//! +//! The non-blocking API allow to process transmitting and receiving at same time. However, the +//! following example require two transfer using same clocks to work correctly: +//! ```ignore +//! let mut samples = (0, 0); +//! loop { +//! if let Ok(s) = transfer1.read() { +//! /* do some processing on s */ +//! samples = s; +//! } +//! transfer2.write(samples).ok(); +//! } +//! ``` +use core::convert::Infallible; +use core::marker::PhantomData; + +use nb::Error::WouldBlock; + +use crate::driver::Channel::*; +use crate::driver::ClockPolarity; +use crate::driver::I2sDriver as Driver; +use crate::driver::I2sDriverConfig as DriverConfig; +use crate::I2sPeripheral; + +pub use crate::marker::{self, *}; + +#[derive(Debug, Clone, Copy)] +/// [`I2sTransfer`] configuration. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` +/// - `FMT`: Frame Format marker, eg `Data16Channel16` +/// +/// **Note:** because of it's typestate, methods of this type don't change variable content, they +/// return a new value instead. +pub struct I2sTransferConfig { + driver_config: DriverConfig, + _fmt: PhantomData, +} + +impl I2sTransferConfig { + /// Create a new default slave configuration. + pub fn new_slave() -> Self { + Self { + driver_config: DriverConfig::new_slave(), + _fmt: PhantomData, + } + } +} + +impl I2sTransferConfig { + /// Create a new default master configuration. + pub fn new_master() -> Self { + Self { + driver_config: DriverConfig::new_master(), + _fmt: PhantomData, + } + } +} + +impl I2sTransferConfig +where + FMT: DataFormat, +{ + /// Create a `I2sTransfer` object around an [`I2sPeripheral`] object. + /// + /// # Panics + /// + /// This method panics if an exact frequency is required and that frequency can not be set. + pub fn i2s_transfer( + self, + i2s_peripheral: I, + ) -> I2sTransfer { + let driver = self.driver_config.i2s_driver(i2s_peripheral); + I2sTransfer:: { + driver, + frame: Default::default(), + frame_state: FrameState::LeftMsb, + sync: false, + _fmt: PhantomData, + } + } +} + +impl Default for I2sTransferConfig { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl I2sTransferConfig { + /// Configure transfert for transmitting data. + pub fn transmit(self) -> I2sTransferConfig { + I2sTransferConfig:: { + driver_config: self.driver_config.transmit(), + _fmt: PhantomData, + } + } + /// Configure transfer for receiving data. + pub fn receive(self) -> I2sTransferConfig { + I2sTransferConfig:: { + driver_config: self.driver_config.receive(), + _fmt: PhantomData, + } + } + /// Select the I2s standard to use. The parameter is just a marker implementing [`I2sStandard`]. + #[allow(non_camel_case_types)] + pub fn standard(self, _standard: NEW_STD) -> I2sTransferConfig + where + NEW_STD: marker::I2sStandard, + { + I2sTransferConfig:: { + driver_config: self.driver_config.standard(_standard), + _fmt: PhantomData, + } + } + /// Select steady state clock polarity + pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { + I2sTransferConfig:: { + driver_config: self.driver_config.clock_polarity(polarity), + _fmt: PhantomData, + } + } + + /// Select data format. The parameter is just a marker implementing [`DataFormat`]. + #[allow(non_camel_case_types)] + pub fn data_format(self, _format: NEW_FMT) -> I2sTransferConfig + where + NEW_FMT: marker::DataFormat, + { + I2sTransferConfig:: { + driver_config: self.driver_config.data_format(NEW_FMT::VALUE), + _fmt: PhantomData, + } + } + + /// Convert to a slave configuration. This delete Master Only Settings. + pub fn to_slave(self) -> I2sTransferConfig { + I2sTransferConfig:: { + driver_config: self.driver_config.to_slave(), + _fmt: PhantomData, + } + } + + /// Convert to a master configuration. + pub fn to_master(self) -> I2sTransferConfig { + I2sTransferConfig:: { + driver_config: self.driver_config.to_master(), + _fmt: PhantomData, + } + } +} + +impl I2sTransferConfig { + /// Enable/Disable Master Clock. Affect the effective sampling rate. + /// + /// This can be only set and only have meaning for Master mode. + pub fn master_clock(self, enable: bool) -> Self { + I2sTransferConfig:: { + driver_config: self.driver_config.master_clock(enable), + _fmt: PhantomData, + } + } + + /// Configure audio frequency of the transfer by setting the prescaler with an odd factor and a + /// divider. + /// + /// The effective sampling frequency is: + /// - `i2s_clock / [256 * ((2 * div) + odd)]` when master clock is enabled + /// - `i2s_clock / [(channel_length * 2) * ((2 * div) + odd)]` when master clock is disabled + /// + /// `i2s_clock` is I2S clock source frequency, and `channel_length` is width in bits of the + /// channel (see [DataFormat]) + /// + /// This setting only have meaning and can be only set for master. + /// + /// # Panics + /// + /// `div` must be at least 2, otherwise the method panics. + pub fn prescaler(self, odd: bool, div: u8) -> Self { + I2sTransferConfig:: { + driver_config: self.driver_config.prescaler(odd, div), + _fmt: PhantomData, + } + } + + /// Request an audio sampling frequency. The effective audio sampling frequency may differ. + pub fn request_frequency(self, freq: u32) -> Self { + I2sTransferConfig:: { + driver_config: self.driver_config.request_frequency(freq), + _fmt: PhantomData, + } + } + + /// Require exactly this audio sampling frequency. + /// + /// If the required frequency can not bet set, Instantiate a transfer will panics. + pub fn require_frequency(self, freq: u32) -> Self { + I2sTransferConfig:: { + driver_config: self.driver_config.require_frequency(freq), + _fmt: PhantomData, + } + } +} + +/// Part of the frame we currently transmitting or receiving +#[derive(Debug, Clone, Copy)] +enum FrameState { + LeftMsb, + LeftLsb, + RightMsb, + RightLsb, +} +use FrameState::*; + +/// Abstraction allowing to transmit/receive I2S data while erasing hardware details. +/// +/// This type is meant to implement the Upcoming I2S embbeded-hal in the future. +/// +/// Note: current implementation never fail when an error is detected, it try to recover intead. As +/// result, data received or transmitted may corrupted. This choice has been made because: +/// - corrupted data can't produce an invalid value that can cause undefined behavior, +/// - audio quality is equally degraded by missing or corrupted data, +/// - it's easier to use. +pub struct I2sTransfer +where + I: I2sPeripheral, + FMT: DataFormat, +{ + driver: Driver, + frame: FMT::AudioFrame, + frame_state: FrameState, + sync: bool, + _fmt: PhantomData, +} + +impl I2sTransfer +where + I: I2sPeripheral, + STD: I2sStandard, + FMT: DataFormat, +{ + /// When `true` the level on WS line make start a slave. The slave must be enabled before this + /// level is set. + #[inline] + fn _ws_is_start(&self) -> bool { + match STD::WS_START_LEVEL { + false => self.driver.ws_is_low(), + true => self.driver.ws_is_high(), + } + } +} + +/// Constructors and Destructors +impl I2sTransfer +where + I: I2sPeripheral, + FMT: DataFormat, +{ + /// Instantiate and configure an i2s driver around an [`I2sPeripheral`]. + /// + /// # Panics + /// + /// This method panics if an exact frequency is required by the config and that frequency can + /// not be set. + pub fn new(i2s_peripheral: I, config: I2sTransferConfig) -> Self { + config.i2s_transfer(i2s_peripheral) + } + + /// Destroy the transfer, release the owned i2s device and reset it's configuration. + pub fn release(self) -> I { + self.driver.release() + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + FMT: DataFormat, +{ + /// Activate the I2s interface. + pub fn begin(&mut self) { + self.driver.enable() + } + + /// Deactivate the I2s interface and reset internal state + pub fn end(&mut self) { + self.driver.disable(); + self.frame = Default::default(); + self.frame_state = FrameState::LeftMsb; + self.sync = false; + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + FMT: DataFormat, +{ + pub fn sample_rate(&self) -> u32 { + self.driver.sample_rate() + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, +{ + /// Transmit (blocking) data from an iterator. + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut samples = samples.into_iter(); + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.txe() { + let data; + match self.frame_state { + LeftMsb => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + data = (self.frame.0) as u16; + self.frame_state = RightMsb; + } + RightMsb => { + data = (self.frame.1) as u16; + self.frame_state = LeftMsb; + } + _ => unreachable!(), + } + self.driver.write_data_register(data); + } + } + } + + /// Write one audio frame. Activate the I2s interface if disabled. + /// + /// To fully transmit the frame, this function need to be continuously called until next + /// frame can be written. + pub fn write(&mut self, frame: (i16, i16)) -> nb::Result<(), Infallible> { + self.driver.enable(); + let status = self.driver.status(); + if status.txe() { + match self.frame_state { + LeftMsb => { + self.frame = frame; + let data = (self.frame.0) as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + return Ok(()); + } + RightMsb => { + let data = (self.frame.1) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; + } + _ => unreachable!(), + } + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, +{ + /// Transmit (blocking) data from an iterator. + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut samples = samples.into_iter(); + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.txe() { + let data; + match self.frame_state { + LeftMsb => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + data = (self.frame.0 as u32 >> 16) as u16; + self.frame_state = LeftLsb; + } + LeftLsb => { + data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.frame_state = RightMsb; + } + RightMsb => { + data = (self.frame.1 as u32 >> 16) as u16; + self.frame_state = RightLsb; + } + RightLsb => { + data = (self.frame.1 as u32 & 0xFFFF) as u16; + self.frame_state = LeftMsb; + } + } + self.driver.write_data_register(data); + } + } + } + + /// Write one audio frame. Activate the I2s interface if disabled. + /// + /// To fully transmit the frame, this function need to be continuously called until next + /// frame can be written. + pub fn write(&mut self, frame: (i32, i32)) -> nb::Result<(), Infallible> { + self.driver.enable(); + let status = self.driver.status(); + if status.txe() { + match self.frame_state { + LeftMsb => { + self.frame = frame; + let data = (self.frame.0 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftLsb; + return Ok(()); + } + LeftLsb => { + let data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + } + RightMsb => { + let data = (self.frame.1 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = RightLsb; + } + RightLsb => { + let data = (self.frame.1 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; + } + } + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + STD: I2sStandard, + FMT: Data16 + DataFormat, +{ + /// Transmit (blocking) data from an iterator. + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut samples = samples.into_iter(); + loop { + if self.sync { + let status = self.driver.status(); + if status.txe() { + let data; + match self.frame_state { + LeftMsb => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + data = self.frame.0 as u16; + self.frame_state = RightMsb; + } + RightMsb => { + data = self.frame.1 as u16; + self.frame_state = LeftMsb; + } + _ => unreachable!(), + } + self.driver.write_data_register(data); + } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + } + } else if !self._ws_is_start() { + // data register may (or not) already contain data, causing uncertainty about next + // time txe flag is set. Writing it remove the uncertainty. + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + let data = self.frame.0 as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + } + } + /// Write one audio frame. Activate the I2s interface if disabled. + /// + /// To fully transmit the frame, this function need to be continuously called until next + /// frame can be written. + pub fn write(&mut self, frame: (i16, i16)) -> nb::Result<(), Infallible> { + if self.sync { + let status = self.driver.status(); + if status.txe() { + match self.frame_state { + LeftMsb => { + self.frame = frame; + let data = self.frame.0 as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + return Ok(()); + } + RightMsb => { + let data = self.frame.1 as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; + } + _ => unreachable!(), + } + } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + } + } else if !self._ws_is_start() { + // data register may (or not) already contain data, causing uncertainty about next + // time txe flag is set. Writing it remove the uncertainty. + let data = self.frame.0 as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + return Ok(()); + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + STD: I2sStandard, +{ + /// Transmit (blocking) data from an iterator. + #[inline] + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut samples = samples.into_iter(); + loop { + if self.sync { + let status = self.driver.status(); + if status.txe() { + let data; + match self.frame_state { + LeftMsb => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + data = (self.frame.0 as u32 >> 16) as u16; + self.frame_state = LeftLsb; + } + LeftLsb => { + data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.frame_state = RightMsb; + } + RightMsb => { + data = (self.frame.1 as u32 >> 16) as u16; + self.frame_state = RightLsb; + } + RightLsb => { + data = (self.frame.1 as u32 & 0xFFFF) as u16; + self.frame_state = LeftMsb; + } + } + self.driver.write_data_register(data); + } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + } + } else if !self._ws_is_start() { + // data register may (or not) already contain data, causing uncertainty about next + // time txe flag is set. Writing it remove the uncertainty. + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + self.frame = smpl.unwrap(); + let data = (self.frame.0 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftLsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + } + } + + /// Write one audio frame. Activate the I2s interface if disabled. + /// + /// To fully transmit the frame, this function need to be continuously called until next + /// frame can be written. + pub fn write(&mut self, frame: (i32, i32)) -> nb::Result<(), Infallible> { + if self.sync { + let status = self.driver.status(); + if status.txe() { + match self.frame_state { + LeftMsb => { + self.frame = frame; + let data = (self.frame.0 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftLsb; + return Ok(()); + } + LeftLsb => { + let data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; + } + RightMsb => { + let data = (self.frame.1 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = RightLsb; + } + RightLsb => { + let data = (self.frame.1 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; + } + } + } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + } + } else if !self._ws_is_start() { + // data register may (or not) already contain data, causing uncertainty about next + // time txe flag is set. Writing it remove the uncertainty. + let data = (self.frame.0 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftLsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + return Ok(()); + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, + STD: ChannelFlag, +{ + /// Read samples while predicate return `true`. + /// + /// The given closure must not block, otherwise communication problems may occur. + pub fn read_while(&mut self, mut predicate: F) + where + F: FnMut((i16, i16)) -> bool, + { + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.rxne() { + match status.chside() { + Left => { + let data = self.driver.read_data_register(); + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + Right => { + let data = self.driver.read_data_register(); + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + if !predicate(self.frame) { + return; + } + } + } + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + } + } + } + + /// Read one audio frame. Activate the I2s interface if disabled. + /// + /// To get the audio frame, this function need to be continuously called until the frame is + /// returned + pub fn read(&mut self) -> nb::Result<(i16, i16), Infallible> { + self.driver.enable(); + let status = self.driver.status(); + if status.rxne() { + match status.chside() { + Left => { + let data = self.driver.read_data_register(); + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + Right => { + let data = self.driver.read_data_register(); + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + return Ok(self.frame); + } + } + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + STD: ChannelFlag, +{ + /// Read samples while predicate return `true`. + /// + /// The given closure must not block, otherwise communication problems may occur. + pub fn read_while(&mut self, mut predicate: F) + where + F: FnMut((i32, i32)) -> bool, + { + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match (self.frame_state, status.chside()) { + (LeftMsb, Left) => { + self.frame.0 = (data as i32) << 16; + self.frame_state = LeftLsb; + } + (LeftLsb, Left) => { + self.frame.0 |= data as i32; + self.frame_state = RightMsb; + } + (RightMsb, Right) => { + self.frame.1 = (data as i32) << 16; + self.frame_state = RightLsb; + } + (RightLsb, Right) => { + self.frame.1 |= data as i32; + self.frame_state = LeftMsb; + if !predicate(self.frame) { + return; + } + } + // in case of ovr this resynchronize at start of new frame + _ => self.frame_state = LeftMsb, + } + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + self.frame_state = LeftMsb; + } + } + } + + /// Read one audio frame. Activate the I2s interface if disabled. + /// + /// To get the audio frame, this function need to be continuously called until the frame is + /// returned + pub fn read(&mut self) -> nb::Result<(i32, i32), Infallible> { + self.driver.enable(); + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match (self.frame_state, status.chside()) { + (LeftMsb, Left) => { + self.frame.0 = (data as i32) << 16; + self.frame_state = LeftLsb; + } + (LeftLsb, Left) => { + self.frame.0 |= data as i32; + self.frame_state = RightMsb; + } + (RightMsb, Right) => { + self.frame.1 = (data as i32) << 16; + self.frame_state = RightLsb; + } + (RightLsb, Right) => { + self.frame.1 |= data as i32; + self.frame_state = LeftMsb; + return Ok(self.frame); + } + // in case of ovr this resynchronize at start of new frame + _ => self.frame_state = LeftMsb, + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + self.frame_state = LeftMsb; + } + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, + STD: ChannelFlag, +{ + /// Read samples while predicate return `true`. + /// + /// The given closure must not block, otherwise communication problems may occur. + pub fn read_while(&mut self, mut predicate: F) + where + F: FnMut((i16, i16)) -> bool, + { + loop { + if self.sync { + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match self.frame_state { + LeftMsb => { + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + RightMsb => { + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + if !predicate(self.frame) { + return; + } + } + _ => unreachable!(), + } + } + if status.fre() || status.ovr() { + self.sync = false; + self.driver.read_data_register(); + self.driver.status(); + self.driver.disable(); + } + } else if !self._ws_is_start() { + self.frame_state = LeftMsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + } + } + + /// Read one audio frame. Activate the I2s interface if disabled. + /// + /// To get the audio frame, this function need to be continuously called until the frame is + /// returned + pub fn read(&mut self) -> nb::Result<(i16, i16), Infallible> { + if !self.sync { + self.driver.disable(); + self.frame_state = RightMsb; + } + if self.sync { + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match self.frame_state { + LeftMsb => { + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + RightMsb => { + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + return Ok(self.frame); + } + _ => unreachable!(), + } + } + if status.fre() || status.ovr() { + self.sync = false; + //self.driver.read_data_register(); + //self.driver.status(); + self.driver.disable(); + } + } else if !self._ws_is_start() { + self.frame_state = LeftMsb; + self.driver.enable(); + self.driver.read_data_register(); + self.driver.status(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + Err(WouldBlock) + } +} + +impl I2sTransfer +where + I: I2sPeripheral, + STD: ChannelFlag, +{ + /// Read samples while predicate return `true`. + /// + /// The given closure must not block, otherwise communication problems may occur. + pub fn read_while(&mut self, mut predicate: F) + where + F: FnMut((i32, i32)) -> bool, + { + loop { + if self.sync { + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match self.frame_state { + LeftMsb => { + self.frame.0 = (data as i32) << 16; + self.frame_state = LeftLsb; + } + LeftLsb => { + self.frame.0 |= data as i32; + self.frame_state = RightMsb; + } + RightMsb => { + self.frame.1 = (data as i32) << 16; + self.frame_state = RightLsb; + } + RightLsb => { + self.frame.1 |= data as i32; + self.frame_state = LeftMsb; + if !predicate(self.frame) { + return; + } + } + } + } + if status.fre() || status.ovr() { + self.sync = false; + self.driver.read_data_register(); + self.driver.status(); + self.driver.disable(); + } + } else if !self._ws_is_start() { + self.frame_state = LeftMsb; + self.driver.enable(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + } + } + + /// Read one audio frame. Activate the I2s interface if disabled. + /// + /// To get the audio frame, this function need to be continuously called until the frame is + /// returned + pub fn read(&mut self) -> nb::Result<(i32, i32), Infallible> { + if !self.sync { + self.driver.disable(); + self.frame_state = RightMsb; + } + if self.sync { + let status = self.driver.status(); + if status.rxne() { + let data = self.driver.read_data_register(); + match self.frame_state { + LeftMsb => { + self.frame.0 = (data as i32) << 16; + self.frame_state = LeftLsb; + } + LeftLsb => { + self.frame.0 |= data as i32; + self.frame_state = RightMsb; + } + RightMsb => { + self.frame.1 = (data as i32) << 16; + self.frame_state = RightLsb; + } + RightLsb => { + self.frame.1 |= data as i32; + self.frame_state = LeftMsb; + return Ok(self.frame); + } + } + } + if status.fre() || status.ovr() { + self.sync = false; + //self.driver.read_data_register(); + //self.driver.status(); + self.driver.disable(); + } + } else if !self._ws_is_start() { + self.frame_state = LeftMsb; + self.driver.enable(); + self.driver.read_data_register(); + self.driver.status(); + // ensure the ws line didn't change during sync process + if !self._ws_is_start() { + self.sync = true; + } else { + self.driver.disable(); + } + } + Err(WouldBlock) + } +}