From 53bc51778422076f95468e445bf80e3b8a871f88 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 17 Apr 2022 22:18:07 +0200 Subject: [PATCH 01/91] start rework branch, document what going on --- README.md | 2 ++ rework-branch.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 rework-branch.md diff --git a/README.md b/README.md index e948bda..a883171 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # STM32 I2S driver +*This library is currently redesigned, see [rework-branch.md](rework-branch.md) for details* + This library provides a driver for I2S communication using the SPI peripherals on some STM32 microcontrollers. ## Differences between STM32 models diff --git a/rework-branch.md b/rework-branch.md new file mode 100644 index 0000000..d26a7e6 --- /dev/null +++ b/rework-branch.md @@ -0,0 +1,33 @@ +# rework branch + +Version 0.2 of this crate have many design flaw making it difficult to use in +general and even unusable in several situations. A redesign is required. The +new approach is to have a low-level driver exposing hardware specificity for +precise control and higher level abstraction will be built on top of this +driver, not inside it. + +You can found below a list of important issues. + +## Undocumented side effects + +reading the status register can reset error flag. Since many function access +to this register, this make impossible to get reliably those errors. + +## Not meaningful and miss usable API + +transmit/receive blocking a slice of interleaved samples: which is left which +is right ? First sample can be left or right depending what happened before. + +`ready_to_transmit` and `sample_ready` return `Option`. The `Channel` +information is meaningless in PCM mode. + +## Missing feature + +Slave operation require to read the WS pin to synchronise reliably. This +functionality is missing, so slave operation is near to unusable + + + + + + From aa92a1d9d533ebf48c3e34de82e34d20391ec16e Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 18 Apr 2022 01:04:05 +0200 Subject: [PATCH 02/91] rename and delete stuff, see details - rename I2s to I2sDriver and Instance to I2sPeripheral to avoid confusion with mcu hal. - delete many thing. it will be reimplemented differently --- src/lib.rs | 611 +++-------------------------------------------------- 1 file changed, 26 insertions(+), 585 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2c75124..18fa237 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! 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 +//! [I2sPeripheral](crate::I2sPeripheral). An [I2sDriver](crate::I2sDriver) object can be created around the I2sPeripheral //! object and used for I2S. #![no_std] @@ -14,13 +14,17 @@ mod config; pub mod format; mod pac; -use core::convert::Infallible; -use core::marker::PhantomData; +mod sealed { + pub trait Sealed {} +} +//use self::sealed::Sealed; + +//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; +pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; +use self::pac::spi1::RegisterBlock; +//use crate::format::{DataFormat, FrameFormat, FrameSync}; +//use crate::pac::spi1::i2scfgr::I2SCFG_A; /// Clock polarity #[derive(Debug, Clone)] @@ -40,20 +44,11 @@ pub enum Channel { Right, } -/// 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, -} - -/// 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,91 +56,33 @@ 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; + const REGISTERS: *const (); } -/// 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 +/// Driver of a SPI peripheral in I2S mode /// /// # Example /// +/// TODO +/// /// ```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, +pub struct I2sDriver { + _instance: I, } -/// 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 +impl I2sDriver where - I: Instance, + I: I2sPeripheral, { - /// 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 } + unsafe { &*(I::REGISTERS as *const RegisterBlock) } } /// Enables the I2S peripheral @@ -167,499 +104,3 @@ where 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), -} From 3d91b2307202023c2720b1db147d9b1f4dec7ca3 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 18 Apr 2022 04:19:08 +0200 Subject: [PATCH 03/91] implement enable/disable, read/write, interrupt and dma --- src/lib.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 18fa237..571e367 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,16 +85,6 @@ where unsafe { &*(I::REGISTERS as *const RegisterBlock) } } - /// 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(); @@ -104,3 +94,57 @@ where registers.i2spr.reset(); } } + +impl I2sDriver +where + I: I2sPeripheral, +{ + /// 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()); + } + + /// 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)); + } + + /// 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 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`, 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`, an interrupt is generated each time an error occurs. + pub fn set_error_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.errie().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)) + } + + /// 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)) + } +} From 398d29e1366c1c1a4cf95d53e10debc010c4d0a9 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 18 Apr 2022 23:19:09 +0200 Subject: [PATCH 04/91] skeleton method for ws line reading --- src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 571e367..dd1a283 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,11 @@ pub enum Channel { Right, } +/// State +pub enum WsState { + Low, + High, +} /// An object composed of a SPI device that can be used for I2S communication. /// /// This trait is meant to be implemented on a type that represent a full SPI device, that means an @@ -147,4 +152,14 @@ where pub fn set_rx_dma(&mut self, enabled: bool) { self.registers().cr2.modify(|_, w| w.rxdmaen().bit(enabled)) } + + /// Is the signal on WS line high ? + pub fn ws_is_high(&self) -> bool { + todo!() + } + + /// Is the signal on WS line low ? + pub fn ws_is_low(&self) -> bool { + todo!() + } } From 16e39c8fb68a324ed7efbb808d7ba544e159c96b Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 19 Apr 2022 00:11:43 +0200 Subject: [PATCH 05/91] placeholder for getting a ws pin handle --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index dd1a283..867a7fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,4 +162,7 @@ where pub fn ws_is_low(&self) -> bool { todo!() } + + //TODO method to get a handle to WS pin. It may usefull for setting an interrupt on pin to + //synchronise I2s in slave mode } From a8a9ed5c2fa3ae1e3e86f1835fe286a34f3a7c9f Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 19 Apr 2022 00:23:53 +0200 Subject: [PATCH 06/91] oops, i don't use WsState --- src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 867a7fe..6aa9ca8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,11 +44,6 @@ pub enum Channel { Right, } -/// State -pub enum WsState { - Low, - High, -} /// An object composed of a SPI device that can be used for I2S communication. /// /// This trait is meant to be implemented on a type that represent a full SPI device, that means an From 320846f2dcb07a70551c4f00ee42034d57c1a474 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 19 Apr 2022 02:50:38 +0200 Subject: [PATCH 07/91] implement status method and struct --- src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6aa9ca8..352a89e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,9 @@ mod sealed { //use core::marker::PhantomData; pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; +use self::pac::generic::{Reg, R}; use self::pac::spi1::RegisterBlock; +use self::pac::spi1::_SR; //use crate::format::{DataFormat, FrameFormat, FrameSync}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; @@ -44,6 +46,73 @@ pub enum Channel { Right, } +/// Content of the status register. +pub struct Status { + value: R>, +} + +impl Status { + /// Get the FRE flag. If `true` a frame error occured. + /// + /// This flag can be set by hardware only if the I2sDriver is configured in Slave mode. It is set + /// when the WS line change at an unexpected moment. Usually, this indicate a synchronisation + /// issue. This flag is cleared when reading the status register. + pub fn fre(&self) -> bool { + self.value.fre().bit() + } + + /// Get the BSY flag. If `true` the I2s device is busy communicating. + pub fn bsy(&self) -> bool { + self.value.bsy().bit() + } + + /// Get the OVR flag. If `true` an overrun error occured. + /// + /// 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. 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 UDR flag. If `true` an underrun error occured. + /// + /// This flag can be set only in slave transmission mode. It 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 is cleared by reading the status register. + pub fn udr(&self) -> bool { + self.value.udr().bit() + } + + /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. Have + /// no meaning with PCM standard. + /// + /// This flag is updated when TXE or RXNE flags are set. This flag is meaningless and therefore + /// not reliable is case of error or when using the PCM standard. + pub fn chside(&self) -> Channel { + match self.value.udr().bit() { + false => Channel::Left, + true => Channel::Right, + } + } + + /// Get the TXE flag. If `true` the Tx buffer is empty and the next data can be loaded into it. + /// + /// This flag can be set only in transmision 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() + } + + /// Get the RXNE flag. If `true` a valid received data is present in the Rx buffer. + /// + /// This flag can be only set in reception mode. It is cleared when the data register is read. + pub fn rxne(&self) -> bool { + self.value.rxne().bit() + } +} + /// An object composed of a SPI device that can be used for I2S communication. /// /// This trait is meant to be implemented on a type that represent a full SPI device, that means an @@ -111,6 +180,16 @@ where self.registers().i2scfgr.modify(|_, w| w.i2se().disabled()); } + /// 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(), + } + } + /// 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. From a7a5d15af97615cae57e2d1fb83cc53a9437ed14 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 19 Apr 2022 23:31:36 +0200 Subject: [PATCH 08/91] draft of aconfig builder --- src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 352a89e..a8cc93c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,15 +19,22 @@ mod sealed { } //use self::sealed::Sealed; -//use core::marker::PhantomData; +use core::marker::PhantomData; pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; use self::pac::generic::{Reg, R}; +use self::pac::spi1::cr2; use self::pac::spi1::RegisterBlock; use self::pac::spi1::_SR; //use crate::format::{DataFormat, FrameFormat, FrameSync}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; +/// Marker, indicated master mode. +struct Master; + +/// Marker, indicate slave mode. +struct Slave; + /// Clock polarity #[derive(Debug, Clone)] pub enum Polarity { @@ -113,6 +120,74 @@ impl Status { } } +#[derive(Debug, Clone, Copy)] +enum SlaveOrMaster { + Slave, + Master, +} + +#[derive(Debug, Clone, Copy)] +enum TransmitOrReceive { + Transmit, + Receive, +} + +#[derive(Debug, Clone, Copy)] +/// I2s Configuration builder. +pub struct Config { + slave_or_master: SlaveOrMaster, + transmit_or_receive: TransmitOrReceive, + + _ms: PhantomData, +} + +impl Config { + /// Create a new default slave configuration. + pub fn new_slave() -> Self { + Self { + slave_or_master: SlaveOrMaster::Slave, + transmit_or_receive: TransmitOrReceive::Transmit, + _ms: PhantomData, + } + } +} + +impl Config { + /// Create a new default master configuration. + pub fn new_master() -> Self { + Self { + slave_or_master: SlaveOrMaster::Master, + transmit_or_receive: TransmitOrReceive::Transmit, + _ms: PhantomData, + } + } +} + +impl Default for Config { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl Config { + /// Instantiate the driver. + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { + let driver = I2sDriver { i2s_peripheral }; + driver.registers().cr1.reset(); // ensure SPI is disabled + 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(), + } + }); + driver + } +} + /// An object composed of a SPI device that can be used for I2S communication. /// /// This trait is meant to be implemented on a type that represent a full SPI device, that means an @@ -142,7 +217,7 @@ pub unsafe trait I2sPeripheral { /// ``` /// pub struct I2sDriver { - _instance: I, + i2s_peripheral: I, } impl I2sDriver From 92d8f12d648a0d52826cbc0b6be76d15c960da53 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 19 Apr 2022 23:37:03 +0200 Subject: [PATCH 09/91] use a nicer alias type for internal status representation --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a8cc93c..b0f0af0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,10 +22,8 @@ mod sealed { use core::marker::PhantomData; pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; -use self::pac::generic::{Reg, R}; -use self::pac::spi1::cr2; +use self::pac::spi1::sr; use self::pac::spi1::RegisterBlock; -use self::pac::spi1::_SR; //use crate::format::{DataFormat, FrameFormat, FrameSync}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; @@ -55,7 +53,7 @@ pub enum Channel { /// Content of the status register. pub struct Status { - value: R>, + value: sr::R, } impl Status { From bb793cfb5367728eec80757f63305c7709b52c01 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 20 Apr 2022 01:04:34 +0200 Subject: [PATCH 10/91] I2s standard config --- src/lib.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b0f0af0..39018e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,11 +130,27 @@ enum TransmitOrReceive { Receive, } +#[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, +} + #[derive(Debug, Clone, Copy)] /// I2s Configuration builder. pub struct Config { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, + standard: I2sStandard, _ms: PhantomData, } @@ -145,6 +161,7 @@ impl Config { Self { slave_or_master: SlaveOrMaster::Slave, transmit_or_receive: TransmitOrReceive::Transmit, + standard: I2sStandard::Philips, _ms: PhantomData, } } @@ -156,6 +173,7 @@ impl Config { Self { slave_or_master: SlaveOrMaster::Master, transmit_or_receive: TransmitOrReceive::Transmit, + standard: I2sStandard::Philips, _ms: PhantomData, } } @@ -169,6 +187,22 @@ impl Default for Config { } impl Config { + /// Configure in transmit mode + pub fn transmit(mut self) -> Self { + self.transmit_or_receive = TransmitOrReceive::Transmit; + self + } + /// Configure in transmit mode + pub fn receive(mut self) -> Self { + self.transmit_or_receive = TransmitOrReceive::Receive; + self + } + /// Select the I2s standard to use + pub fn standard(mut self, standard: I2sStandard) -> Self { + self.standard = standard; + self + } + /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { let driver = I2sDriver { i2s_peripheral }; @@ -180,7 +214,15 @@ impl Config { (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(), + }; + w }); driver } From 13108259c1cf55c0ef8781947f5c5b897f7e965c Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 20 Apr 2022 01:30:19 +0200 Subject: [PATCH 11/91] Steady state clock polarity configuration --- src/lib.rs | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 39018e1..da9f85b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ extern crate nb; extern crate vcell; -mod config; +//mod config; pub mod format; mod pac; @@ -21,7 +21,7 @@ mod sealed { use core::marker::PhantomData; -pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; +//pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; use self::pac::spi1::sr; use self::pac::spi1::RegisterBlock; //use crate::format::{DataFormat, FrameFormat, FrameSync}; @@ -33,15 +33,6 @@ struct Master; /// Marker, indicate slave mode. struct Slave; -/// 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 { @@ -145,12 +136,22 @@ pub enum I2sStandard { PcmLongSync, } +/// Steady state clock polarity +#[derive(Debug, Clone, Copy)] +pub enum ClockPolarity { + /// Clock low when idle + IdleLow, + /// Clock high when idle + IdleHigh, +} + #[derive(Debug, Clone, Copy)] /// I2s Configuration builder. pub struct Config { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, standard: I2sStandard, + clock_polarity: ClockPolarity, _ms: PhantomData, } @@ -162,6 +163,7 @@ impl Config { slave_or_master: SlaveOrMaster::Slave, transmit_or_receive: TransmitOrReceive::Transmit, standard: I2sStandard::Philips, + clock_polarity: ClockPolarity::IdleLow, _ms: PhantomData, } } @@ -174,6 +176,7 @@ impl Config { slave_or_master: SlaveOrMaster::Master, transmit_or_receive: TransmitOrReceive::Transmit, standard: I2sStandard::Philips, + clock_polarity: ClockPolarity::IdleLow, _ms: PhantomData, } } @@ -202,6 +205,13 @@ impl Config { self.standard = standard; self } + /// 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 + } /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { From 513f07c5fe9ead52c75332ec7127de023723ea7d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 20 Apr 2022 02:29:53 +0200 Subject: [PATCH 12/91] data and channel lenght config --- src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index da9f85b..0e78abe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,6 +145,25 @@ pub enum ClockPolarity { IdleHigh, } +/// Data length to be transferred and channel length +#[derive(Debug, Clone, Copy)] +pub enum DataFormat { + /// 16 bit date length on 16 bit wide channel + Data16Channel16, + /// 16 bit date length on 32 bit wide channel + Data16Channel32, + /// 24 bit date length on 32 bit wide channel + Data24Channel32, + /// 32 bit date length on 32 bit wide channel + Data32Channel32, +} + +impl Default for DataFormat { + fn default() -> Self { + DataFormat::Data16Channel16 + } +} + #[derive(Debug, Clone, Copy)] /// I2s Configuration builder. pub struct Config { @@ -152,6 +171,7 @@ pub struct Config { transmit_or_receive: TransmitOrReceive, standard: I2sStandard, clock_polarity: ClockPolarity, + data_format: DataFormat, _ms: PhantomData, } @@ -164,6 +184,7 @@ impl Config { transmit_or_receive: TransmitOrReceive::Transmit, standard: I2sStandard::Philips, clock_polarity: ClockPolarity::IdleLow, + data_format: Default::default(), _ms: PhantomData, } } @@ -177,6 +198,7 @@ impl Config { transmit_or_receive: TransmitOrReceive::Transmit, standard: I2sStandard::Philips, clock_polarity: ClockPolarity::IdleLow, + data_format: Default::default(), _ms: PhantomData, } } @@ -232,6 +254,14 @@ impl Config { 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 From b9d139b01a5ed6f8c771c4683cba981d44ae2ba7 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 21 Apr 2022 00:15:23 +0200 Subject: [PATCH 13/91] master clock config --- src/lib.rs | 79 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0e78abe..e2d4f10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,6 +172,7 @@ pub struct Config { standard: I2sStandard, clock_polarity: ClockPolarity, data_format: DataFormat, + master_clock: bool, _ms: PhantomData, } @@ -185,6 +186,7 @@ impl Config { standard: I2sStandard::Philips, clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), + master_clock: false, _ms: PhantomData, } } @@ -199,42 +201,13 @@ impl Config { standard: I2sStandard::Philips, clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), + master_clock: false, _ms: PhantomData, } } } -impl Default for Config { - /// Create a default configuration. It correspond to a default slave configuration. - fn default() -> Self { - Self::new_slave() - } -} - impl Config { - /// Configure in transmit mode - pub fn transmit(mut self) -> Self { - self.transmit_or_receive = TransmitOrReceive::Transmit; - self - } - /// Configure in transmit mode - pub fn receive(mut self) -> Self { - self.transmit_or_receive = TransmitOrReceive::Receive; - self - } - /// Select the I2s standard to use - pub fn standard(mut self, standard: I2sStandard) -> Self { - self.standard = standard; - self - } - /// 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 - } - /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { let driver = I2sDriver { i2s_peripheral }; @@ -264,10 +237,56 @@ impl Config { }; w }); + driver.registers().i2spr.write(|w| { + w.mckoe().bit(self.master_clock); + w + }); driver } } +impl Default for Config { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl Config { + /// Configure in transmit mode + pub fn transmit(mut self) -> Self { + self.transmit_or_receive = TransmitOrReceive::Transmit; + self + } + /// Configure in transmit mode + pub fn receive(mut self) -> Self { + self.transmit_or_receive = TransmitOrReceive::Receive; + self + } + /// Select the I2s standard to use + pub fn standard(mut self, standard: I2sStandard) -> Self { + self.standard = standard; + self + } + /// 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 + } +} + +impl Config { + /// 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 + } +} + /// An object composed of a SPI device that can be used for I2S communication. /// /// This trait is meant to be implemented on a type that represent a full SPI device, that means an From 9764e4bb75d5fc2d6b8dcf39e0621eeb1fd3d7da Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 21 Apr 2022 02:05:55 +0200 Subject: [PATCH 14/91] prescaler setting --- src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e2d4f10..856d2ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,6 +173,7 @@ pub struct Config { clock_polarity: ClockPolarity, data_format: DataFormat, master_clock: bool, + prescaler: (bool, u8), _ms: PhantomData, } @@ -187,6 +188,7 @@ impl Config { clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), master_clock: false, + prescaler: (false, 0b10), _ms: PhantomData, } } @@ -202,6 +204,7 @@ impl Config { clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), master_clock: false, + prescaler: (false, 0b10), _ms: PhantomData, } } @@ -239,6 +242,8 @@ impl Config { }); driver.registers().i2spr.write(|w| { w.mckoe().bit(self.master_clock); + w.odd().bit(self.prescaler.0); + unsafe { w.i2sdiv().bits(self.prescaler.1) }; w }); driver @@ -285,6 +290,29 @@ impl Config { 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 between 2 and 127, otherwise the method panics. + pub fn prescaler(mut self, odd: bool, div: u8) -> Self { + #[allow(clippy::manual_range_contains)] + if div < 2 || div > 127 { + panic!("div is out of bounds") + } + self.prescaler = (odd, div); + self + } } /// An object composed of a SPI device that can be used for I2S communication. From bb361bcb1468ffa3daf9df72619defdac975e5e1 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 21 Apr 2022 19:52:53 +0200 Subject: [PATCH 15/91] Request and require frequency (not fully implemented) --- src/lib.rs | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 856d2ef..70a4ce9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,8 @@ mod sealed { use core::marker::PhantomData; //pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; -use self::pac::spi1::sr; use self::pac::spi1::RegisterBlock; +use self::pac::spi1::{i2spr, sr}; //use crate::format::{DataFormat, FrameFormat, FrameSync}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; @@ -121,6 +121,14 @@ enum TransmitOrReceive { Receive, } +/// Various ways to specify sampling frequency. +#[derive(Debug, Clone, Copy)] +enum Frequency { + Prescaler(bool, u8), + Request(u32), + Require(u32), +} + #[derive(Debug, Clone, Copy)] /// I2s standard selection. pub enum I2sStandard { @@ -173,7 +181,7 @@ pub struct Config { clock_polarity: ClockPolarity, data_format: DataFormat, master_clock: bool, - prescaler: (bool, u8), + frequency: Frequency, _ms: PhantomData, } @@ -188,7 +196,7 @@ impl Config { clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), master_clock: false, - prescaler: (false, 0b10), + frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, } } @@ -204,12 +212,17 @@ impl Config { clock_polarity: ClockPolarity::IdleLow, data_format: Default::default(), master_clock: false, - prescaler: (false, 0b10), + frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, } } } +fn _set_prescaler(w: &mut i2spr::W, odd: bool, div: u8) { + w.odd().bit(odd); + unsafe { w.i2sdiv().bits(div) }; +} + impl Config { /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { @@ -242,8 +255,11 @@ impl Config { }); driver.registers().i2spr.write(|w| { w.mckoe().bit(self.master_clock); - w.odd().bit(self.prescaler.0); - unsafe { w.i2sdiv().bits(self.prescaler.1) }; + match self.frequency { + Frequency::Prescaler(odd, div) => _set_prescaler(w, odd, div), + Frequency::Request(_freq) => todo!(), + Frequency::Require(_freq) => todo!(), + } w }); driver @@ -310,7 +326,21 @@ impl Config { if div < 2 || div > 127 { panic!("div is out of bounds") } - self.prescaler = (odd, div); + 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, Instatiate the driver will produce a error + pub fn require_frequency(mut self, freq: u32) -> Self { + self.frequency = Frequency::Require(freq); self } } From 6045d51332a2b07f336452a5ad82a7a5da940d74 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 22 Apr 2022 00:27:16 +0200 Subject: [PATCH 16/91] require a new function for I2sPeripheral trait --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 70a4ce9..aea56ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,6 +362,10 @@ impl Config { pub unsafe trait I2sPeripheral { /// Pointer to the SPI register block const REGISTERS: *const (); + /// Get I2s clock source frequency from the I2s device. + /// + /// Implemetors are allowed to panic in case i2s sourcefrequencey is unavailable. + fn i2s_source_freq(&self) -> u32; } /// Driver of a SPI peripheral in I2S mode From cc02e0dd4ae830965dd1b1217abeee7ffa17afaa Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 22 Apr 2022 03:07:47 +0200 Subject: [PATCH 17/91] implement setting prescaler with requested freq --- src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aea56ee..2d90658 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -218,11 +218,62 @@ impl Config { } } +/// rounding division +fn div_round(n: u32, d: u32) -> u32 { + (n + (d >> 1)) / d +} + 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 > 255 { + (true, 127) + } else { + ((division & 1) == 1, (division >> 1) as u8) + }; + _set_prescaler(w, odd, div); +} + +// set _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 Config { /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { @@ -257,7 +308,13 @@ impl Config { w.mckoe().bit(self.master_clock); match self.frequency { Frequency::Prescaler(odd, div) => _set_prescaler(w, odd, div), - Frequency::Request(_freq) => todo!(), + Frequency::Request(freq) => _set_request_frequency( + w, + driver.i2s_peripheral.i2s_freq(), + freq, + self.master_clock, + self.data_format, + ), Frequency::Require(_freq) => todo!(), } w @@ -365,7 +422,7 @@ pub unsafe trait I2sPeripheral { /// Get I2s clock source frequency from the I2s device. /// /// Implemetors are allowed to panic in case i2s sourcefrequencey is unavailable. - fn i2s_source_freq(&self) -> u32; + fn i2s_freq(&self) -> u32; } /// Driver of a SPI peripheral in I2S mode @@ -476,3 +533,16 @@ where //TODO method to get a handle to WS pin. It may usefull for setting an interrupt on pin to //synchronise I2s in slave mode } + +#[cfg(test)] +mod tests { + #[test] + fn test_div_rounding() { + let fracs = [(1, 2), (2, 2), (1, 3), (2, 3), (2, 4), (3, 5), (9, 2)]; + for (n, d) in fracs { + let res = div_rounding(n, d); + let check = f32::round((n as f32) / (d as f32)) as u32; + assert_eq!(res, check); + } + } +} From 163697a11052e59fcabad4a0fa24d6e149fcc1d7 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 22 Apr 2022 03:18:58 +0200 Subject: [PATCH 18/91] fix mistake about div max --- src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2d90658..a86cf5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,6 +223,7 @@ 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) }; @@ -254,8 +255,8 @@ fn _set_request_frequency( let division = div_round(i2s_clock, coef * request_freq); let (odd, div) = if division < 4 { (false, 2) - } else if division > 255 { - (true, 127) + } else if division > 511 { + (true, 255) } else { ((division & 1) == 1, (division >> 1) as u8) }; @@ -377,11 +378,11 @@ impl Config { /// /// # Panics /// - /// `div` must be between 2 and 127, otherwise the method 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 || div > 127 { - panic!("div is out of bounds") + if div < 2 { + panic!("div is less than 2, frobidden value") } self.frequency = Frequency::Prescaler(odd, div); self From cf54f5679bd035c7dfa38bae3d06eb81d6d903ac Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 22 Apr 2022 03:41:40 +0200 Subject: [PATCH 19/91] implment setting prescaler with a required frequency --- src/lib.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a86cf5a..68bab84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,6 +263,26 @@ fn _set_request_frequency( _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") + }; +} + // set _set_request_frequency for explanation fn _coef(mclk: bool, data_format: DataFormat) -> u32 { if mclk { @@ -316,7 +336,13 @@ impl Config { self.master_clock, self.data_format, ), - Frequency::Require(_freq) => todo!(), + Frequency::Require(freq) => _set_require_frequency( + w, + driver.i2s_peripheral.i2s_freq(), + freq, + self.master_clock, + self.data_format, + ), } w }); From 8258dbe65f0c80eb6b486c683bc2c29125c92ea2 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 26 Apr 2022 03:14:18 +0200 Subject: [PATCH 20/91] method to get reference to the i2s device --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 68bab84..0afc765 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -488,6 +488,11 @@ impl I2sDriver where I: I2sPeripheral, { + /// Get a reference to the underlaying i2s device + pub fn i2s_peripheral(&self) -> &I { + &self.i2s_peripheral + } + /// Enable the I2S peripheral. pub fn enable(&mut self) { self.registers().i2scfgr.modify(|_, w| w.i2se().enabled()); From 2fe74e44997a1f6e977d27ba934312e8dc212c6d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 27 Apr 2022 00:41:20 +0200 Subject: [PATCH 21/91] add ctor and dtor --- src/lib.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0afc765..ebf2b79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -484,6 +484,28 @@ where } } +/// Constructors and Desctructors +impl I2sDriver +where + I: I2sPeripheral, +{ + /// Instantiate and configure an i2s driver. + pub fn new(i2s_peripheral: I, config: Config) -> I2sDriver { + config.i2s_driver(i2s_peripheral) + } + + /// Destroy the driver and release the owned i2s device. + pub fn release(self) -> I { + self.i2s_peripheral + } + + /// Consume the driver and create a new one with the given config + pub fn reconfigure(self, config: Config) -> I2sDriver { + let i2s_peripheral = self.release(); + config.i2s_driver(i2s_peripheral) + } +} + impl I2sDriver where I: I2sPeripheral, From d597fc62f0b2204597914b45bc227e0d55e877fb Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 27 Apr 2022 01:01:30 +0200 Subject: [PATCH 22/91] release unconfigured device, fix missing reset in instantiation --- src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ebf2b79..dbb5213 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,6 +300,7 @@ impl Config { pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { let driver = I2sDriver { i2s_peripheral }; 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) { @@ -494,14 +495,19 @@ where config.i2s_driver(i2s_peripheral) } - /// Destroy the driver and release the owned i2s device. + /// 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 config pub fn reconfigure(self, config: Config) -> I2sDriver { - let i2s_peripheral = self.release(); + let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } } From eb5da76ea2829a65ac9fe620ad3d4b54b87523cc Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 27 Apr 2022 01:24:24 +0200 Subject: [PATCH 23/91] fix, markers wasn't public --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dbb5213..47c7ba6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,10 +28,10 @@ use self::pac::spi1::{i2spr, sr}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; /// Marker, indicated master mode. -struct Master; +pub struct Master; /// Marker, indicate slave mode. -struct Slave; +pub struct Slave; /// The channel associated with a sample #[derive(Debug, Clone, PartialEq)] From a60f2930016f55d335258a4b06aa2442a7a9f8fe Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 27 Apr 2022 02:43:54 +0200 Subject: [PATCH 24/91] implement to master and slave conversion --- src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 47c7ba6..b344836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,6 +381,52 @@ impl Config { self.clock_polarity = polarity; self } + + /// Convert to a slave configuration. This delete Master Only Settings. + pub fn to_slave(self) -> Config { + let Self { + slave_or_master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + .. + } = self; + Config:: { + slave_or_master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock: false, + frequency: Frequency::Prescaler(false, 0b10), + _ms: PhantomData, + } + } + + /// Convert to a master configuration. + pub fn to_master(self) -> Config { + let Self { + slave_or_master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + .. + } = self; + Config:: { + slave_or_master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + _ms: PhantomData, + } + } } impl Config { From 1fbc0eca2482724c6411ffe2c76dfa82182a3f0d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 27 Apr 2022 02:44:39 +0200 Subject: [PATCH 25/91] fix, missing derive on marker --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b344836..0ccf67f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,9 +28,11 @@ use self::pac::spi1::{i2spr, sr}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; /// Marker, indicated master mode. +#[derive(Debug, Clone, Copy)] pub struct Master; /// Marker, indicate slave mode. +#[derive(Debug, Clone, Copy)] pub struct Slave; /// The channel associated with a sample From 30891493206ae19a17b6a94287f35a48f38ee82a Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 28 Apr 2022 16:20:32 +0200 Subject: [PATCH 26/91] finally,i think reset_regiters() would never used --- src/lib.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0ccf67f..b18017e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -522,15 +522,6 @@ where fn registers(&self) -> &RegisterBlock { unsafe { &*(I::REGISTERS as *const RegisterBlock) } } - - /// 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(); - } } /// Constructors and Desctructors From 9679e05cb1f6bf1428fb49b7589f3c7ff3ab015c Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 28 Apr 2022 16:23:41 +0200 Subject: [PATCH 27/91] delete an empty source --- src/ehal.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/ehal.rs diff --git a/src/ehal.rs b/src/ehal.rs deleted file mode 100644 index e69de29..0000000 From 573ad4796460b626670b0884577d14c185770680 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 28 Apr 2022 16:31:43 +0200 Subject: [PATCH 28/91] remove unused source files almost nothing remain from v0.2 --- src/config.rs | 168 --------------------------------- src/format.rs | 182 ----------------------------------- src/format/data24frame32.rs | 183 ------------------------------------ src/lib.rs | 1 - 4 files changed, 534 deletions(-) delete mode 100644 src/config.rs delete mode 100644 src/format.rs delete mode 100644 src/format/data24frame32.rs 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/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 b18017e..4a8ca8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ extern crate nb; extern crate vcell; //mod config; -pub mod format; mod pac; mod sealed { From ddac2aa52dfcf069119a2a6acaeaaa9b314388ca Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 29 Apr 2022 02:26:10 +0200 Subject: [PATCH 29/91] wip on ws_level --- src/lib.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a8ca8e..d54a312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,12 @@ pub enum Channel { Right, } +/// Level when reading an I2S WS Pin. +pub enum WsLevel { + High, + Low, +} + /// Content of the status register. pub struct Status { value: sr::R, @@ -496,8 +502,10 @@ pub unsafe trait I2sPeripheral { const REGISTERS: *const (); /// Get I2s clock source frequency from the I2s device. /// - /// Implemetors are allowed to panic in case i2s sourcefrequencey is unavailable. + /// Implemetors are allowed to panic in case i2s source frequencey is unavailable. fn i2s_freq(&self) -> u32; + /// get the level at WS pin. + fn ws_level(&self) -> WsLevel; } /// Driver of a SPI peripheral in I2S mode @@ -618,14 +626,9 @@ where self.registers().cr2.modify(|_, w| w.rxdmaen().bit(enabled)) } - /// Is the signal on WS line high ? - pub fn ws_is_high(&self) -> bool { - todo!() - } - - /// Is the signal on WS line low ? - pub fn ws_is_low(&self) -> bool { - todo!() + /// Get the level on the WS line. + pub fn ws_level(&self) -> WsLevel { + self.i2s_peripheral.ws_level() } //TODO method to get a handle to WS pin. It may usefull for setting an interrupt on pin to From 142bf83a17f6fee6f190d2a922b4b6dda5b3f796 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 2 May 2022 01:30:16 +0200 Subject: [PATCH 30/91] be closer to other hal convention to get pin state --- src/lib.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d54a312..4116cd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,12 +43,6 @@ pub enum Channel { Right, } -/// Level when reading an I2S WS Pin. -pub enum WsLevel { - High, - Low, -} - /// Content of the status register. pub struct Status { value: sr::R, @@ -504,8 +498,10 @@ pub unsafe trait I2sPeripheral { /// /// Implemetors are allowed to panic in case i2s source frequencey is unavailable. fn i2s_freq(&self) -> u32; - /// get the level at WS pin. - fn ws_level(&self) -> WsLevel; + /// 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; } /// Driver of a SPI peripheral in I2S mode @@ -626,9 +622,14 @@ where self.registers().cr2.modify(|_, w| w.rxdmaen().bit(enabled)) } - /// Get the level on the WS line. - pub fn ws_level(&self) -> WsLevel { - self.i2s_peripheral.ws_level() + /// 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 method to get a handle to WS pin. It may usefull for setting an interrupt on pin to From 08b7e85c57786b05acdd1772179ef6cdb48caf4f Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 2 May 2022 22:34:47 +0200 Subject: [PATCH 31/91] fix, select data format was missing --- src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4116cd0..10bf0fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -383,6 +383,12 @@ impl Config { 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) -> Config { let Self { @@ -563,6 +569,11 @@ where &self.i2s_peripheral } + /// Get a mutable reference to the underlaying 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()); From 61d491834ffe21e232a465b5737d41b83b054677 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 3 May 2022 01:51:48 +0200 Subject: [PATCH 32/91] fix errors when converting slave <-> master config --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 10bf0fb..e3da32b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,7 +392,6 @@ impl Config { /// Convert to a slave configuration. This delete Master Only Settings. pub fn to_slave(self) -> Config { let Self { - slave_or_master, transmit_or_receive, standard, clock_polarity, @@ -400,7 +399,7 @@ impl Config { .. } = self; Config:: { - slave_or_master, + slave_or_master: SlaveOrMaster::Slave, transmit_or_receive, standard, clock_polarity, @@ -414,7 +413,6 @@ impl Config { /// Convert to a master configuration. pub fn to_master(self) -> Config { let Self { - slave_or_master, transmit_or_receive, standard, clock_polarity, @@ -424,7 +422,7 @@ impl Config { .. } = self; Config:: { - slave_or_master, + slave_or_master: SlaveOrMaster::Master, transmit_or_receive, standard, clock_polarity, From ff3d33cbf5f29c8e678d9460c34e3a018a19af30 Mon Sep 17 00:00:00 2001 From: YruamaLairba Date: Thu, 5 May 2022 20:27:18 +0200 Subject: [PATCH 33/91] fix getting channel from status --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e3da32b..3ad4bd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ impl Status { /// This flag is updated when TXE or RXNE flags are set. This flag is meaningless and therefore /// not reliable is case of error or when using the PCM standard. pub fn chside(&self) -> Channel { - match self.value.udr().bit() { + match self.value.chside().bit() { false => Channel::Left, true => Channel::Right, } From eaab8f1389528b779ce0edf8807f2ce830fed20e Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 01:23:19 +0200 Subject: [PATCH 34/91] put my names into authors field --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 Date: Sat, 7 May 2022 17:55:33 +0200 Subject: [PATCH 35/91] add Transmit Receive marker to Config --- src/lib.rs | 69 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3ad4bd8..06eb584 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,14 @@ pub struct Master; #[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; + /// The channel associated with a sample #[derive(Debug, Clone, PartialEq)] pub enum Channel { @@ -175,7 +183,7 @@ impl Default for DataFormat { #[derive(Debug, Clone, Copy)] /// I2s Configuration builder. -pub struct Config { +pub struct Config { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, standard: I2sStandard, @@ -185,9 +193,10 @@ pub struct Config { frequency: Frequency, _ms: PhantomData, + _tr: PhantomData, } -impl Config { +impl Config { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { @@ -199,11 +208,12 @@ impl Config { master_clock: false, frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, + _tr: PhantomData, } } } -impl Config { +impl Config { /// Create a new default master configuration. pub fn new_master() -> Self { Self { @@ -215,6 +225,7 @@ impl Config { master_clock: false, frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, + _tr: PhantomData, } } } @@ -296,7 +307,7 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { } } -impl Config { +impl Config { /// Instantiate the driver. pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { let driver = I2sDriver { i2s_peripheral }; @@ -352,23 +363,41 @@ impl Config { } } -impl Default for Config { +impl Default for Config { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl Config { +impl Config { /// Configure in transmit mode - pub fn transmit(mut self) -> Self { - self.transmit_or_receive = TransmitOrReceive::Transmit; - self + pub fn transmit(self) -> Config { + Config:: { + 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, + } } /// Configure in transmit mode - pub fn receive(mut self) -> Self { - self.transmit_or_receive = TransmitOrReceive::Receive; - self + pub fn receive(self) -> Config { + Config:: { + 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, + } } /// Select the I2s standard to use pub fn standard(mut self, standard: I2sStandard) -> Self { @@ -390,7 +419,7 @@ impl Config { } /// Convert to a slave configuration. This delete Master Only Settings. - pub fn to_slave(self) -> Config { + pub fn to_slave(self) -> Config { let Self { transmit_or_receive, standard, @@ -398,7 +427,7 @@ impl Config { data_format, .. } = self; - Config:: { + Config:: { slave_or_master: SlaveOrMaster::Slave, transmit_or_receive, standard, @@ -407,11 +436,12 @@ impl Config { master_clock: false, frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, + _tr: PhantomData, } } /// Convert to a master configuration. - pub fn to_master(self) -> Config { + pub fn to_master(self) -> Config { let Self { transmit_or_receive, standard, @@ -421,7 +451,7 @@ impl Config { frequency, .. } = self; - Config:: { + Config:: { slave_or_master: SlaveOrMaster::Master, transmit_or_receive, standard, @@ -430,11 +460,12 @@ impl Config { master_clock, frequency, _ms: PhantomData, + _tr: PhantomData, } } } -impl Config { +impl Config { /// Enable/Disable Master Clock. Affect the effective sampling rate. /// /// This can be only set and only have meaning for Master mode. @@ -537,7 +568,7 @@ where I: I2sPeripheral, { /// Instantiate and configure an i2s driver. - pub fn new(i2s_peripheral: I, config: Config) -> I2sDriver { + pub fn new(i2s_peripheral: I, config: Config) -> I2sDriver { config.i2s_driver(i2s_peripheral) } @@ -552,7 +583,7 @@ where } /// Consume the driver and create a new one with the given config - pub fn reconfigure(self, config: Config) -> I2sDriver { + pub fn reconfigure(self, config: Config) -> I2sDriver { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } From d6e2efb87e2d2b36c82eba360d4da339a4fc1ae8 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 18:34:31 +0200 Subject: [PATCH 36/91] typestate the I2sDriver to prevent irrelevant operation --- src/lib.rs | 95 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 06eb584..e1f66b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -309,8 +309,14 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { impl Config { /// Instantiate the driver. - pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { - let driver = I2sDriver { i2s_peripheral }; + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { + let _ms = PhantomData; + let _tr = PhantomData; + let driver = I2sDriver:: { + i2s_peripheral, + _ms, + _tr, + }; driver.registers().cr1.reset(); // ensure SPI is disabled driver.registers().cr2.reset(); // disable interrupt and DMA request driver.registers().i2scfgr.write(|w| { @@ -548,11 +554,14 @@ pub unsafe trait I2sPeripheral { /// ```no_run /// ``` /// -pub struct I2sDriver { +pub struct I2sDriver { i2s_peripheral: I, + + _ms: PhantomData, + _tr: PhantomData, } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -563,12 +572,12 @@ where } /// Constructors and Desctructors -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { /// Instantiate and configure an i2s driver. - pub fn new(i2s_peripheral: I, config: Config) -> I2sDriver { + pub fn new(i2s_peripheral: I, config: Config) -> Self { config.i2s_driver(i2s_peripheral) } @@ -583,13 +592,17 @@ where } /// Consume the driver and create a new one with the given config - pub fn reconfigure(self, config: Config) -> I2sDriver { + #[allow(non_camel_case_types)] + pub fn reconfigure( + self, + config: Config, + ) -> I2sDriver { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -625,6 +638,30 @@ where } } + /// When set to `true`, an interrupt is generated each time an error occurs. + pub fn set_error_interrupt(&mut self, enabled: bool) { + self.registers().cr2.modify(|_, w| w.errie().bit(enabled)) + } + + /// 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 method to get a handle to WS pin. It may usefull for setting an interrupt on pin to + //synchronise I2s in slave mode +} + +/// 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. @@ -632,48 +669,36 @@ where self.registers().dr.write(|w| w.dr().bits(value)); } - /// 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 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`, 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`, an interrupt is generated each time an error occurs. - pub fn set_error_interrupt(&mut self, enabled: bool) { - self.registers().cr2.modify(|_, w| w.errie().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)) } +} - /// 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)) +/// 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() } - /// Return `true` if the level on the WS line is high. - pub fn ws_is_high(&self) -> bool { - self.i2s_peripheral.ws_is_high() + /// 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)) } - /// Return `true` if the level on the WS line is low. - pub fn ws_is_low(&self) -> bool { - self.i2s_peripheral.ws_is_low() + /// 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)) } - - //TODO method to get a handle to WS pin. It may usefull for setting an interrupt on pin to - //synchronise I2s in slave mode } #[cfg(test)] From 4881a2c99c68d5402fc93a592bf2d1b132d4d2c5 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 18:47:03 +0200 Subject: [PATCH 37/91] move markers in dedicated module --- src/lib.rs | 19 +++---------------- src/marker.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 src/marker.rs diff --git a/src/lib.rs b/src/lib.rs index e1f66b4..33cf454 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ extern crate vcell; //mod config; mod pac; +pub mod marker; +use marker::*; + mod sealed { pub trait Sealed {} } @@ -26,22 +29,6 @@ use self::pac::spi1::{i2spr, sr}; //use crate::format::{DataFormat, FrameFormat, FrameSync}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; -/// 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; - /// The channel associated with a sample #[derive(Debug, Clone, PartialEq)] pub enum Channel { diff --git a/src/marker.rs b/src/marker.rs new file mode 100644 index 0000000..bc27065 --- /dev/null +++ b/src/marker.rs @@ -0,0 +1,17 @@ +//! Markers for [`Config`](super::Config) and [`I2sDriver`](super::I2sDriver) + +/// 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; From bbbc88aed25da18d6cdda645e9f677adbbc7b89d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 19:11:15 +0200 Subject: [PATCH 38/91] Use a Mode marker to improve redability --- src/lib.rs | 27 ++++++++++++--------------- src/marker.rs | 8 ++++++++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 33cf454..567b2c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -296,13 +296,11 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { impl Config { /// Instantiate the driver. - pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { - let _ms = PhantomData; - let _tr = PhantomData; - let driver = I2sDriver:: { + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver> { + let _mode = PhantomData; + let driver = I2sDriver::> { i2s_peripheral, - _ms, - _tr, + _mode, }; driver.registers().cr1.reset(); // ensure SPI is disabled driver.registers().cr2.reset(); // disable interrupt and DMA request @@ -541,14 +539,13 @@ pub unsafe trait I2sPeripheral { /// ```no_run /// ``` /// -pub struct I2sDriver { +pub struct I2sDriver { i2s_peripheral: I, - _ms: PhantomData, - _tr: PhantomData, + _mode: PhantomData, } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -559,7 +556,7 @@ where } /// Constructors and Desctructors -impl I2sDriver +impl I2sDriver> where I: I2sPeripheral, { @@ -583,13 +580,13 @@ where pub fn reconfigure( self, config: Config, - ) -> I2sDriver { + ) -> I2sDriver> { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -645,7 +642,7 @@ where } /// Transmit only methods -impl I2sDriver +impl I2sDriver> where I: I2sPeripheral, { @@ -668,7 +665,7 @@ where } /// Receive only methods -impl I2sDriver +impl I2sDriver> where I: I2sPeripheral, { diff --git a/src/marker.rs b/src/marker.rs index bc27065..365f132 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -1,4 +1,12 @@ //! Markers for [`Config`](super::Config) and [`I2sDriver`](super::I2sDriver) +use core::marker::PhantomData; + +/// Marker, indicate operation mode of the I2sDriver. +#[derive(Debug, Clone, Copy)] +pub struct Mode { + _ms: PhantomData, + _tr: PhantomData, +} /// Marker, indicated master mode. #[derive(Debug, Clone, Copy)] From b7dc179f0e20a6c8664db996b5835d27a9a50d59 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 19:21:38 +0200 Subject: [PATCH 39/91] fix, wrong marker for receive methods --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 567b2c1..5010bfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -665,7 +665,7 @@ where } /// Receive only methods -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { From be5c547951403ea81397872a5d0decfe35113ca0 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 20:55:09 +0200 Subject: [PATCH 40/91] method to get actual sample rate --- src/lib.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5010bfc..b5a815c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -685,6 +685,31 @@ where } } +/// Receive only methods +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 { #[test] From 99a8b4290537e9de75f5ed3c6b7e442f10084f43 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 22:03:58 +0200 Subject: [PATCH 41/91] remove set_error_interrupt for master transmit mode --- src/lib.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b5a815c..96a84e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -622,11 +622,6 @@ where } } - /// When set to `true`, an interrupt is generated each time an error occurs. - pub fn set_error_interrupt(&mut self, enabled: bool) { - self.registers().cr2.modify(|_, w| w.errie().bit(enabled)) - } - /// Return `true` if the level on the WS line is high. pub fn ws_is_high(&self) -> bool { self.i2s_peripheral.ws_is_high() @@ -685,7 +680,33 @@ where } } -/// Receive only methods +/// 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, From 35265bcd08ee0f349f3fed22be5b18714bf18ef0 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 23:06:28 +0200 Subject: [PATCH 42/91] use marker on status to prevent getting irrelevant flags --- src/lib.rs | 119 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 96a84e6..ed173f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,44 +39,18 @@ pub enum Channel { } /// Content of the status register. -pub struct Status { +pub struct Status { value: sr::R, + _ms: PhantomData, + _tr: PhantomData, } -impl Status { - /// Get the FRE flag. If `true` a frame error occured. - /// - /// This flag can be set by hardware only if the I2sDriver is configured in Slave mode. It is set - /// when the WS line change at an unexpected moment. Usually, this indicate a synchronisation - /// issue. This flag is cleared when reading the status register. - pub fn fre(&self) -> bool { - self.value.fre().bit() - } - +impl Status { /// Get the BSY flag. If `true` the I2s device is busy communicating. pub fn bsy(&self) -> bool { self.value.bsy().bit() } - /// Get the OVR flag. If `true` an overrun error occured. - /// - /// 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. 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 UDR flag. If `true` an underrun error occured. - /// - /// This flag can be set only in slave transmission mode. It 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 is cleared by reading the status register. - pub fn udr(&self) -> bool { - self.value.udr().bit() - } - /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. Have /// no meaning with PCM standard. /// @@ -88,23 +62,68 @@ impl Status { true => Channel::Right, } } +} - /// Get the TXE flag. If `true` the Tx buffer is empty and the next data can be loaded into it. +impl Status { + /// Get the FRE flag. If `true` a frame error occurred. /// - /// This flag can be set only in transmision 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() + /// 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 be only set in reception mode. It is cleared when the data register is read. + /// 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, @@ -612,16 +631,6 @@ where self.registers().i2scfgr.modify(|_, w| w.i2se().disabled()); } - /// 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(), - } - } - /// Return `true` if the level on the WS line is high. pub fn ws_is_high(&self) -> bool { self.i2s_peripheral.ws_is_high() @@ -636,6 +645,24 @@ where //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, + } + } +} + /// Transmit only methods impl I2sDriver> where From c476d56002efc20e73c97034228c0142f293bcff Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 23:11:06 +0200 Subject: [PATCH 43/91] orthograph and typo fixes --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ed173f0..10aa8c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -501,7 +501,7 @@ impl Config { pub fn prescaler(mut self, odd: bool, div: u8) -> Self { #[allow(clippy::manual_range_contains)] if div < 2 { - panic!("div is less than 2, frobidden value") + panic!("div is less than 2, forbidden value") } self.frequency = Frequency::Prescaler(odd, div); self @@ -515,7 +515,7 @@ impl Config { /// Require exactly this audio sampling frequency. /// - /// If the required frequency can not bet set, Instatiate the driver will produce a error + /// If the required frequency can not bet set, Instantiate the driver will produce a error pub fn require_frequency(mut self, freq: u32) -> Self { self.frequency = Frequency::Require(freq); self @@ -541,7 +541,7 @@ pub unsafe trait I2sPeripheral { const REGISTERS: *const (); /// Get I2s clock source frequency from the I2s device. /// - /// Implemetors are allowed to panic in case i2s source frequencey is unavailable. + /// 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; @@ -574,7 +574,7 @@ where } } -/// Constructors and Desctructors +/// Constructors and Destructors impl I2sDriver> where I: I2sPeripheral, @@ -594,7 +594,7 @@ where self.i2s_peripheral } - /// Consume the driver and create a new one with the given config + /// Consume the driver and create a new one with the given configuration. #[allow(non_camel_case_types)] pub fn reconfigure( self, @@ -609,12 +609,12 @@ impl I2sDriver where I: I2sPeripheral, { - /// Get a reference to the underlaying i2s device + /// Get a reference to the underlying i2s device pub fn i2s_peripheral(&self) -> &I { &self.i2s_peripheral } - /// Get a mutable reference to the underlaying i2s device + /// Get a mutable reference to the underlying i2s device pub fn i2s_peripheral_mut(&mut self) -> &mut I { &mut self.i2s_peripheral } @@ -641,7 +641,7 @@ where self.i2s_peripheral.ws_is_low() } - //TODO method to get a handle to WS pin. It may usefull for setting an interrupt on pin to + //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 } From 2654588a6bbfc4e87ecbf8a4dfa82849521cac32 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 7 May 2022 23:18:30 +0200 Subject: [PATCH 44/91] documment some generic parameters --- src/lib.rs | 6 ++++++ src/marker.rs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 10aa8c3..16169fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,9 @@ pub enum Channel { } /// Content of the status register. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` pub struct Status { value: sr::R, _ms: PhantomData, @@ -189,6 +192,9 @@ impl Default for DataFormat { #[derive(Debug, Clone, Copy)] /// I2s Configuration builder. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` pub struct Config { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, diff --git a/src/marker.rs b/src/marker.rs index 365f132..749b627 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -2,6 +2,9 @@ use core::marker::PhantomData; /// Marker, indicate operation mode of the I2sDriver. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` #[derive(Debug, Clone, Copy)] pub struct Mode { _ms: PhantomData, From 1958a3c274816b316461bd24aa0761c12e10063d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 9 May 2022 21:40:45 +0200 Subject: [PATCH 45/91] TransferConfig that wrap driver config --- src/lib.rs | 3 + src/transfer.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/transfer.rs diff --git a/src/lib.rs b/src/lib.rs index 16169fd..a3e5f93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,9 @@ mod sealed { use core::marker::PhantomData; +mod transfer; +pub use transfer::*; + //pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; use self::pac::spi1::RegisterBlock; use self::pac::spi1::{i2spr, sr}; diff --git a/src/transfer.rs b/src/transfer.rs new file mode 100644 index 0000000..997d305 --- /dev/null +++ b/src/transfer.rs @@ -0,0 +1,148 @@ +//! Abstraction for I2S transfer +//! +//! + +use crate::Config as DriverConfig; +use crate::I2sDriver as Driver; +use crate::*; + +#[derive(Debug, Clone, Copy)] +/// I2s TransferConfiguration builder. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +pub struct TransferConfig { + driver_config: DriverConfig, +} + +impl TransferConfig { + /// Create a new default slave configuration. + pub fn new_slave() -> Self { + Self { + driver_config: DriverConfig::new_slave(), + } + } +} + +impl TransferConfig { + /// Create a new default master configuration. + pub fn new_master() -> Self { + Self { + driver_config: DriverConfig::new_master(), + } + } +} + +impl TransferConfig { + /// Create a `Transfer` object. + pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer> { + let driver = self.driver_config.i2s_driver(i2s_peripheral); + Transfer::> { driver } + } +} + +impl Default for TransferConfig { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl TransferConfig { + /// Configure transfert for transmission. + pub fn transmit(self) -> TransferConfig { + TransferConfig:: { + driver_config: self.driver_config.transmit(), + } + } + /// TransferConfigure in transmit mode + pub fn receive(self) -> TransferConfig { + TransferConfig:: { + driver_config: self.driver_config.receive(), + } + } + /// Select the I2s standard to use + pub fn standard(self, standard: I2sStandard) -> Self { + TransferConfig:: { + driver_config: self.driver_config.standard(standard), + } + } + /// Select steady state clock polarity + pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { + TransferConfig:: { + driver_config: self.driver_config.clock_polarity(polarity), + } + } + + /// Select data format + pub fn data_format(self, format: DataFormat) -> Self { + TransferConfig:: { + driver_config: self.driver_config.data_format(format), + } + } + + /// Convert to a slave configuration. This delete Master Only Settings. + pub fn to_slave(self) -> TransferConfig { + TransferConfig:: { + driver_config: self.driver_config.to_slave(), + } + } + + /// Convert to a master configuration. + pub fn to_master(self) -> TransferConfig { + TransferConfig:: { + driver_config: self.driver_config.to_master(), + } + } +} + +impl TransferConfig { + /// 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 { + TransferConfig:: { + driver_config: self.driver_config.master_clock(enable), + } + } + + /// TransferConfigure 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(self, odd: bool, div: u8) -> Self { + TransferConfig:: { + driver_config: self.driver_config.prescaler(odd, div), + } + } + + /// Request an audio sampling frequency. The effective audio sampling frequency may differ. + pub fn request_frequency(self, freq: u32) -> Self { + TransferConfig:: { + driver_config: self.driver_config.request_frequency(freq), + } + } + + /// Require exactly this audio sampling frequency. + /// + /// If the required frequency can not bet set, Instantiate the driver will produce a error + pub fn require_frequency(self, freq: u32) -> Self { + TransferConfig:: { + driver_config: self.driver_config.require_frequency(freq), + } + } +} + +pub struct Transfer { + driver: Driver, +} From ab348774532bcf737e956390284e5ddc2290beb1 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 11 May 2022 20:15:04 +0200 Subject: [PATCH 46/91] POC of writer_iter function --- src/transfer.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/transfer.rs b/src/transfer.rs index 997d305..8f9bddc 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -143,6 +143,92 @@ impl TransferConfig { } } -pub struct Transfer { +/// Part of the frame we currently transmitting or receiving +#[derive(Debug)] +enum FrameState { + LeftMsb, + LeftLsb, + RightMsb, + RightLsb, +} +use FrameState::*; + +pub struct Transfer +where + I: I2sPeripheral, +{ driver: Driver, } + +/// Constructors and Destructors +impl Transfer> +where + I: I2sPeripheral, +{ + /// Instantiate and configure an i2s driver. + pub fn new(i2s_peripheral: I, config: TransferConfig) -> 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 Transfer> +where + I: I2sPeripheral, +{ + pub fn sample_rate(&self) -> u32 { + self.driver.sample_rate() + } +} + +impl Transfer> +where + I: I2sPeripheral, +{ + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut frame_state = LeftMsb; + let mut frame = (0, 0); + let mut samples = samples.into_iter(); + self.driver.disable(); + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.txe() { + let data; + match frame_state { + LeftMsb => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + frame = smpl.unwrap(); + data = (frame.0 as u32 >> 16) as u16; + frame_state = LeftLsb; + } + LeftLsb => { + data = (frame.0 as u32 & 0xFFFF) as u16; + frame_state = RightMsb; + } + RightMsb => { + data = (frame.1 as u32 >> 16) as u16; + frame_state = RightLsb; + } + RightLsb => { + data = (frame.1 as u32 & 0xFFFF) as u16; + frame_state = LeftMsb; + } + } + self.driver.write_data_register(data); + } + } + self.driver.disable(); + } +} From 7bddc998499c06406bf2674ab8dd269e6606ab79 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 12 May 2022 15:44:53 +0200 Subject: [PATCH 47/91] POC, non blocking write --- src/transfer.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/transfer.rs b/src/transfer.rs index 8f9bddc..23a9d2a 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -1,6 +1,8 @@ //! Abstraction for I2S transfer //! //! +use core::convert::Infallible; +use nb::Error::WouldBlock; use crate::Config as DriverConfig; use crate::I2sDriver as Driver; @@ -37,7 +39,11 @@ impl TransferConfig { /// Create a `Transfer` object. pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer> { let driver = self.driver_config.i2s_driver(i2s_peripheral); - Transfer::> { driver } + Transfer::> { + driver, + frame: Default::default(), + frame_state: FrameState::LeftMsb, + } } } @@ -158,6 +164,8 @@ where I: I2sPeripheral, { driver: Driver, + frame: (i32, i32), + frame_state: FrameState, } /// Constructors and Destructors @@ -176,6 +184,23 @@ where } } +impl Transfer> +where + I: I2sPeripheral, +{ + /// 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; + } +} + impl Transfer> where I: I2sPeripheral, @@ -231,4 +256,40 @@ where } 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> { + 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 = (frame.1 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; + } + } + } + Err(WouldBlock) + } } From 157d2757f6f1e73032d4b9f987957504b32dfdd3 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 00:14:06 +0200 Subject: [PATCH 48/91] remove the "Mode" on Transfer, this trick is actually a bad idea --- src/transfer.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 23a9d2a..83bba3c 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -37,9 +37,9 @@ impl TransferConfig { impl TransferConfig { /// Create a `Transfer` object. - pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer> { + pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer { let driver = self.driver_config.i2s_driver(i2s_peripheral); - Transfer::> { + Transfer:: { driver, frame: Default::default(), frame_state: FrameState::LeftMsb, @@ -159,17 +159,17 @@ enum FrameState { } use FrameState::*; -pub struct Transfer +pub struct Transfer where I: I2sPeripheral, { - driver: Driver, + driver: Driver>, frame: (i32, i32), frame_state: FrameState, } /// Constructors and Destructors -impl Transfer> +impl Transfer where I: I2sPeripheral, { @@ -184,7 +184,7 @@ where } } -impl Transfer> +impl Transfer where I: I2sPeripheral, { @@ -201,7 +201,7 @@ where } } -impl Transfer> +impl Transfer where I: I2sPeripheral, { @@ -210,7 +210,7 @@ where } } -impl Transfer> +impl Transfer where I: I2sPeripheral, { From bf4fa1c9fe18f085e5227e6d77b80ec07803fbee Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 00:15:08 +0200 Subject: [PATCH 49/91] not working slave transmit transfer write --- src/transfer.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 83bba3c..6c85eeb 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -293,3 +293,70 @@ where Err(WouldBlock) } } + +impl Transfer +where + I: I2sPeripheral, +{ + #[inline] + // Can't make it work now + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut frame_state = LeftMsb; + let mut frame = (0, 0); + let mut samples = samples.into_iter(); + self.driver.disable(); + self.driver.status(); + // initial synchronisation + while !self.driver.ws_is_high() {} + self.driver.enable(); + loop { + let status = self.driver.status(); + if status.txe() { + let data; + match (frame_state, status.chside()) { + (LeftMsb, Channel::Left) => { + let smpl = samples.next(); + //breaking here ensure the last frame is fully transmitted + if smpl.is_none() { + break; + } + frame = smpl.unwrap(); + data = (frame.0 as u32 >> 16) as u16; + frame_state = LeftLsb; + } + (LeftLsb, _) => { + data = (frame.0 as u32 & 0xFFFF) as u16; + frame_state = RightMsb; + } + (RightMsb, _) => { + data = (frame.1 as u32 >> 16) as u16; + frame_state = RightLsb; + } + (RightLsb, _) => { + data = (frame.1 as u32 & 0xFFFF) as u16; + frame_state = LeftMsb; + } + _ => { + data = 0; + frame_state = LeftMsb; + } + } + self.driver.write_data_register(data); + } + if status.fre() { + rtt_target::rprintln!("{} {}", status.fre(), status.udr()); + self.driver.disable(); + frame_state = LeftMsb; + while !self.driver.ws_is_high() {} + self.driver.enable(); + } + if status.udr() { + rtt_target::rprintln!("udr"); + } + } + self.driver.disable(); + } +} From 83c159afe781cfe2344c837828ba04f57ef632cd Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 04:04:00 +0200 Subject: [PATCH 50/91] fix write_iter to avoid problem when mixing with nb version --- src/marker.rs | 56 ++++++++++++++++++++++++ src/transfer.rs | 111 ++++++++++++++++++++++++++++-------------------- 2 files changed, 120 insertions(+), 47 deletions(-) diff --git a/src/marker.rs b/src/marker.rs index 749b627..12d4cfe 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -1,6 +1,8 @@ //! Markers for [`Config`](super::Config) and [`I2sDriver`](super::I2sDriver) use core::marker::PhantomData; +use crate::sealed::Sealed; + /// Marker, indicate operation mode of the I2sDriver. /// /// - `MS`: `Master` or `Slave` @@ -26,3 +28,57 @@ 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; + +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 {} + +/// 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 { + /// Runtime value. + const VALUE: crate::DataFormat; +} + +macro_rules! impl_data_format{ + ($($marker:ident),*) => { + $( + impl DataFormat for $marker { + const VALUE: crate::DataFormat = crate::DataFormat::$marker; + } + )* + }; +} + +impl_data_format!( + Data16Channel16, + Data16Channel32, + Data24Channel32, + Data32Channel32 +); diff --git a/src/transfer.rs b/src/transfer.rs index 6c85eeb..cd31dba 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -13,102 +13,119 @@ use crate::*; /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` -pub struct TransferConfig { +/// - `FMT`: Frame Format marker, eg `Data16Channel16` +pub struct TransferConfig { driver_config: DriverConfig, + _fmt: PhantomData, } -impl TransferConfig { +impl TransferConfig { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { driver_config: DriverConfig::new_slave(), + _fmt: PhantomData, } } } -impl TransferConfig { +impl TransferConfig { /// Create a new default master configuration. pub fn new_master() -> Self { Self { driver_config: DriverConfig::new_master(), + _fmt: PhantomData, } } } -impl TransferConfig { +impl TransferConfig { /// Create a `Transfer` object. - pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer { + pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer { let driver = self.driver_config.i2s_driver(i2s_peripheral); - Transfer:: { + Transfer:: { driver, frame: Default::default(), frame_state: FrameState::LeftMsb, + _fmt: PhantomData, } } } -impl Default for TransferConfig { +impl Default for TransferConfig { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl TransferConfig { +impl TransferConfig { /// Configure transfert for transmission. - pub fn transmit(self) -> TransferConfig { - TransferConfig:: { + pub fn transmit(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.transmit(), + _fmt: PhantomData, } } /// TransferConfigure in transmit mode - pub fn receive(self) -> TransferConfig { - TransferConfig:: { + pub fn receive(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.receive(), + _fmt: PhantomData, } } /// Select the I2s standard to use pub fn standard(self, standard: I2sStandard) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.standard(standard), + _fmt: PhantomData, } } /// Select steady state clock polarity pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.clock_polarity(polarity), + _fmt: PhantomData, } } /// Select data format - pub fn data_format(self, format: DataFormat) -> Self { - TransferConfig:: { - driver_config: self.driver_config.data_format(format), + #[allow(non_camel_case_types)] + pub fn data_format(self, _format: NEW_FMT) -> TransferConfig + where + NEW_FMT: marker::DataFormat, + { + TransferConfig:: { + 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) -> TransferConfig { - TransferConfig:: { + pub fn to_slave(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.to_slave(), + _fmt: PhantomData, } } /// Convert to a master configuration. - pub fn to_master(self) -> TransferConfig { - TransferConfig:: { + pub fn to_master(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.to_master(), + _fmt: PhantomData, } } } -impl TransferConfig { +impl TransferConfig { /// 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 { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.master_clock(enable), + _fmt: PhantomData, } } @@ -127,15 +144,17 @@ impl TransferConfig { /// /// `div` must be at least 2, otherwise the method panics. pub fn prescaler(self, odd: bool, div: u8) -> Self { - TransferConfig:: { + TransferConfig:: { 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 { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.request_frequency(freq), + _fmt: PhantomData, } } @@ -143,8 +162,9 @@ impl TransferConfig { /// /// If the required frequency can not bet set, Instantiate the driver will produce a error pub fn require_frequency(self, freq: u32) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.require_frequency(freq), + _fmt: PhantomData, } } } @@ -159,22 +179,23 @@ enum FrameState { } use FrameState::*; -pub struct Transfer +pub struct Transfer where I: I2sPeripheral, { driver: Driver>, frame: (i32, i32), frame_state: FrameState, + _fmt: PhantomData, } /// Constructors and Destructors -impl Transfer +impl Transfer where I: I2sPeripheral, { /// Instantiate and configure an i2s driver. - pub fn new(i2s_peripheral: I, config: TransferConfig) -> Self { + pub fn new(i2s_peripheral: I, config: TransferConfig) -> Self { config.i2s_transfer(i2s_peripheral) } @@ -184,7 +205,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { @@ -201,7 +222,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { @@ -210,7 +231,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { @@ -218,43 +239,39 @@ where where ITER: IntoIterator, { - let mut frame_state = LeftMsb; - let mut frame = (0, 0); let mut samples = samples.into_iter(); - self.driver.disable(); self.driver.enable(); loop { let status = self.driver.status(); if status.txe() { let data; - match frame_state { + match self.frame_state { LeftMsb => { let smpl = samples.next(); //breaking here ensure the last frame is fully transmitted if smpl.is_none() { break; } - frame = smpl.unwrap(); - data = (frame.0 as u32 >> 16) as u16; - frame_state = LeftLsb; + self.frame = smpl.unwrap(); + data = (self.frame.0 as u32 >> 16) as u16; + self.frame_state = LeftLsb; } LeftLsb => { - data = (frame.0 as u32 & 0xFFFF) as u16; - frame_state = RightMsb; + data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.frame_state = RightMsb; } RightMsb => { - data = (frame.1 as u32 >> 16) as u16; - frame_state = RightLsb; + data = (self.frame.1 as u32 >> 16) as u16; + self.frame_state = RightLsb; } RightLsb => { - data = (frame.1 as u32 & 0xFFFF) as u16; - frame_state = LeftMsb; + data = (self.frame.1 as u32 & 0xFFFF) as u16; + self.frame_state = LeftMsb; } } self.driver.write_data_register(data); } } - self.driver.disable(); } /// Write one audio frame. Activate the I2s interface if disabled. @@ -294,7 +311,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { From 3c55d7ad469e5e152c665be99035ae4dce8c5a15 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 16:44:53 +0200 Subject: [PATCH 51/91] impl 16 bit master transmit transfer --- src/marker.rs | 13 +++++---- src/transfer.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/marker.rs b/src/marker.rs index 12d4cfe..b5b2ef5 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -64,21 +64,24 @@ impl Data16 for Data16Channel32 {} pub trait DataFormat: Sealed { /// Runtime value. const VALUE: crate::DataFormat; + /// Audio frame representation from API point of view; + type AudioFrame: Default; } macro_rules! impl_data_format{ - ($($marker:ident),*) => { + ($(($marker:ident,$audio_frame:ty)),*) => { $( impl DataFormat for $marker { const VALUE: crate::DataFormat = crate::DataFormat::$marker; + type AudioFrame = $audio_frame; } )* }; } impl_data_format!( - Data16Channel16, - Data16Channel32, - Data24Channel32, - Data32Channel32 + (Data16Channel16, (i16, i16)), + (Data16Channel32, (i16, i16)), + (Data24Channel32, (i32, i32)), + (Data32Channel32, (i32, i32)) ); diff --git a/src/transfer.rs b/src/transfer.rs index cd31dba..95cc12e 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -39,7 +39,10 @@ impl TransferConfig { } } -impl TransferConfig { +impl TransferConfig +where + FMT: DataFormat, +{ /// Create a `Transfer` object. pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer { let driver = self.driver_config.i2s_driver(i2s_peripheral); @@ -182,9 +185,10 @@ use FrameState::*; pub struct Transfer where I: I2sPeripheral, + FMT: DataFormat, { driver: Driver>, - frame: (i32, i32), + frame: FMT::AudioFrame, frame_state: FrameState, _fmt: PhantomData, } @@ -193,6 +197,7 @@ where impl Transfer where I: I2sPeripheral, + FMT: DataFormat, { /// Instantiate and configure an i2s driver. pub fn new(i2s_peripheral: I, config: TransferConfig) -> Self { @@ -208,6 +213,7 @@ where impl Transfer where I: I2sPeripheral, + FMT: DataFormat, { /// Activate the I2s interface. pub fn begin(&mut self) { @@ -225,12 +231,78 @@ where impl Transfer where I: I2sPeripheral, + FMT: DataFormat, { pub fn sample_rate(&self) -> u32 { self.driver.sample_rate() } } +impl Transfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, +{ + 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 Transfer where I: I2sPeripheral, From 5cbed6ecc0994302fc2621654b0784cddff4c5fd Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 17:53:45 +0200 Subject: [PATCH 52/91] 16 bits slave transmit write_iter poc --- src/transfer.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 95cc12e..65d2b3c 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -50,6 +50,7 @@ where driver, frame: Default::default(), frame_state: FrameState::LeftMsb, + sync: false, _fmt: PhantomData, } } @@ -190,6 +191,7 @@ where driver: Driver>, frame: FMT::AudioFrame, frame_state: FrameState, + sync: bool, _fmt: PhantomData, } @@ -225,6 +227,7 @@ where self.driver.disable(); self.frame = Default::default(); self.frame_state = FrameState::LeftMsb; + self.sync = false; } } @@ -383,6 +386,56 @@ where } } +impl Transfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, +{ + //TODO WS_line sensing is protocol dependent + pub fn write_iter(&mut self, samples: ITER) + where + ITER: IntoIterator, + { + let mut samples = samples.into_iter(); + self.driver.enable(); + 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(); + self.frame_state = FrameState::LeftMsb; + } + } else if self.driver.ws_is_high() { + self.sync = true; + self.driver.disable(); + self.driver.enable() + } + } + } +} + impl Transfer where I: I2sPeripheral, From 89d4c1eec3794ce89c3a94f44f3b68c2fbb5ae41 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 13 May 2022 18:11:28 +0200 Subject: [PATCH 53/91] 16 bit slave transmit non blocking write Poc --- src/transfer.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 65d2b3c..99873b7 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -434,6 +434,44 @@ where } } } + /// 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(); + if self.sync { + let status = self.driver.status(); + if status.txe() { + let data; + 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 => { + 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(); + self.frame_state = FrameState::LeftMsb; + } + } else if self.driver.ws_is_high() { + self.sync = true; + self.driver.disable(); + self.driver.enable() + } + Err(WouldBlock) + } } impl Transfer From a3fbb1939906f7a8a3ecfbbc0d0b32ceb3b9ab3c Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 14 May 2022 22:58:03 +0200 Subject: [PATCH 54/91] fix i32 slave transmission, write data before enable --- src/transfer.rs | 141 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 41 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 99873b7..c484789 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -484,59 +484,118 @@ where where ITER: IntoIterator, { - let mut frame_state = LeftMsb; - let mut frame = (0, 0); let mut samples = samples.into_iter(); - self.driver.disable(); - self.driver.status(); - // initial synchronisation - while !self.driver.ws_is_high() {} - self.driver.enable(); loop { - let status = self.driver.status(); - if status.txe() { - let data; - match (frame_state, status.chside()) { - (LeftMsb, Channel::Left) => { - let smpl = samples.next(); - //breaking here ensure the last frame is fully transmitted - if smpl.is_none() { - break; + 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; } - frame = smpl.unwrap(); - data = (frame.0 as u32 >> 16) as u16; - frame_state = LeftLsb; } - (LeftLsb, _) => { - data = (frame.0 as u32 & 0xFFFF) as u16; - frame_state = RightMsb; + self.driver.write_data_register(data); + } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + self.frame_state = FrameState::LeftMsb; + } + } else if self.driver.ws_is_high() { + self.driver.disable(); + // 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.driver.ws_is_high() { + 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> { + self.driver.enable(); + 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(()); } - (RightMsb, _) => { - data = (frame.1 as u32 >> 16) as u16; - frame_state = RightLsb; + LeftLsb => { + let data = (self.frame.0 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = RightMsb; } - (RightLsb, _) => { - data = (frame.1 as u32 & 0xFFFF) as u16; - frame_state = LeftMsb; + RightMsb => { + let data = (self.frame.1 as u32 >> 16) as u16; + self.driver.write_data_register(data); + self.frame_state = RightLsb; } - _ => { - data = 0; - frame_state = LeftMsb; + RightLsb => { + let data = (frame.1 as u32 & 0xFFFF) as u16; + self.driver.write_data_register(data); + self.frame_state = LeftMsb; } } - self.driver.write_data_register(data); } - if status.fre() { - rtt_target::rprintln!("{} {}", status.fre(), status.udr()); + } else if self.driver.ws_is_high() { + self.driver.disable(); + // 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.driver.ws_is_high() { + self.sync = true; + } else { self.driver.disable(); - frame_state = LeftMsb; - while !self.driver.ws_is_high() {} - self.driver.enable(); - } - if status.udr() { - rtt_target::rprintln!("udr"); } + return Ok(()); } - self.driver.disable(); + Err(WouldBlock) } } From e0f8b94d045702761905983116b92666ab4eb828 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 14 May 2022 23:41:11 +0200 Subject: [PATCH 55/91] forgot testing flags, --- src/transfer.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/transfer.rs b/src/transfer.rs index c484789..f5443b2 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -519,7 +519,6 @@ where if status.fre() || status.udr() { self.sync = false; self.driver.disable(); - self.frame_state = FrameState::LeftMsb; } } else if self.driver.ws_is_high() { self.driver.disable(); @@ -580,6 +579,10 @@ where } } } + if status.fre() || status.udr() { + self.sync = false; + self.driver.disable(); + } } else if self.driver.ws_is_high() { self.driver.disable(); // data register may (or not) already contain data, causing uncertainty about next From 25b3901e1d895b3ab5c3a631f91c05842ca9dd32 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 15 May 2022 00:36:50 +0200 Subject: [PATCH 56/91] remove adriver enable that cause issue --- src/transfer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/transfer.rs b/src/transfer.rs index f5443b2..db6a82c 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -550,7 +550,6 @@ where /// 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(); if self.sync { let status = self.driver.status(); if status.txe() { From 776aeb96b3897b38ac5a39f7851417858646a8b5 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 15 May 2022 01:14:03 +0200 Subject: [PATCH 57/91] remove useless disable --- src/transfer.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index db6a82c..de6d8d3 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -521,7 +521,6 @@ where self.driver.disable(); } } else if self.driver.ws_is_high() { - self.driver.disable(); // 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(); @@ -533,7 +532,6 @@ where 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.driver.ws_is_high() { @@ -583,7 +581,6 @@ where self.driver.disable(); } } else if self.driver.ws_is_high() { - self.driver.disable(); // 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; From d16fe3791447479a2c0657637fea8f20a0e626cc Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 15 May 2022 01:22:59 +0200 Subject: [PATCH 58/91] transpose i32 slave transmit logic back to i16 --- src/transfer.rs | 52 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index de6d8d3..6ee9d58 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -397,7 +397,6 @@ where ITER: IntoIterator, { let mut samples = samples.into_iter(); - self.driver.enable(); loop { if self.sync { let status = self.driver.status(); @@ -411,11 +410,11 @@ where break; } self.frame = smpl.unwrap(); - data = (self.frame.0) as u16; + data = self.frame.0 as u16; self.frame_state = RightMsb; } RightMsb => { - data = (self.frame.1) as u16; + data = self.frame.1 as u16; self.frame_state = LeftMsb; } _ => unreachable!(), @@ -425,12 +424,26 @@ where if status.fre() || status.udr() { self.sync = false; self.driver.disable(); - self.frame_state = FrameState::LeftMsb; } } else if self.driver.ws_is_high() { - self.sync = true; - self.driver.disable(); - self.driver.enable() + // 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.driver.ws_is_high() { + self.sync = true; + } else { + self.driver.disable(); + } } } } @@ -439,36 +452,43 @@ where /// 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(); if self.sync { let status = self.driver.status(); if status.txe() { - let data; match self.frame_state { LeftMsb => { self.frame = frame; - let data = (self.frame.0) as u16; + let data = self.frame.0 as u16; self.driver.write_data_register(data); self.frame_state = RightMsb; return Ok(()); } RightMsb => { - data = (self.frame.1) as u16; + let data = self.frame.1 as u16; + self.driver.write_data_register(data); self.frame_state = LeftMsb; } _ => unreachable!(), } - self.driver.write_data_register(data); } if status.fre() || status.udr() { self.sync = false; self.driver.disable(); - self.frame_state = FrameState::LeftMsb; } } else if self.driver.ws_is_high() { - self.sync = true; - self.driver.disable(); - self.driver.enable() + // 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.driver.ws_is_high() { + self.sync = true; + } else { + self.driver.disable(); + } + return Ok(()); } Err(WouldBlock) } From 2c548919d34ebb0ca91224abbb8ddd6ac54a79ae Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 15 May 2022 02:53:59 +0200 Subject: [PATCH 59/91] add I2S standard to the transfer typestate --- src/marker.rs | 54 ++++++++++++++++++++++++++++ src/transfer.rs | 96 ++++++++++++++++++++++++++++++------------------- 2 files changed, 114 insertions(+), 36 deletions(-) diff --git a/src/marker.rs b/src/marker.rs index b5b2ef5..5f56ee6 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -45,6 +45,26 @@ pub struct Data24Channel32; #[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 {} @@ -53,6 +73,11 @@ 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` @@ -85,3 +110,32 @@ impl_data_format!( (Data24Channel32, (i32, i32)), (Data32Channel32, (i32, i32)) ); + +/// Trait for marker indicating a i2s standard. +pub trait I2sStandard: Sealed { + /// Runtime value. + const VALUE: crate::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::I2sStandard = crate::I2sStandard::$marker; + const WS_START_LEVEL: bool = $ws_start_level; + } + )* + }; +} + +impl_i2s_standard!( + (Philips, false), + (Msb, true), + (Lsb, true), + (PcmShortSync, true), + (PcmLongSync, true) +); diff --git a/src/transfer.rs b/src/transfer.rs index 6ee9d58..5cdf432 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -13,122 +13,142 @@ use crate::*; /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` /// - `FMT`: Frame Format marker, eg `Data16Channel16` -pub struct TransferConfig { +pub struct TransferConfig { driver_config: DriverConfig, + _std: PhantomData, _fmt: PhantomData, } -impl TransferConfig { +impl TransferConfig { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { driver_config: DriverConfig::new_slave(), + _std: PhantomData, _fmt: PhantomData, } } } -impl TransferConfig { +impl TransferConfig { /// Create a new default master configuration. pub fn new_master() -> Self { Self { driver_config: DriverConfig::new_master(), + _std: PhantomData, _fmt: PhantomData, } } } -impl TransferConfig +impl TransferConfig where FMT: DataFormat, { /// Create a `Transfer` object. - pub fn i2s_transfer(self, i2s_peripheral: I) -> Transfer { + pub fn i2s_transfer( + self, + i2s_peripheral: I, + ) -> Transfer { let driver = self.driver_config.i2s_driver(i2s_peripheral); - Transfer:: { + Transfer:: { driver, frame: Default::default(), frame_state: FrameState::LeftMsb, sync: false, + _std: PhantomData, _fmt: PhantomData, } } } -impl Default for TransferConfig { +impl Default for TransferConfig { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl TransferConfig { +impl TransferConfig { /// Configure transfert for transmission. - pub fn transmit(self) -> TransferConfig { - TransferConfig:: { + pub fn transmit(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.transmit(), + _std: PhantomData, _fmt: PhantomData, } } /// TransferConfigure in transmit mode - pub fn receive(self) -> TransferConfig { - TransferConfig:: { + pub fn receive(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.receive(), + _std: PhantomData, _fmt: PhantomData, } } /// Select the I2s standard to use - pub fn standard(self, standard: I2sStandard) -> Self { - TransferConfig:: { - driver_config: self.driver_config.standard(standard), + #[allow(non_camel_case_types)] + pub fn standard(self, _standard: NEW_STD) -> TransferConfig + where + NEW_STD: marker::I2sStandard, + { + TransferConfig:: { + driver_config: self.driver_config.standard(NEW_STD::VALUE), + _std: PhantomData, _fmt: PhantomData, } } /// Select steady state clock polarity pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.clock_polarity(polarity), + _std: PhantomData, _fmt: PhantomData, } } /// Select data format #[allow(non_camel_case_types)] - pub fn data_format(self, _format: NEW_FMT) -> TransferConfig + pub fn data_format(self, _format: NEW_FMT) -> TransferConfig where NEW_FMT: marker::DataFormat, { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.data_format(NEW_FMT::VALUE), + _std: PhantomData, _fmt: PhantomData, } } /// Convert to a slave configuration. This delete Master Only Settings. - pub fn to_slave(self) -> TransferConfig { - TransferConfig:: { + pub fn to_slave(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.to_slave(), + _std: PhantomData, _fmt: PhantomData, } } /// Convert to a master configuration. - pub fn to_master(self) -> TransferConfig { - TransferConfig:: { + pub fn to_master(self) -> TransferConfig { + TransferConfig:: { driver_config: self.driver_config.to_master(), + _std: PhantomData, _fmt: PhantomData, } } } -impl TransferConfig { +impl TransferConfig { /// 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 { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.master_clock(enable), + _std: PhantomData, _fmt: PhantomData, } } @@ -148,16 +168,18 @@ impl TransferConfig { /// /// `div` must be at least 2, otherwise the method panics. pub fn prescaler(self, odd: bool, div: u8) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.prescaler(odd, div), + _std: PhantomData, _fmt: PhantomData, } } /// Request an audio sampling frequency. The effective audio sampling frequency may differ. pub fn request_frequency(self, freq: u32) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.request_frequency(freq), + _std: PhantomData, _fmt: PhantomData, } } @@ -166,8 +188,9 @@ impl TransferConfig { /// /// If the required frequency can not bet set, Instantiate the driver will produce a error pub fn require_frequency(self, freq: u32) -> Self { - TransferConfig:: { + TransferConfig:: { driver_config: self.driver_config.require_frequency(freq), + _std: PhantomData, _fmt: PhantomData, } } @@ -183,7 +206,7 @@ enum FrameState { } use FrameState::*; -pub struct Transfer +pub struct Transfer where I: I2sPeripheral, FMT: DataFormat, @@ -192,17 +215,18 @@ where frame: FMT::AudioFrame, frame_state: FrameState, sync: bool, + _std: PhantomData, _fmt: PhantomData, } /// Constructors and Destructors -impl Transfer +impl Transfer where I: I2sPeripheral, FMT: DataFormat, { /// Instantiate and configure an i2s driver. - pub fn new(i2s_peripheral: I, config: TransferConfig) -> Self { + pub fn new(i2s_peripheral: I, config: TransferConfig) -> Self { config.i2s_transfer(i2s_peripheral) } @@ -212,7 +236,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, FMT: DataFormat, @@ -231,7 +255,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, FMT: DataFormat, @@ -241,7 +265,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, FMT: Data16 + DataFormat, @@ -306,7 +330,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { @@ -386,7 +410,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, FMT: Data16 + DataFormat, @@ -494,7 +518,7 @@ where } } -impl Transfer +impl Transfer where I: I2sPeripheral, { From c20fe5dfa5014027f4f99a5d33f556a31dae27db Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 15 May 2022 03:34:54 +0200 Subject: [PATCH 60/91] adapte slave sync to support all standard --- src/transfer.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 5cdf432..03d0e99 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -219,6 +219,23 @@ where _fmt: PhantomData, } +impl Transfer +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 Transfer where @@ -413,6 +430,7 @@ where impl Transfer where I: I2sPeripheral, + STD: I2sStandard, FMT: Data16 + DataFormat, { //TODO WS_line sensing is protocol dependent @@ -449,7 +467,7 @@ where self.sync = false; self.driver.disable(); } - } else if self.driver.ws_is_high() { + } 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(); @@ -463,7 +481,7 @@ where self.frame_state = RightMsb; self.driver.enable(); // ensure the ws line didn't change during sync process - if self.driver.ws_is_high() { + if !self._ws_is_start() { self.sync = true; } else { self.driver.disable(); @@ -499,7 +517,7 @@ where self.sync = false; self.driver.disable(); } - } else if self.driver.ws_is_high() { + } 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; @@ -507,7 +525,7 @@ where self.frame_state = RightMsb; self.driver.enable(); // ensure the ws line didn't change during sync process - if self.driver.ws_is_high() { + if !self._ws_is_start() { self.sync = true; } else { self.driver.disable(); @@ -521,6 +539,7 @@ where impl Transfer where I: I2sPeripheral, + STD: I2sStandard, { #[inline] // Can't make it work now @@ -564,7 +583,7 @@ where self.sync = false; self.driver.disable(); } - } else if self.driver.ws_is_high() { + } 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(); @@ -578,7 +597,7 @@ where self.frame_state = LeftLsb; self.driver.enable(); // ensure the ws line didn't change during sync process - if self.driver.ws_is_high() { + if !self._ws_is_start() { self.sync = true; } else { self.driver.disable(); @@ -624,7 +643,7 @@ where self.sync = false; self.driver.disable(); } - } else if self.driver.ws_is_high() { + } 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; @@ -632,7 +651,7 @@ where self.frame_state = LeftLsb; self.driver.enable(); // ensure the ws line didn't change during sync process - if self.driver.ws_is_high() { + if !self._ws_is_start() { self.sync = true; } else { self.driver.disable(); From d1be182195d44d8ecb9ee08455f73ef32a87b896 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 16 May 2022 01:34:48 +0200 Subject: [PATCH 61/91] impl master receive 16 bits --- src/transfer.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 03d0e99..a36c5e7 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -661,3 +661,80 @@ where Err(WouldBlock) } } + +impl Transfer +where + I: I2sPeripheral, + FMT: Data16 + DataFormat, +{ + /// 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 self.frame_state { + LeftMsb => { + let data = self.driver.read_data_register(); + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + RightMsb => { + let data = self.driver.read_data_register(); + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + if !predicate(self.frame) { + return; + } + } + _ => unreachable!(), + } + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + self.driver.disable(); + self.driver.enable(); + 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<(i16, i16), Infallible> { + self.driver.enable(); + let status = self.driver.status(); + if status.rxne() { + match self.frame_state { + LeftMsb => { + let data = self.driver.read_data_register(); + self.frame.0 = data as i16; + self.frame_state = RightMsb; + } + RightMsb => { + let data = self.driver.read_data_register(); + self.frame.1 = data as i16; + self.frame_state = LeftMsb; + return Ok(self.frame); + } + _ => unreachable!(), + } + } + if status.ovr() { + self.driver.read_data_register(); + self.driver.status(); + self.driver.disable(); + self.driver.enable(); + self.frame_state = LeftMsb; + } + Err(WouldBlock) + } +} From a1f01dab33ec39cdc18ed28451ae5680a18dd8cb Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 22 May 2022 11:18:43 +0200 Subject: [PATCH 62/91] 32 bits master receive transfer, (PCM not supported) --- src/lib.rs | 2 +- src/marker.rs | 14 ++++++++ src/transfer.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a3e5f93..cbb0678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ use self::pac::spi1::{i2spr, sr}; //use crate::pac::spi1::i2scfgr::I2SCFG_A; /// The channel associated with a sample -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Channel { /// Left channel (word select low) Left, diff --git a/src/marker.rs b/src/marker.rs index 5f56ee6..75f5442 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -139,3 +139,17 @@ impl_i2s_standard!( (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 index a36c5e7..ac9681e 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -4,6 +4,7 @@ use core::convert::Infallible; use nb::Error::WouldBlock; +use crate::Channel::*; use crate::Config as DriverConfig; use crate::I2sDriver as Driver; use crate::*; @@ -197,7 +198,7 @@ impl TransferConfig { } /// Part of the frame we currently transmitting or receiving -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum FrameState { LeftMsb, LeftLsb, @@ -738,3 +739,92 @@ where Err(WouldBlock) } } + +impl Transfer +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) + } +} From 2020bd02f53c92f78f5f69bd7efc7226ce704d45 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 22 May 2022 12:46:23 +0200 Subject: [PATCH 63/91] remake 16 bit master receive, unsupport PCM --- src/transfer.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index ac9681e..160c6c6 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -667,6 +667,7 @@ impl Transfer where I: I2sPeripheral, FMT: Data16 + DataFormat, + STD: ChannelFlag, { /// Read samples while predicate return `true`. /// @@ -679,13 +680,13 @@ where loop { let status = self.driver.status(); if status.rxne() { - match self.frame_state { - LeftMsb => { + match status.chside() { + Left => { let data = self.driver.read_data_register(); self.frame.0 = data as i16; self.frame_state = RightMsb; } - RightMsb => { + Right => { let data = self.driver.read_data_register(); self.frame.1 = data as i16; self.frame_state = LeftMsb; @@ -693,15 +694,11 @@ where return; } } - _ => unreachable!(), } } if status.ovr() { self.driver.read_data_register(); self.driver.status(); - self.driver.disable(); - self.driver.enable(); - self.frame_state = LeftMsb; } } } @@ -714,27 +711,23 @@ where self.driver.enable(); let status = self.driver.status(); if status.rxne() { - match self.frame_state { - LeftMsb => { + match status.chside() { + Left => { let data = self.driver.read_data_register(); self.frame.0 = data as i16; self.frame_state = RightMsb; } - RightMsb => { + Right => { let data = self.driver.read_data_register(); self.frame.1 = data as i16; self.frame_state = LeftMsb; return Ok(self.frame); } - _ => unreachable!(), } } if status.ovr() { self.driver.read_data_register(); self.driver.status(); - self.driver.disable(); - self.driver.enable(); - self.frame_state = LeftMsb; } Err(WouldBlock) } From 3a2f851d295beef99e4e47443e19d4e4b43c7599 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 22 May 2022 21:48:23 +0200 Subject: [PATCH 64/91] slave receive mode, (can't check read_while) --- src/transfer.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 160c6c6..398d016 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -821,3 +821,104 @@ where Err(WouldBlock) } } + +impl Transfer +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() { + //defmt::println!("sycing"); + self.frame_state = RightMsb; + 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) + } +} From 81c2475661b5c7e0f4fe878fcb7d40a36efa2cb0 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 22 May 2022 23:22:22 +0200 Subject: [PATCH 65/91] slave receive 32 bit , (can't test read_while) --- src/transfer.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 398d016..211853b 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -922,3 +922,117 @@ where Err(WouldBlock) } } + +impl Transfer +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() { + //defmt::println!("sycing"); + self.frame_state = RightMsb; + 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) + } +} From 90fcbb5934697575e36f6d6dfb80769107faa36b Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Tue, 24 May 2022 01:59:50 +0200 Subject: [PATCH 66/91] reorganisation, one module for driver, one module for transfer --- src/driver.rs | 730 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 740 +----------------------------------------------- src/marker.rs | 10 +- src/transfer.rs | 21 +- 4 files changed, 754 insertions(+), 747 deletions(-) create mode 100644 src/driver.rs diff --git a/src/driver.rs b/src/driver.rs new file mode 100644 index 0000000..7704fa7 --- /dev/null +++ b/src/driver.rs @@ -0,0 +1,730 @@ +//! Type definition for I2S driver. +use core::marker::PhantomData; + +use crate::marker::*; +use crate::pac::spi1::RegisterBlock; +use crate::pac::spi1::{i2spr, sr}; +use crate::I2sPeripheral; + +/// The channel associated with a sample +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Channel { + /// Left channel (word select low) + Left, + /// Right channel (word select high) + Right, +} + +/// Content of the status register. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +pub struct Status { + value: sr::R, + _ms: PhantomData, + _tr: PhantomData, +} + +impl Status { + /// Get the BSY flag. If `true` the I2s device is busy communicating. + pub fn bsy(&self) -> bool { + self.value.bsy().bit() + } + + /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. Have + /// no meaning with PCM standard. + /// + /// This flag is updated when TXE or RXNE flags are set. This flag is meaningless and therefore + /// not reliable is case of error or when using the PCM standard. + 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), +} + +#[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, +} + +/// 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 date length on 16 bit wide channel + Data16Channel16, + /// 16 bit date length on 32 bit wide channel + Data16Channel32, + /// 24 bit date length on 32 bit wide channel + Data24Channel32, + /// 32 bit date length on 32 bit wide channel + Data32Channel32, +} + +impl Default for DataFormat { + fn default() -> Self { + DataFormat::Data16Channel16 + } +} + +#[derive(Debug, Clone, Copy)] +/// I2s Configuration builder. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +pub struct Config { + 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, +} + +impl Config { + /// 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, + } + } +} + +impl Config { + /// 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, + } + } +} + +/// 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") + }; +} + +// set _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 Config { + /// Instantiate the driver. + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver> { + let _mode = PhantomData; + let driver = I2sDriver::> { + i2s_peripheral, + _mode, + }; + 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 Config { + /// Create a default configuration. It correspond to a default slave configuration. + fn default() -> Self { + Self::new_slave() + } +} + +impl Config { + /// Configure in transmit mode + pub fn transmit(self) -> Config { + Config:: { + 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, + } + } + /// Configure in transmit mode + pub fn receive(self) -> Config { + Config:: { + 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, + } + } + /// Select the I2s standard to use + pub fn standard(mut self, standard: I2sStandard) -> Self { + self.standard = standard; + self + } + /// 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) -> Config { + let Self { + transmit_or_receive, + standard, + clock_polarity, + data_format, + .. + } = self; + Config:: { + 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, + } + } + + /// Convert to a master configuration. + pub fn to_master(self) -> Config { + let Self { + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + .. + } = self; + Config:: { + slave_or_master: SlaveOrMaster::Master, + transmit_or_receive, + standard, + clock_polarity, + data_format, + master_clock, + frequency, + _ms: PhantomData, + _tr: PhantomData, + } + } +} + +impl Config { + /// 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 produce a error + 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 avanced usage, for example using interrupt or DMA. +/// +/// # Example +/// +/// TODO +/// +/// ```no_run +/// ``` +/// +pub struct I2sDriver { + i2s_peripheral: I, + + _mode: 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 and configure an i2s driver. + pub fn new(i2s_peripheral: I, config: Config) -> 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: Config, + ) -> 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, + } + } +} + +/// 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 { + #[test] + fn test_div_rounding() { + let fracs = [(1, 2), (2, 2), (1, 3), (2, 3), (2, 4), (3, 5), (9, 2)]; + for (n, d) in fracs { + let res = div_rounding(n, d); + let check = f32::round((n as f32) / (d as f32)) as u32; + assert_eq!(res, check); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index cbb0678..e2e47de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,529 +7,15 @@ #![no_std] -extern crate nb; -extern crate vcell; - -//mod config; mod pac; +pub mod driver; pub mod marker; -use marker::*; +pub mod transfer; mod sealed { pub trait Sealed {} } -//use self::sealed::Sealed; - -use core::marker::PhantomData; - -mod transfer; -pub use transfer::*; - -//pub use self::config::{MasterClock, MasterConfig, SlaveConfig}; -use self::pac::spi1::RegisterBlock; -use self::pac::spi1::{i2spr, sr}; -//use crate::format::{DataFormat, FrameFormat, FrameSync}; -//use crate::pac::spi1::i2scfgr::I2SCFG_A; - -/// The channel associated with a sample -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Channel { - /// Left channel (word select low) - Left, - /// Right channel (word select high) - Right, -} - -/// Content of the status register. -/// -/// - `MS`: `Master` or `Slave` -/// - `TR`: `Transmit` or `Receive` -pub struct Status { - value: sr::R, - _ms: PhantomData, - _tr: PhantomData, -} - -impl Status { - /// Get the BSY flag. If `true` the I2s device is busy communicating. - pub fn bsy(&self) -> bool { - self.value.bsy().bit() - } - - /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. Have - /// no meaning with PCM standard. - /// - /// This flag is updated when TXE or RXNE flags are set. This flag is meaningless and therefore - /// not reliable is case of error or when using the PCM standard. - 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), -} - -#[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, -} - -/// 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 date length on 16 bit wide channel - Data16Channel16, - /// 16 bit date length on 32 bit wide channel - Data16Channel32, - /// 24 bit date length on 32 bit wide channel - Data24Channel32, - /// 32 bit date length on 32 bit wide channel - Data32Channel32, -} - -impl Default for DataFormat { - fn default() -> Self { - DataFormat::Data16Channel16 - } -} - -#[derive(Debug, Clone, Copy)] -/// I2s Configuration builder. -/// -/// - `MS`: `Master` or `Slave` -/// - `TR`: `Transmit` or `Receive` -pub struct Config { - 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, -} - -impl Config { - /// 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, - } - } -} - -impl Config { - /// 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, - } - } -} - -/// 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") - }; -} - -// set _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 Config { - /// Instantiate the driver. - pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver> { - let _mode = PhantomData; - let driver = I2sDriver::> { - i2s_peripheral, - _mode, - }; - 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 Config { - /// Create a default configuration. It correspond to a default slave configuration. - fn default() -> Self { - Self::new_slave() - } -} - -impl Config { - /// Configure in transmit mode - pub fn transmit(self) -> Config { - Config:: { - 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, - } - } - /// Configure in transmit mode - pub fn receive(self) -> Config { - Config:: { - 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, - } - } - /// Select the I2s standard to use - pub fn standard(mut self, standard: I2sStandard) -> Self { - self.standard = standard; - self - } - /// 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) -> Config { - let Self { - transmit_or_receive, - standard, - clock_polarity, - data_format, - .. - } = self; - Config:: { - 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, - } - } - - /// Convert to a master configuration. - pub fn to_master(self) -> Config { - let Self { - transmit_or_receive, - standard, - clock_polarity, - data_format, - master_clock, - frequency, - .. - } = self; - Config:: { - slave_or_master: SlaveOrMaster::Master, - transmit_or_receive, - standard, - clock_polarity, - data_format, - master_clock, - frequency, - _ms: PhantomData, - _tr: PhantomData, - } - } -} - -impl Config { - /// 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 produce a error - pub fn require_frequency(mut self, freq: u32) -> Self { - self.frequency = Frequency::Require(freq); - self - } -} /// An object composed of a SPI device that can be used for I2S communication. /// @@ -557,225 +43,3 @@ pub unsafe trait I2sPeripheral { /// Return `true` if the level at WS pin is low. fn ws_is_low(&self) -> bool; } - -/// Driver of a SPI peripheral in I2S mode -/// -/// # Example -/// -/// TODO -/// -/// ```no_run -/// ``` -/// -pub struct I2sDriver { - i2s_peripheral: I, - - _mode: 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 and configure an i2s driver. - pub fn new(i2s_peripheral: I, config: Config) -> 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: Config, - ) -> 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, - } - } -} - -/// 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 { - #[test] - fn test_div_rounding() { - let fracs = [(1, 2), (2, 2), (1, 3), (2, 3), (2, 4), (3, 5), (9, 2)]; - for (n, d) in fracs { - let res = div_rounding(n, d); - let check = f32::round((n as f32) / (d as f32)) as u32; - assert_eq!(res, check); - } - } -} diff --git a/src/marker.rs b/src/marker.rs index 75f5442..ec94966 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -1,4 +1,4 @@ -//! Markers for [`Config`](super::Config) and [`I2sDriver`](super::I2sDriver) +//! Markers used in this crate. use core::marker::PhantomData; use crate::sealed::Sealed; @@ -88,7 +88,7 @@ impl Data16 for Data16Channel32 {} /// Trait for marker indicating a DataFormat pub trait DataFormat: Sealed { /// Runtime value. - const VALUE: crate::DataFormat; + const VALUE: crate::driver::DataFormat; /// Audio frame representation from API point of view; type AudioFrame: Default; } @@ -97,7 +97,7 @@ macro_rules! impl_data_format{ ($(($marker:ident,$audio_frame:ty)),*) => { $( impl DataFormat for $marker { - const VALUE: crate::DataFormat = crate::DataFormat::$marker; + const VALUE: crate::driver::DataFormat = crate::driver::DataFormat::$marker; type AudioFrame = $audio_frame; } )* @@ -114,7 +114,7 @@ impl_data_format!( /// Trait for marker indicating a i2s standard. pub trait I2sStandard: Sealed { /// Runtime value. - const VALUE: crate::I2sStandard; + 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. @@ -125,7 +125,7 @@ macro_rules! impl_i2s_standard{ ($(($marker:ident,$ws_start_level:literal)),*) => { $( impl I2sStandard for $marker { - const VALUE: crate::I2sStandard = crate::I2sStandard::$marker; + const VALUE: crate::driver::I2sStandard = crate::driver::I2sStandard::$marker; const WS_START_LEVEL: bool = $ws_start_level; } )* diff --git a/src/transfer.rs b/src/transfer.rs index 211853b..9efa32e 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -2,12 +2,16 @@ //! //! use core::convert::Infallible; +use core::marker::PhantomData; + use nb::Error::WouldBlock; -use crate::Channel::*; -use crate::Config as DriverConfig; -use crate::I2sDriver as Driver; -use crate::*; +use crate::driver::Channel::*; +use crate::driver::ClockPolarity; +use crate::driver::Config as DriverConfig; +use crate::driver::I2sDriver as Driver; +use crate::marker::{self, *}; +use crate::I2sPeripheral; #[derive(Debug, Clone, Copy)] /// I2s TransferConfiguration builder. @@ -207,6 +211,15 @@ enum FrameState { } 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 Transfer where I: I2sPeripheral, From 0fd89943f58ead8228be917e449eda8b6e7af30c Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 25 May 2022 01:57:46 +0200 Subject: [PATCH 67/91] add some panics doc, fix some typo --- src/driver.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 7704fa7..95af048 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -151,13 +151,13 @@ pub enum ClockPolarity { /// Data length to be transferred and channel length #[derive(Debug, Clone, Copy)] pub enum DataFormat { - /// 16 bit date length on 16 bit wide channel + /// 16 bit data length on 16 bit wide channel Data16Channel16, - /// 16 bit date length on 32 bit wide channel + /// 16 bit data length on 32 bit wide channel Data16Channel32, - /// 24 bit date length on 32 bit wide channel + /// 24 bit data length on 32 bit wide channel Data24Channel32, - /// 32 bit date length on 32 bit wide channel + /// 32 bit data length on 32 bit wide channel Data32Channel32, } @@ -297,7 +297,11 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { } impl Config { - /// Instantiate the driver. + /// 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 _mode = PhantomData; let driver = I2sDriver::> { @@ -498,7 +502,7 @@ impl Config { /// Require exactly this audio sampling frequency. /// - /// If the required frequency can not bet set, Instantiate the driver will produce a error + /// 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 @@ -507,7 +511,7 @@ impl Config { /// Driver of a SPI peripheral in I2S mode. /// -/// Meant for avanced usage, for example using interrupt or DMA. +/// Meant for advanced usage, for example using interrupt or DMA. /// /// # Example /// @@ -537,7 +541,12 @@ impl I2sDriver> where I: I2sPeripheral, { - /// Instantiate and configure an i2s driver. + /// Instantiate an i2s driver from an [`I2sPeripheral`] object and a configuration. + /// + /// # 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: Config) -> Self { config.i2s_driver(i2s_peripheral) } From eb346f812760ba77ca0a356f46f2b4552a50a944 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 25 May 2022 02:12:36 +0200 Subject: [PATCH 68/91] document some panics --- src/transfer.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 9efa32e..f303c0e 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -52,7 +52,11 @@ impl TransferConfig where FMT: DataFormat, { - /// Create a `Transfer` object. + /// Create a `Transfer` 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, @@ -191,7 +195,7 @@ impl TransferConfig { /// Require exactly this audio sampling frequency. /// - /// If the required frequency can not bet set, Instantiate the driver will produce a error + /// If the required frequency can not bet set, Instantiate the driver will panics. pub fn require_frequency(self, freq: u32) -> Self { TransferConfig:: { driver_config: self.driver_config.require_frequency(freq), @@ -256,7 +260,12 @@ where I: I2sPeripheral, FMT: DataFormat, { - /// Instantiate and configure an i2s driver. + /// 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: TransferConfig) -> Self { config.i2s_transfer(i2s_peripheral) } From 00d00ee7deb7b0317c9dfa722fa81067510b3db5 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 25 May 2022 02:30:55 +0200 Subject: [PATCH 69/91] add 'I2s' prefix to 'Transfer', fix doc --- src/transfer.rs | 95 +++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index f303c0e..9302385 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -14,19 +14,19 @@ use crate::marker::{self, *}; use crate::I2sPeripheral; #[derive(Debug, Clone, Copy)] -/// I2s TransferConfiguration builder. +/// [`I2sTransfer`] configuration. /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` /// - `STD`: I2S standard, eg `Philips` /// - `FMT`: Frame Format marker, eg `Data16Channel16` -pub struct TransferConfig { +pub struct I2sTransferConfig { driver_config: DriverConfig, _std: PhantomData, _fmt: PhantomData, } -impl TransferConfig { +impl I2sTransferConfig { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { @@ -37,7 +37,7 @@ impl TransferConfig { } } -impl TransferConfig { +impl I2sTransferConfig { /// Create a new default master configuration. pub fn new_master() -> Self { Self { @@ -48,11 +48,11 @@ impl TransferConfig { } } -impl TransferConfig +impl I2sTransferConfig where FMT: DataFormat, { - /// Create a `Transfer` object around an [`I2sPeripheral`] object. + /// Create a `I2sTransfer` object around an [`I2sPeripheral`] object. /// /// # Panics /// @@ -60,9 +60,9 @@ where pub fn i2s_transfer( self, i2s_peripheral: I, - ) -> Transfer { + ) -> I2sTransfer { let driver = self.driver_config.i2s_driver(i2s_peripheral); - Transfer:: { + I2sTransfer:: { driver, frame: Default::default(), frame_state: FrameState::LeftMsb, @@ -73,37 +73,37 @@ where } } -impl Default for TransferConfig { +impl Default for I2sTransferConfig { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl TransferConfig { - /// Configure transfert for transmission. - pub fn transmit(self) -> TransferConfig { - TransferConfig:: { +impl I2sTransferConfig { + /// Configure transfert for transmitting data. + pub fn transmit(self) -> I2sTransferConfig { + I2sTransferConfig:: { driver_config: self.driver_config.transmit(), _std: PhantomData, _fmt: PhantomData, } } - /// TransferConfigure in transmit mode - pub fn receive(self) -> TransferConfig { - TransferConfig:: { + /// Configure transfer for receiving data. + pub fn receive(self) -> I2sTransferConfig { + I2sTransferConfig:: { driver_config: self.driver_config.receive(), _std: PhantomData, _fmt: PhantomData, } } - /// Select the I2s standard to use + /// Select the I2s standard to use. #[allow(non_camel_case_types)] - pub fn standard(self, _standard: NEW_STD) -> TransferConfig + pub fn standard(self, _standard: NEW_STD) -> I2sTransferConfig where NEW_STD: marker::I2sStandard, { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.standard(NEW_STD::VALUE), _std: PhantomData, _fmt: PhantomData, @@ -111,7 +111,7 @@ impl TransferConfig { } /// Select steady state clock polarity pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.clock_polarity(polarity), _std: PhantomData, _fmt: PhantomData, @@ -120,11 +120,11 @@ impl TransferConfig { /// Select data format #[allow(non_camel_case_types)] - pub fn data_format(self, _format: NEW_FMT) -> TransferConfig + pub fn data_format(self, _format: NEW_FMT) -> I2sTransferConfig where NEW_FMT: marker::DataFormat, { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.data_format(NEW_FMT::VALUE), _std: PhantomData, _fmt: PhantomData, @@ -132,8 +132,8 @@ impl TransferConfig { } /// Convert to a slave configuration. This delete Master Only Settings. - pub fn to_slave(self) -> TransferConfig { - TransferConfig:: { + pub fn to_slave(self) -> I2sTransferConfig { + I2sTransferConfig:: { driver_config: self.driver_config.to_slave(), _std: PhantomData, _fmt: PhantomData, @@ -141,8 +141,8 @@ impl TransferConfig { } /// Convert to a master configuration. - pub fn to_master(self) -> TransferConfig { - TransferConfig:: { + pub fn to_master(self) -> I2sTransferConfig { + I2sTransferConfig:: { driver_config: self.driver_config.to_master(), _std: PhantomData, _fmt: PhantomData, @@ -150,19 +150,20 @@ impl TransferConfig { } } -impl TransferConfig { +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 { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.master_clock(enable), _std: PhantomData, _fmt: PhantomData, } } - /// TransferConfigure audio frequency by setting the prescaler with an odd factor and a divider. + /// 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 @@ -177,7 +178,7 @@ impl TransferConfig { /// /// `div` must be at least 2, otherwise the method panics. pub fn prescaler(self, odd: bool, div: u8) -> Self { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.prescaler(odd, div), _std: PhantomData, _fmt: PhantomData, @@ -186,7 +187,7 @@ impl TransferConfig { /// Request an audio sampling frequency. The effective audio sampling frequency may differ. pub fn request_frequency(self, freq: u32) -> Self { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.request_frequency(freq), _std: PhantomData, _fmt: PhantomData, @@ -195,9 +196,9 @@ impl TransferConfig { /// Require exactly this audio sampling frequency. /// - /// If the required frequency can not bet set, Instantiate the driver will panics. + /// If the required frequency can not bet set, Instantiate a transfer will panics. pub fn require_frequency(self, freq: u32) -> Self { - TransferConfig:: { + I2sTransferConfig:: { driver_config: self.driver_config.require_frequency(freq), _std: PhantomData, _fmt: PhantomData, @@ -224,7 +225,7 @@ use FrameState::*; /// - 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 Transfer +pub struct I2sTransfer where I: I2sPeripheral, FMT: DataFormat, @@ -237,7 +238,7 @@ where _fmt: PhantomData, } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, STD: I2sStandard, @@ -255,7 +256,7 @@ where } /// Constructors and Destructors -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: DataFormat, @@ -266,7 +267,7 @@ where /// /// 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: TransferConfig) -> Self { + pub fn new(i2s_peripheral: I, config: I2sTransferConfig) -> Self { config.i2s_transfer(i2s_peripheral) } @@ -276,7 +277,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: DataFormat, @@ -295,7 +296,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: DataFormat, @@ -305,7 +306,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: Data16 + DataFormat, @@ -370,7 +371,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, { @@ -450,7 +451,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, STD: I2sStandard, @@ -559,7 +560,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, STD: I2sStandard, @@ -685,7 +686,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: Data16 + DataFormat, @@ -755,7 +756,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, STD: ChannelFlag, @@ -844,7 +845,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, FMT: Data16 + DataFormat, @@ -945,7 +946,7 @@ where } } -impl Transfer +impl I2sTransfer where I: I2sPeripheral, STD: ChannelFlag, From 06a4d02e9883090798afe416f27367a21cf33589 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Wed, 25 May 2022 02:37:57 +0200 Subject: [PATCH 70/91] rename to I2SDriverConfig and fix doc --- src/driver.rs | 44 ++++++++++++++++++++++---------------------- src/transfer.rs | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 95af048..1b5a2f9 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -168,11 +168,11 @@ impl Default for DataFormat { } #[derive(Debug, Clone, Copy)] -/// I2s Configuration builder. +/// I2s configuration. /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` -pub struct Config { +pub struct I2sDriverConfig { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, standard: I2sStandard, @@ -185,7 +185,7 @@ pub struct Config { _tr: PhantomData, } -impl Config { +impl I2sDriverConfig { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { @@ -202,7 +202,7 @@ impl Config { } } -impl Config { +impl I2sDriverConfig { /// Create a new default master configuration. pub fn new_master() -> Self { Self { @@ -296,7 +296,7 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { } } -impl Config { +impl I2sDriverConfig { /// Instantiate the driver by wrapping the given [`I2sPeripheral`]. /// /// # Panics @@ -360,17 +360,17 @@ impl Config { } } -impl Default for Config { +impl Default for I2sDriverConfig { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl Config { - /// Configure in transmit mode - pub fn transmit(self) -> Config { - Config:: { +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, @@ -382,9 +382,9 @@ impl Config { _tr: PhantomData, } } - /// Configure in transmit mode - pub fn receive(self) -> Config { - Config:: { + /// 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, @@ -416,7 +416,7 @@ impl Config { } /// Convert to a slave configuration. This delete Master Only Settings. - pub fn to_slave(self) -> Config { + pub fn to_slave(self) -> I2sDriverConfig { let Self { transmit_or_receive, standard, @@ -424,7 +424,7 @@ impl Config { data_format, .. } = self; - Config:: { + I2sDriverConfig:: { slave_or_master: SlaveOrMaster::Slave, transmit_or_receive, standard, @@ -438,7 +438,7 @@ impl Config { } /// Convert to a master configuration. - pub fn to_master(self) -> Config { + pub fn to_master(self) -> I2sDriverConfig { let Self { transmit_or_receive, standard, @@ -448,7 +448,7 @@ impl Config { frequency, .. } = self; - Config:: { + I2sDriverConfig:: { slave_or_master: SlaveOrMaster::Master, transmit_or_receive, standard, @@ -462,7 +462,7 @@ impl Config { } } -impl Config { +impl I2sDriverConfig { /// Enable/Disable Master Clock. Affect the effective sampling rate. /// /// This can be only set and only have meaning for Master mode. @@ -545,9 +545,9 @@ where /// /// # 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: Config) -> Self { + /// 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) } @@ -565,7 +565,7 @@ where #[allow(non_camel_case_types)] pub fn reconfigure( self, - config: Config, + config: I2sDriverConfig, ) -> I2sDriver> { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) diff --git a/src/transfer.rs b/src/transfer.rs index 9302385..ce7050f 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -8,8 +8,8 @@ use nb::Error::WouldBlock; use crate::driver::Channel::*; use crate::driver::ClockPolarity; -use crate::driver::Config as DriverConfig; use crate::driver::I2sDriver as Driver; +use crate::driver::I2sDriverConfig as DriverConfig; use crate::marker::{self, *}; use crate::I2sPeripheral; From e087af8d944529230a2c5bfcd6a9d9be18a35713 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Thu, 26 May 2022 17:28:48 +0200 Subject: [PATCH 71/91] fix error in channel doc --- src/driver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 1b5a2f9..7b2cf48 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -9,9 +9,9 @@ use crate::I2sPeripheral; /// The channel associated with a sample #[derive(Debug, Clone, Copy, PartialEq)] pub enum Channel { - /// Left channel (word select low) + /// Left channel Left, - /// Right channel (word select high) + /// Right channel Right, } From d8ce8af73a264f8a2fa08b773affc608dfef6e34 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 27 May 2022 00:13:17 +0200 Subject: [PATCH 72/91] change marker module description --- src/marker.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/marker.rs b/src/marker.rs index ec94966..30c8586 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -1,4 +1,5 @@ -//! Markers used in this crate. +//! Markers for [`I2sDriver`](crate::driver::I2sDriver), +//! [`I2sTransfer`](crate::transfer::I2sTransfer) and their respective configuration. use core::marker::PhantomData; use crate::sealed::Sealed; From 8a3a979de80533212caf4d477509ec4c3bfe4f44 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Fri, 27 May 2022 23:38:21 +0200 Subject: [PATCH 73/91] document driver module --- src/driver.rs | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 7b2cf48..1befd78 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,4 +1,30 @@ -//! Type definition for I2S driver. +//! 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 +//! let driver_config = I2sDriverConfig::new_master() +//! .receive() +//! .standard(I2sStandard::Philips) +//! .data_format(DataFormat::Data24Channel32) +//! .master_clock(true) +//! .request_frequency(48_000); +//! ``` +//! Then you can instantiate the driver around an `I2sPeripheral`: +//! ```no_run +//! // instantiate from configuration +//! let driver = driver_config.i2s_driver(i2s_peripheral); +//! +//! // alternate way +//! let driver = I2sDriver::new(i2s_peripheral, driver_config); +//! ``` use core::marker::PhantomData; use crate::marker::*; @@ -168,7 +194,7 @@ impl Default for DataFormat { } #[derive(Debug, Clone, Copy)] -/// I2s configuration. +/// I2s configuration. Can be used as an i2s driver builder. /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` @@ -284,7 +310,7 @@ fn _set_require_frequency( }; } -// set _set_request_frequency for explanation +// see _set_request_frequency for explanation fn _coef(mclk: bool, data_format: DataFormat) -> u32 { if mclk { return 256; @@ -512,14 +538,6 @@ impl I2sDriverConfig { /// Driver of a SPI peripheral in I2S mode. /// /// Meant for advanced usage, for example using interrupt or DMA. -/// -/// # Example -/// -/// TODO -/// -/// ```no_run -/// ``` -/// pub struct I2sDriver { i2s_peripheral: I, From 46b59383c5a5b9056d9a5ce0480574e0eeb9561f Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 28 May 2022 00:33:53 +0200 Subject: [PATCH 74/91] document transfer module --- src/transfer.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/transfer.rs b/src/transfer.rs index ce7050f..8acd0f4 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -1,6 +1,29 @@ -//! Abstraction for I2S transfer +//! 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. //! +//! # Configure and instantiate transfer. +//! +//! [`I2sTransferConfig`] is used to create configuration of the i2s transfer: +//! ```no_run +//! let transfer_config = I2sTransferConfig::new_master() +//! .receive() +//! .standard(marker::Philips) +//! .data_format(marker::Data24Channel32) +//! .master_clock(true) +//! .request_frequency(48_000); +//! ``` +//! Then you can instantiate the transfer around an `I2sPeripheral`: +//! ```no_run +//! // instantiate from configuration +//! let transfer = transfer_config.i2s_driver(i2s_peripheral); +//! +//! // alternate way +//! let transfer = I2sTransfer::new(i2s_peripheral, transfer_config); +//! ``` use core::convert::Infallible; use core::marker::PhantomData; From 1fca2c559684a25e4f792bfd0032f43eb63799f1 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 28 May 2022 00:56:46 +0200 Subject: [PATCH 75/91] note about how work config --- src/driver.rs | 5 ++++- src/transfer.rs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 1befd78..596f2a1 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -194,10 +194,13 @@ impl Default for DataFormat { } #[derive(Debug, Clone, Copy)] -/// I2s configuration. Can be used as an i2s driver builder. +/// I2s driver configuration. Can be used as an i2s driver builder. /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` +/// +/// **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, diff --git a/src/transfer.rs b/src/transfer.rs index 8acd0f4..0376dee 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -43,6 +43,9 @@ use crate::I2sPeripheral; /// - `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, _std: PhantomData, From 1336280249a76f55ec48427676326ffbd3a8b4e1 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 28 May 2022 01:06:57 +0200 Subject: [PATCH 76/91] note on method taking a marker as parameter --- src/transfer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 0376dee..4c3eaa1 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -123,7 +123,7 @@ impl I2sTransferConfig { _fmt: PhantomData, } } - /// Select the I2s standard to use. + /// 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 @@ -144,7 +144,7 @@ impl I2sTransferConfig { } } - /// Select data format + /// 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 From 20c003b4ce0a163cdce5d354f54fe840b1da1caf Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sat, 28 May 2022 23:12:14 +0200 Subject: [PATCH 77/91] rexport markers in driver and transfer modules --- src/driver.rs | 3 ++- src/transfer.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 596f2a1..2ab96cd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -27,11 +27,12 @@ //! ``` use core::marker::PhantomData; -use crate::marker::*; 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 { diff --git a/src/transfer.rs b/src/transfer.rs index 4c3eaa1..66ccc44 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -33,9 +33,10 @@ use crate::driver::Channel::*; use crate::driver::ClockPolarity; use crate::driver::I2sDriver as Driver; use crate::driver::I2sDriverConfig as DriverConfig; -use crate::marker::{self, *}; use crate::I2sPeripheral; +pub use crate::marker::{self, *}; + #[derive(Debug, Clone, Copy)] /// [`I2sTransfer`] configuration. /// From 7e26cfdf5cec0a8df28a36ac399586d8bcf8aceb Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 00:19:50 +0200 Subject: [PATCH 78/91] add STD typestate on driver --- src/driver.rs | 106 ++++++++++++++++++++++++++++++------------------ src/marker.rs | 4 +- src/transfer.rs | 22 ++-------- 3 files changed, 73 insertions(+), 59 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 2ab96cd..0909560 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -46,13 +46,15 @@ pub enum Channel { /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` -pub struct Status { +/// - `STD`: I2S standard, eg `Philips` +pub struct Status { value: sr::R, _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } -impl Status { +impl Status { /// Get the BSY flag. If `true` the I2s device is busy communicating. pub fn bsy(&self) -> bool { self.value.bsy().bit() @@ -71,7 +73,7 @@ impl Status { } } -impl Status { +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 @@ -84,7 +86,7 @@ impl Status { } } -impl Status { +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 @@ -107,7 +109,7 @@ impl Status { } } -impl Status { +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. @@ -118,7 +120,7 @@ impl Status { } } -impl Status { +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 @@ -199,10 +201,11 @@ impl Default for DataFormat { /// /// - `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 { +pub struct I2sDriverConfig { slave_or_master: SlaveOrMaster, transmit_or_receive: TransmitOrReceive, standard: I2sStandard, @@ -213,9 +216,10 @@ pub struct I2sDriverConfig { _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } -impl I2sDriverConfig { +impl I2sDriverConfig { /// Create a new default slave configuration. pub fn new_slave() -> Self { Self { @@ -228,11 +232,12 @@ impl I2sDriverConfig { frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } } -impl I2sDriverConfig { +impl I2sDriverConfig { /// Create a new default master configuration. pub fn new_master() -> Self { Self { @@ -245,6 +250,7 @@ impl I2sDriverConfig { frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } } @@ -326,15 +332,18 @@ fn _coef(mclk: bool, data_format: DataFormat) -> u32 { } } -impl I2sDriverConfig { +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> { + pub fn i2s_driver( + self, + i2s_peripheral: I, + ) -> I2sDriver> { let _mode = PhantomData; - let driver = I2sDriver::> { + let driver = I2sDriver::> { i2s_peripheral, _mode, }; @@ -390,17 +399,17 @@ impl I2sDriverConfig { } } -impl Default for I2sDriverConfig { +impl Default for I2sDriverConfig { /// Create a default configuration. It correspond to a default slave configuration. fn default() -> Self { Self::new_slave() } } -impl I2sDriverConfig { +impl I2sDriverConfig { /// Configure driver in transmit mode - pub fn transmit(self) -> I2sDriverConfig { - I2sDriverConfig:: { + pub fn transmit(self) -> I2sDriverConfig { + I2sDriverConfig:: { slave_or_master: self.slave_or_master, transmit_or_receive: TransmitOrReceive::Transmit, standard: self.standard, @@ -410,11 +419,12 @@ impl I2sDriverConfig { frequency: self.frequency, _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } /// Configure driver in receive mode - pub fn receive(self) -> I2sDriverConfig { - I2sDriverConfig:: { + pub fn receive(self) -> I2sDriverConfig { + I2sDriverConfig:: { slave_or_master: self.slave_or_master, transmit_or_receive: TransmitOrReceive::Receive, standard: self.standard, @@ -424,12 +434,27 @@ impl I2sDriverConfig { frequency: self.frequency, _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } /// Select the I2s standard to use - pub fn standard(mut self, standard: I2sStandard) -> Self { - self.standard = standard; - self + #[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 @@ -446,7 +471,7 @@ impl I2sDriverConfig { } /// Convert to a slave configuration. This delete Master Only Settings. - pub fn to_slave(self) -> I2sDriverConfig { + pub fn to_slave(self) -> I2sDriverConfig { let Self { transmit_or_receive, standard, @@ -454,7 +479,7 @@ impl I2sDriverConfig { data_format, .. } = self; - I2sDriverConfig:: { + I2sDriverConfig:: { slave_or_master: SlaveOrMaster::Slave, transmit_or_receive, standard, @@ -464,11 +489,12 @@ impl I2sDriverConfig { frequency: Frequency::Prescaler(false, 0b10), _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } /// Convert to a master configuration. - pub fn to_master(self) -> I2sDriverConfig { + pub fn to_master(self) -> I2sDriverConfig { let Self { transmit_or_receive, standard, @@ -478,7 +504,7 @@ impl I2sDriverConfig { frequency, .. } = self; - I2sDriverConfig:: { + I2sDriverConfig:: { slave_or_master: SlaveOrMaster::Master, transmit_or_receive, standard, @@ -488,11 +514,12 @@ impl I2sDriverConfig { frequency, _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } } -impl I2sDriverConfig { +impl I2sDriverConfig { /// Enable/Disable Master Clock. Affect the effective sampling rate. /// /// This can be only set and only have meaning for Master mode. @@ -559,7 +586,7 @@ where } /// Constructors and Destructors -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -569,7 +596,7 @@ where /// /// 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 { + pub fn new(i2s_peripheral: I, config: I2sDriverConfig) -> Self { config.i2s_driver(i2s_peripheral) } @@ -585,10 +612,10 @@ where /// Consume the driver and create a new one with the given configuration. #[allow(non_camel_case_types)] - pub fn reconfigure( + pub fn reconfigure( self, - config: I2sDriverConfig, - ) -> I2sDriver> { + config: I2sDriverConfig, + ) -> I2sDriver> { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } @@ -635,7 +662,7 @@ where } /// Status -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -643,17 +670,18 @@ where /// /// 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:: { + pub fn status(&mut self) -> Status { + Status:: { value: self.registers().sr.read(), _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } } } /// Transmit only methods -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -676,7 +704,7 @@ where } /// Receive only methods -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -697,7 +725,7 @@ where } /// Error interrupt, Master Receive Mode. -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -710,7 +738,7 @@ where } /// Error interrupt, Slave Mode. -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { @@ -723,7 +751,7 @@ where } /// Sampling Rate -impl I2sDriver> +impl I2sDriver> where I: I2sPeripheral, { diff --git a/src/marker.rs b/src/marker.rs index 30c8586..e0d5b40 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -8,10 +8,12 @@ use crate::sealed::Sealed; /// /// - `MS`: `Master` or `Slave` /// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` #[derive(Debug, Clone, Copy)] -pub struct Mode { +pub struct Mode { _ms: PhantomData, _tr: PhantomData, + _std: PhantomData, } /// Marker, indicated master mode. diff --git a/src/transfer.rs b/src/transfer.rs index 66ccc44..d11cbfa 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -48,8 +48,7 @@ pub use crate::marker::{self, *}; /// **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, - _std: PhantomData, + driver_config: DriverConfig, _fmt: PhantomData, } @@ -58,7 +57,6 @@ impl I2sTransferConfig { pub fn new_slave() -> Self { Self { driver_config: DriverConfig::new_slave(), - _std: PhantomData, _fmt: PhantomData, } } @@ -69,7 +67,6 @@ impl I2sTransferConfig { pub fn new_master() -> Self { Self { driver_config: DriverConfig::new_master(), - _std: PhantomData, _fmt: PhantomData, } } @@ -94,7 +91,6 @@ where frame: Default::default(), frame_state: FrameState::LeftMsb, sync: false, - _std: PhantomData, _fmt: PhantomData, } } @@ -112,7 +108,6 @@ impl I2sTransferConfig { pub fn transmit(self) -> I2sTransferConfig { I2sTransferConfig:: { driver_config: self.driver_config.transmit(), - _std: PhantomData, _fmt: PhantomData, } } @@ -120,7 +115,6 @@ impl I2sTransferConfig { pub fn receive(self) -> I2sTransferConfig { I2sTransferConfig:: { driver_config: self.driver_config.receive(), - _std: PhantomData, _fmt: PhantomData, } } @@ -131,8 +125,7 @@ impl I2sTransferConfig { NEW_STD: marker::I2sStandard, { I2sTransferConfig:: { - driver_config: self.driver_config.standard(NEW_STD::VALUE), - _std: PhantomData, + driver_config: self.driver_config.standard(_standard), _fmt: PhantomData, } } @@ -140,7 +133,6 @@ impl I2sTransferConfig { pub fn clock_polarity(self, polarity: ClockPolarity) -> Self { I2sTransferConfig:: { driver_config: self.driver_config.clock_polarity(polarity), - _std: PhantomData, _fmt: PhantomData, } } @@ -153,7 +145,6 @@ impl I2sTransferConfig { { I2sTransferConfig:: { driver_config: self.driver_config.data_format(NEW_FMT::VALUE), - _std: PhantomData, _fmt: PhantomData, } } @@ -162,7 +153,6 @@ impl I2sTransferConfig { pub fn to_slave(self) -> I2sTransferConfig { I2sTransferConfig:: { driver_config: self.driver_config.to_slave(), - _std: PhantomData, _fmt: PhantomData, } } @@ -171,7 +161,6 @@ impl I2sTransferConfig { pub fn to_master(self) -> I2sTransferConfig { I2sTransferConfig:: { driver_config: self.driver_config.to_master(), - _std: PhantomData, _fmt: PhantomData, } } @@ -184,7 +173,6 @@ impl I2sTransferConfig { pub fn master_clock(self, enable: bool) -> Self { I2sTransferConfig:: { driver_config: self.driver_config.master_clock(enable), - _std: PhantomData, _fmt: PhantomData, } } @@ -207,7 +195,6 @@ impl I2sTransferConfig { pub fn prescaler(self, odd: bool, div: u8) -> Self { I2sTransferConfig:: { driver_config: self.driver_config.prescaler(odd, div), - _std: PhantomData, _fmt: PhantomData, } } @@ -216,7 +203,6 @@ impl I2sTransferConfig { pub fn request_frequency(self, freq: u32) -> Self { I2sTransferConfig:: { driver_config: self.driver_config.request_frequency(freq), - _std: PhantomData, _fmt: PhantomData, } } @@ -227,7 +213,6 @@ impl I2sTransferConfig { pub fn require_frequency(self, freq: u32) -> Self { I2sTransferConfig:: { driver_config: self.driver_config.require_frequency(freq), - _std: PhantomData, _fmt: PhantomData, } } @@ -257,11 +242,10 @@ where I: I2sPeripheral, FMT: DataFormat, { - driver: Driver>, + driver: Driver>, frame: FMT::AudioFrame, frame_state: FrameState, sync: bool, - _std: PhantomData, _fmt: PhantomData, } From 81b4a56c0b2b4f237ff526953eaeda1b088909c2 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 00:36:48 +0200 Subject: [PATCH 79/91] remove I2sStandard from public API --- src/driver.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 0909560..4d30eb6 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -153,20 +153,24 @@ enum Frequency { Require(u32), } -#[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, -} +/// 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)] From 4fb192424e7fbf64d39727b0c7a6f10ca04e0593 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 01:22:00 +0200 Subject: [PATCH 80/91] make Cside flag unavailable in PCM mode --- src/driver.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 4d30eb6..109ea21 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -59,12 +59,17 @@ impl Status { pub fn bsy(&self) -> bool { self.value.bsy().bit() } +} - /// Get the CHSIDE flag. It indicate the channel has been received or to be transmitted. Have - /// no meaning with PCM standard. +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 or when using the PCM standard. + /// 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, From 3e448eaf7d59bed5eba33ac0dcc314142b96dcd4 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 01:25:18 +0200 Subject: [PATCH 81/91] update doc example --- src/driver.rs | 2 +- src/transfer.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 109ea21..ca81ac3 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -12,7 +12,7 @@ //! ```no_run //! let driver_config = I2sDriverConfig::new_master() //! .receive() -//! .standard(I2sStandard::Philips) +//! .standard(Philips) //! .data_format(DataFormat::Data24Channel32) //! .master_clock(true) //! .request_frequency(48_000); diff --git a/src/transfer.rs b/src/transfer.rs index d11cbfa..6de1f91 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -11,8 +11,8 @@ //! ```no_run //! let transfer_config = I2sTransferConfig::new_master() //! .receive() -//! .standard(marker::Philips) -//! .data_format(marker::Data24Channel32) +//! .standard(Philips) +//! .data_format(Data24Channel32) //! .master_clock(true) //! .request_frequency(48_000); //! ``` From 0c4ddbe4c6f35751dcc20cefb390bda6cfac132c Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 01:53:49 +0200 Subject: [PATCH 82/91] remove examples (they are actually test) --- examples/master_transmit_d16f16.rs | 117 ----------------- examples/master_transmit_d16f32.rs | 118 ----------------- examples/master_transmit_d24f32.rs | 118 ----------------- examples/master_transmit_d32f32.rs | 118 ----------------- examples/master_transmit_with_master_clock.rs | 122 ------------------ 5 files changed, 593 deletions(-) delete mode 100644 examples/master_transmit_d16f16.rs delete mode 100644 examples/master_transmit_d16f32.rs delete mode 100644 examples/master_transmit_d24f32.rs delete mode 100644 examples/master_transmit_d32f32.rs delete mode 100644 examples/master_transmit_with_master_clock.rs diff --git a/examples/master_transmit_d16f16.rs b/examples/master_transmit_d16f16.rs deleted file mode 100644 index 141f7c2..0000000 --- a/examples/master_transmit_d16f16.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! -//! Periodically transmits a sequence of 16-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_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; 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(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; -} From b3ad69fecbce2c868bb35d2bc4c5179768878210 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 02:11:06 +0200 Subject: [PATCH 83/91] remove "Mode" marker, not very usefull --- src/driver.rs | 39 +++++++++++++++++++-------------------- src/marker.rs | 14 -------------- src/transfer.rs | 2 +- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index ca81ac3..e58997e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -347,14 +347,12 @@ impl I2sDriverConfig { /// # 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 _mode = PhantomData; - let driver = I2sDriver::> { + pub fn i2s_driver(self, i2s_peripheral: I) -> I2sDriver { + let driver = I2sDriver:: { i2s_peripheral, - _mode, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, }; driver.registers().cr1.reset(); // ensure SPI is disabled driver.registers().cr2.reset(); // disable interrupt and DMA request @@ -578,13 +576,14 @@ impl I2sDriverConfig { /// Driver of a SPI peripheral in I2S mode. /// /// Meant for advanced usage, for example using interrupt or DMA. -pub struct I2sDriver { +pub struct I2sDriver { i2s_peripheral: I, - - _mode: PhantomData, + _ms: PhantomData, + _tr: PhantomData, + _std: PhantomData, } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -595,7 +594,7 @@ where } /// Constructors and Destructors -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -624,13 +623,13 @@ where pub fn reconfigure( self, config: I2sDriverConfig, - ) -> I2sDriver> { + ) -> I2sDriver { let i2s_peripheral = self.i2s_peripheral; config.i2s_driver(i2s_peripheral) } } -impl I2sDriver +impl I2sDriver where I: I2sPeripheral, { @@ -671,7 +670,7 @@ where } /// Status -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -690,7 +689,7 @@ where } /// Transmit only methods -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -713,7 +712,7 @@ where } /// Receive only methods -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -734,7 +733,7 @@ where } /// Error interrupt, Master Receive Mode. -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -747,7 +746,7 @@ where } /// Error interrupt, Slave Mode. -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { @@ -760,7 +759,7 @@ where } /// Sampling Rate -impl I2sDriver> +impl I2sDriver where I: I2sPeripheral, { diff --git a/src/marker.rs b/src/marker.rs index e0d5b40..436a370 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -1,21 +1,7 @@ //! Markers for [`I2sDriver`](crate::driver::I2sDriver), //! [`I2sTransfer`](crate::transfer::I2sTransfer) and their respective configuration. -use core::marker::PhantomData; - use crate::sealed::Sealed; -/// Marker, indicate operation mode of the I2sDriver. -/// -/// - `MS`: `Master` or `Slave` -/// - `TR`: `Transmit` or `Receive` -/// - `STD`: I2S standard, eg `Philips` -#[derive(Debug, Clone, Copy)] -pub struct Mode { - _ms: PhantomData, - _tr: PhantomData, - _std: PhantomData, -} - /// Marker, indicated master mode. #[derive(Debug, Clone, Copy)] pub struct Master; diff --git a/src/transfer.rs b/src/transfer.rs index 6de1f91..3c3bbbd 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -242,7 +242,7 @@ where I: I2sPeripheral, FMT: DataFormat, { - driver: Driver>, + driver: Driver, frame: FMT::AudioFrame, frame_state: FrameState, sync: bool, From 8373b367b82a70fe28e7611b7d64d3b1b555d3fd Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 16:33:03 +0200 Subject: [PATCH 84/91] doc, associated const of trait marker are for internal use only --- src/marker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/marker.rs b/src/marker.rs index 436a370..0e36028 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -76,7 +76,7 @@ impl Data16 for Data16Channel32 {} /// Trait for marker indicating a DataFormat pub trait DataFormat: Sealed { - /// Runtime value. + /// Internal use only (used by configuration types). const VALUE: crate::driver::DataFormat; /// Audio frame representation from API point of view; type AudioFrame: Default; @@ -102,7 +102,7 @@ impl_data_format!( /// Trait for marker indicating a i2s standard. pub trait I2sStandard: Sealed { - /// Runtime value. + /// 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. /// From 7e73f3ad9bf82308fad40823552d114ec0fad7e7 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 16:40:47 +0200 Subject: [PATCH 85/91] fix the test --- src/driver.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index e58997e..5012379 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -785,11 +785,12 @@ where #[cfg(test)] mod tests { + use super::*; #[test] - fn test_div_rounding() { + 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_rounding(n, d); + let res = div_round(n, d); let check = f32::round((n as f32) / (d as f32)) as u32; assert_eq!(res, check); } From 70b3b148729f659275d2075148357add5319d0d2 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 29 May 2022 19:11:18 +0200 Subject: [PATCH 86/91] carte presentation --- src/lib.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e2e47de..a921bf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,36 @@ //! 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 -//! [I2sPeripheral](crate::I2sPeripheral). An [I2sDriver](crate::I2sDriver) object can be created around the I2sPeripheral -//! 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] mod pac; From a0dff707bdd6f1d94979fa2da671469b1fd58877 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 30 May 2022 02:14:35 +0200 Subject: [PATCH 87/91] complete transfer doc --- src/transfer.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 3c3bbbd..c9b9949 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -5,6 +5,9 @@ //! 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: @@ -12,18 +15,87 @@ //! let transfer_config = I2sTransferConfig::new_master() //! .receive() //! .standard(Philips) -//! .data_format(Data24Channel32) +//! .data_format(Data16Channel32) //! .master_clock(true) //! .request_frequency(48_000); //! ``` //! Then you can instantiate the transfer around an `I2sPeripheral`: //! ```no_run //! // instantiate from configuration -//! let transfer = transfer_config.i2s_driver(i2s_peripheral); +//! 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) +//! +//! ```no_run +//! // 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). +//! ```no_run +//! // 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: +//! ```no_run +//! 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; @@ -322,6 +394,7 @@ where I: I2sPeripheral, FMT: Data16 + DataFormat, { + /// Transmit (blocking) data from an iterator. pub fn write_iter(&mut self, samples: ITER) where ITER: IntoIterator, @@ -386,6 +459,7 @@ impl I2sTransfer where I: I2sPeripheral, { + /// Transmit (blocking) data from an iterator. pub fn write_iter(&mut self, samples: ITER) where ITER: IntoIterator, @@ -468,7 +542,7 @@ where STD: I2sStandard, FMT: Data16 + DataFormat, { - //TODO WS_line sensing is protocol dependent + /// Transmit (blocking) data from an iterator. pub fn write_iter(&mut self, samples: ITER) where ITER: IntoIterator, @@ -576,8 +650,8 @@ where I: I2sPeripheral, STD: I2sStandard, { + /// Transmit (blocking) data from an iterator. #[inline] - // Can't make it work now pub fn write_iter(&mut self, samples: ITER) where ITER: IntoIterator, From 9297bf724bc3353f8ddffd6afdd4052917c6bff6 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 30 May 2022 13:44:44 +0200 Subject: [PATCH 88/91] complete driver doc with usage example --- src/driver.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 5012379..c81a7ea 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -13,7 +13,7 @@ //! let driver_config = I2sDriverConfig::new_master() //! .receive() //! .standard(Philips) -//! .data_format(DataFormat::Data24Channel32) +//! .data_format(DataFormat::Data16Channel32) //! .master_clock(true) //! .request_frequency(48_000); //! ``` @@ -25,6 +25,24 @@ //! // 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: +//! ```no_run +//! 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; From d4feaf43a0ff7541fb4a03d09bd41f81059ea169 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 30 May 2022 15:28:04 +0200 Subject: [PATCH 89/91] fix doc example producing error with cargo test --- src/driver.rs | 5 +++-- src/transfer.rs | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index c81a7ea..65dd9c2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -10,6 +10,7 @@ //! //! [`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) @@ -18,7 +19,7 @@ //! .request_frequency(48_000); //! ``` //! Then you can instantiate the driver around an `I2sPeripheral`: -//! ```no_run +//! ```ignore //! // instantiate from configuration //! let driver = driver_config.i2s_driver(i2s_peripheral); //! @@ -33,7 +34,7 @@ //! available informations. //! //! Pseudocode example when driver is configured to receive 16 bit audio data: -//! ```no_run +//! ```ignore //! let status = driver.status(); //! if status.rxne() { //! let data = driver.read_data_register(); diff --git a/src/transfer.rs b/src/transfer.rs index c9b9949..9aa6526 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -12,6 +12,7 @@ //! //! [`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) @@ -20,7 +21,7 @@ //! .request_frequency(48_000); //! ``` //! Then you can instantiate the transfer around an `I2sPeripheral`: -//! ```no_run +//! ```ignore //! // instantiate from configuration //! let transfer = transfer_config.i2s_transfer(i2s_peripheral); //! @@ -32,7 +33,7 @@ //! //! Transmitting data can be done with `write_iter` (blocking API) or `write` (non-blocking API) //! -//! ```no_run +//! ```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] = [ @@ -59,7 +60,7 @@ //! # Receiving data //! //! Receiving data can be done with `read_while` (blocking API) or `read` (non-blocking API). -//! ```no_run +//! ```ignore //! // buffer to record 1 second of 8 bit mono data at 48 kHz //! let mut buf = [0u8; 48000]; //! @@ -86,7 +87,7 @@ //! //! 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: -//! ```no_run +//! ```ignore //! let mut samples = (0, 0); //! loop { //! if let Ok(s) = transfer1.read() { From 47f9cc1f3e2eaff0eb5bf8432fc164a8df604f93 Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Mon, 30 May 2022 16:23:23 +0200 Subject: [PATCH 90/91] Update README remove branch.md (ready for merge) --- README.md | 16 +++++++++------- rework-branch.md | 33 --------------------------------- 2 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 rework-branch.md diff --git a/README.md b/README.md index a883171..f9189e8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # STM32 I2S driver -*This library is currently redesigned, see [rework-branch.md](rework-branch.md) for details* - -This library provides a driver for I2S communication using the SPI peripherals on some STM32 microcontrollers. +This library provides driver and abstractions for I2S communication using the +SPI peripherals on some STM32 microcontrollers. ## Differences between STM32 models -According to application note [AN5543](https://www.st.com/resource/en/application_note/dm00725181-enhanced-methods-to-handle-spi-communication-on-stm32-devices-stmicroelectronics.pdf), -there are four major versions of the SPI/I2S peripheral used on STM32 microcontrollers: +According to application note +[AN5543](https://www.st.com/resource/en/application_note/dm00725181-enhanced-methods-to-handle-spi-communication-on-stm32-devices-stmicroelectronics.pdf), +there are four major versions of the SPI/I2S peripheral used on STM32 +microcontrollers: * 1.2.x: F1, F2, F4, L0, L1 * 1.3.x: F0, F3, F7, L4, L5, WB, WL @@ -19,8 +20,9 @@ This library currently has code for SPI version 1.2 (STM32F1, STM32F2, STM32F4, ## Status This library has been tested on a few different STM32F4 microcontrollers. The -other models that use the same SPI version (F1, F2, L0, and L1) may work, -but I have not tested any of them. +other models that use the same SPI version (F1, F2, L0, and L1) may work, but +we haven't tested any of them. Trait implementation and working example will be +availaible in stm32f4xx-hal. ## License diff --git a/rework-branch.md b/rework-branch.md deleted file mode 100644 index d26a7e6..0000000 --- a/rework-branch.md +++ /dev/null @@ -1,33 +0,0 @@ -# rework branch - -Version 0.2 of this crate have many design flaw making it difficult to use in -general and even unusable in several situations. A redesign is required. The -new approach is to have a low-level driver exposing hardware specificity for -precise control and higher level abstraction will be built on top of this -driver, not inside it. - -You can found below a list of important issues. - -## Undocumented side effects - -reading the status register can reset error flag. Since many function access -to this register, this make impossible to get reliably those errors. - -## Not meaningful and miss usable API - -transmit/receive blocking a slice of interleaved samples: which is left which -is right ? First sample can be left or right depending what happened before. - -`ready_to_transmit` and `sample_ready` return `Option`. The `Channel` -information is meaningless in PCM mode. - -## Missing feature - -Slave operation require to read the WS pin to synchronise reliably. This -functionality is missing, so slave operation is near to unusable - - - - - - From c518921af8abdbb908514c319a909b3a37a3630d Mon Sep 17 00:00:00 2001 From: Yruama_Lairba Date: Sun, 19 Jun 2022 17:44:30 +0200 Subject: [PATCH 91/91] fix errors and remove some commented code --- src/transfer.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 9aa6526..2c18c98 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -527,7 +527,7 @@ where self.frame_state = RightLsb; } RightLsb => { - let data = (frame.1 as u32 & 0xFFFF) as u16; + let data = (self.frame.1 as u32 & 0xFFFF) as u16; self.driver.write_data_register(data); self.frame_state = LeftMsb; } @@ -743,7 +743,7 @@ where self.frame_state = RightLsb; } RightLsb => { - let data = (frame.1 as u32 & 0xFFFF) as u16; + let data = (self.frame.1 as u32 & 0xFFFF) as u16; self.driver.write_data_register(data); self.frame_state = LeftMsb; } @@ -1016,8 +1016,7 @@ where self.driver.disable(); } } else if !self._ws_is_start() { - //defmt::println!("sycing"); - self.frame_state = RightMsb; + self.frame_state = LeftMsb; self.driver.enable(); self.driver.read_data_register(); self.driver.status(); @@ -1130,8 +1129,7 @@ where self.driver.disable(); } } else if !self._ws_is_start() { - //defmt::println!("sycing"); - self.frame_state = RightMsb; + self.frame_state = LeftMsb; self.driver.enable(); self.driver.read_data_register(); self.driver.status();