1340: Add I2S for f4 r=Dirbaio a=xoviat This is only for f4, but it puts us equal to or ahead of the standard rust hal. 1474: stm32: Fix watchdog timeout computation r=Dirbaio a=rmja Co-authored-by: xoviat <xoviat@users.noreply.github.com> Co-authored-by: Rasmus Melchior Jacobsen <rmja@laesoe.org>
This commit is contained in:
		@@ -420,6 +420,10 @@ fn main() {
 | 
				
			|||||||
        (("spi", "SCK"), quote!(crate::spi::SckPin)),
 | 
					        (("spi", "SCK"), quote!(crate::spi::SckPin)),
 | 
				
			||||||
        (("spi", "MOSI"), quote!(crate::spi::MosiPin)),
 | 
					        (("spi", "MOSI"), quote!(crate::spi::MosiPin)),
 | 
				
			||||||
        (("spi", "MISO"), quote!(crate::spi::MisoPin)),
 | 
					        (("spi", "MISO"), quote!(crate::spi::MisoPin)),
 | 
				
			||||||
 | 
					        (("spi", "NSS"), quote!(crate::spi::CsPin)),
 | 
				
			||||||
 | 
					        (("spi", "I2S_MCK"), quote!(crate::spi::MckPin)),
 | 
				
			||||||
 | 
					        (("spi", "I2S_CK"), quote!(crate::spi::CkPin)),
 | 
				
			||||||
 | 
					        (("spi", "I2S_WS"), quote!(crate::spi::WsPin)),
 | 
				
			||||||
        (("i2c", "SDA"), quote!(crate::i2c::SdaPin)),
 | 
					        (("i2c", "SDA"), quote!(crate::i2c::SdaPin)),
 | 
				
			||||||
        (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
 | 
					        (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
 | 
				
			||||||
        (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
 | 
					        (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										310
									
								
								embassy-stm32/src/i2s.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								embassy-stm32/src/i2s.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,310 @@
 | 
				
			|||||||
 | 
					use embassy_hal_common::into_ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::gpio::sealed::{AFType, Pin as _};
 | 
				
			||||||
 | 
					use crate::gpio::AnyPin;
 | 
				
			||||||
 | 
					use crate::pac::spi::vals;
 | 
				
			||||||
 | 
					use crate::rcc::get_freqs;
 | 
				
			||||||
 | 
					use crate::spi::{Config as SpiConfig, *};
 | 
				
			||||||
 | 
					use crate::time::Hertz;
 | 
				
			||||||
 | 
					use crate::{Peripheral, PeripheralRef};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Mode {
 | 
				
			||||||
 | 
					    Master,
 | 
				
			||||||
 | 
					    Slave,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Function {
 | 
				
			||||||
 | 
					    Transmit,
 | 
				
			||||||
 | 
					    Receive,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Standard {
 | 
				
			||||||
 | 
					    Philips,
 | 
				
			||||||
 | 
					    MsbFirst,
 | 
				
			||||||
 | 
					    LsbFirst,
 | 
				
			||||||
 | 
					    PcmLongSync,
 | 
				
			||||||
 | 
					    PcmShortSync,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Standard {
 | 
				
			||||||
 | 
					    #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					    pub const fn i2sstd(&self) -> vals::I2sstd {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Standard::Philips => vals::I2sstd::PHILIPS,
 | 
				
			||||||
 | 
					            Standard::MsbFirst => vals::I2sstd::MSB,
 | 
				
			||||||
 | 
					            Standard::LsbFirst => vals::I2sstd::LSB,
 | 
				
			||||||
 | 
					            Standard::PcmLongSync => vals::I2sstd::PCM,
 | 
				
			||||||
 | 
					            Standard::PcmShortSync => vals::I2sstd::PCM,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					    pub const fn pcmsync(&self) -> vals::Pcmsync {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Standard::PcmLongSync => vals::Pcmsync::LONG,
 | 
				
			||||||
 | 
					            _ => vals::Pcmsync::SHORT,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum Format {
 | 
				
			||||||
 | 
					    /// 16 bit data length on 16 bit wide channel
 | 
				
			||||||
 | 
					    Data16Channel16,
 | 
				
			||||||
 | 
					    /// 16 bit data length on 32 bit wide channel
 | 
				
			||||||
 | 
					    Data16Channel32,
 | 
				
			||||||
 | 
					    /// 24 bit data length on 32 bit wide channel
 | 
				
			||||||
 | 
					    Data24Channel32,
 | 
				
			||||||
 | 
					    /// 32 bit data length on 32 bit wide channel
 | 
				
			||||||
 | 
					    Data32Channel32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Format {
 | 
				
			||||||
 | 
					    #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					    pub const fn datlen(&self) -> vals::Datlen {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Format::Data16Channel16 => vals::Datlen::SIXTEENBIT,
 | 
				
			||||||
 | 
					            Format::Data16Channel32 => vals::Datlen::SIXTEENBIT,
 | 
				
			||||||
 | 
					            Format::Data24Channel32 => vals::Datlen::TWENTYFOURBIT,
 | 
				
			||||||
 | 
					            Format::Data32Channel32 => vals::Datlen::THIRTYTWOBIT,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					    pub const fn chlen(&self) -> vals::Chlen {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Format::Data16Channel16 => vals::Chlen::SIXTEENBIT,
 | 
				
			||||||
 | 
					            Format::Data16Channel32 => vals::Chlen::THIRTYTWOBIT,
 | 
				
			||||||
 | 
					            Format::Data24Channel32 => vals::Chlen::THIRTYTWOBIT,
 | 
				
			||||||
 | 
					            Format::Data32Channel32 => vals::Chlen::THIRTYTWOBIT,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub enum ClockPolarity {
 | 
				
			||||||
 | 
					    IdleLow,
 | 
				
			||||||
 | 
					    IdleHigh,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ClockPolarity {
 | 
				
			||||||
 | 
					    #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					    pub const fn ckpol(&self) -> vals::Ckpol {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            ClockPolarity::IdleHigh => vals::Ckpol::IDLEHIGH,
 | 
				
			||||||
 | 
					            ClockPolarity::IdleLow => vals::Ckpol::IDLELOW,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// [`I2S`] configuration.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///  - `MS`: `Master` or `Slave`
 | 
				
			||||||
 | 
					///  - `TR`: `Transmit` or `Receive`
 | 
				
			||||||
 | 
					///  - `STD`: I2S standard, eg `Philips`
 | 
				
			||||||
 | 
					///  - `FMT`: Frame Format marker, eg `Data16Channel16`
 | 
				
			||||||
 | 
					#[non_exhaustive]
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					pub struct Config {
 | 
				
			||||||
 | 
					    pub mode: Mode,
 | 
				
			||||||
 | 
					    pub function: Function,
 | 
				
			||||||
 | 
					    pub standard: Standard,
 | 
				
			||||||
 | 
					    pub format: Format,
 | 
				
			||||||
 | 
					    pub clock_polarity: ClockPolarity,
 | 
				
			||||||
 | 
					    pub master_clock: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Config {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mode: Mode::Master,
 | 
				
			||||||
 | 
					            function: Function::Transmit,
 | 
				
			||||||
 | 
					            standard: Standard::Philips,
 | 
				
			||||||
 | 
					            format: Format::Data16Channel16,
 | 
				
			||||||
 | 
					            clock_polarity: ClockPolarity::IdleLow,
 | 
				
			||||||
 | 
					            master_clock: true,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct I2S<'d, T: Instance, Tx, Rx> {
 | 
				
			||||||
 | 
					    _peri: Spi<'d, T, Tx, Rx>,
 | 
				
			||||||
 | 
					    sd: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
 | 
					    ws: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
 | 
					    ck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
 | 
					    mck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
 | 
				
			||||||
 | 
					    /// Note: Full-Duplex modes are not supported at this time
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
				
			||||||
 | 
					        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
				
			||||||
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
 | 
					        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
				
			||||||
 | 
					        txdma: impl Peripheral<P = Tx> + 'd,
 | 
				
			||||||
 | 
					        rxdma: impl Peripheral<P = Rx> + 'd,
 | 
				
			||||||
 | 
					        freq: Hertz,
 | 
				
			||||||
 | 
					        config: Config,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(sd, ws, ck, mck);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
 | 
					            sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            sd.set_speed(crate::gpio::Speed::VeryHigh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            ws.set_speed(crate::gpio::Speed::VeryHigh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            ck.set_speed(crate::gpio::Speed::VeryHigh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
 | 
				
			||||||
 | 
					            mck.set_speed(crate::gpio::Speed::VeryHigh);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(all(rcc_f4, not(stm32f410)))]
 | 
				
			||||||
 | 
					        let pclk = unsafe { get_freqs() }.plli2s.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(stm32f410)]
 | 
				
			||||||
 | 
					        let pclk = T::frequency();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
 | 
					            use stm32_metapac::spi::vals::{I2scfg, Odd};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud
 | 
				
			||||||
 | 
					            // rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR
 | 
				
			||||||
 | 
					            // register also has to be defined.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            T::REGS.i2spr().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_i2sdiv(div);
 | 
				
			||||||
 | 
					                w.set_odd(match odd {
 | 
				
			||||||
 | 
					                    true => Odd::ODD,
 | 
				
			||||||
 | 
					                    false => Odd::EVEN,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                w.set_mckoe(config.master_clock);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
 | 
				
			||||||
 | 
					            // MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to
 | 
				
			||||||
 | 
					            // the external DAC/ADC audio component (the I2SDIV and ODD values should be
 | 
				
			||||||
 | 
					            // computed depending on the state of the MCK output, for more details refer to
 | 
				
			||||||
 | 
					            // Section 28.4.4: Clock generator).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
 | 
				
			||||||
 | 
					            // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
 | 
				
			||||||
 | 
					            // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
 | 
				
			||||||
 | 
					            // Select also the I2S master mode and direction (Transmitter or Receiver) through the
 | 
				
			||||||
 | 
					            // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 4. If needed, select all the potential interruption sources and the DMA capabilities by
 | 
				
			||||||
 | 
					            // writing the SPI_CR2 register.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 5. The I2SE bit in SPI_I2SCFGR register must be set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            T::REGS.i2scfgr().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_ckpol(config.clock_polarity.ckpol());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                w.set_i2smod(true);
 | 
				
			||||||
 | 
					                w.set_i2sstd(config.standard.i2sstd());
 | 
				
			||||||
 | 
					                w.set_pcmsync(config.standard.pcmsync());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                w.set_datlen(config.format.datlen());
 | 
				
			||||||
 | 
					                w.set_chlen(config.format.chlen());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                w.set_i2scfg(match (config.mode, config.function) {
 | 
				
			||||||
 | 
					                    (Mode::Master, Function::Transmit) => I2scfg::MASTERTX,
 | 
				
			||||||
 | 
					                    (Mode::Master, Function::Receive) => I2scfg::MASTERRX,
 | 
				
			||||||
 | 
					                    (Mode::Slave, Function::Transmit) => I2scfg::SLAVETX,
 | 
				
			||||||
 | 
					                    (Mode::Slave, Function::Receive) => I2scfg::SLAVERX,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                w.set_i2se(true)
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[cfg(spi_v2)]
 | 
				
			||||||
 | 
					        unsafe {}
 | 
				
			||||||
 | 
					        #[cfg(any(spi_v3, spi_v4))]
 | 
				
			||||||
 | 
					        unsafe {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            _peri: spi,
 | 
				
			||||||
 | 
					            sd: Some(sd.map_into()),
 | 
				
			||||||
 | 
					            ws: Some(ws.map_into()),
 | 
				
			||||||
 | 
					            ck: Some(ck.map_into()),
 | 
				
			||||||
 | 
					            mck: Some(mck.map_into()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        Tx: TxDma<T>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self._peri.write(data).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        Tx: TxDma<T>,
 | 
				
			||||||
 | 
					        Rx: RxDma<T>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self._peri.read(data).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> {
 | 
				
			||||||
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
 | 
					            self.sd.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
 | 
					            self.ws.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
 | 
					            self.ck.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
 | 
					            self.mck.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_format: Format) -> (bool, u8) {
 | 
				
			||||||
 | 
					    let coef = if mclk {
 | 
				
			||||||
 | 
					        256
 | 
				
			||||||
 | 
					    } else if let Format::Data16Channel16 = data_format {
 | 
				
			||||||
 | 
					        32
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        64
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (n, d) = (i2s_clock.0, coef * request_freq.0);
 | 
				
			||||||
 | 
					    let division = (n + (d >> 1)) / d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if division < 4 {
 | 
				
			||||||
 | 
					        (false, 2)
 | 
				
			||||||
 | 
					    } else if division > 511 {
 | 
				
			||||||
 | 
					        (true, 255)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        ((division & 1) == 1, (division >> 1) as u8)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -39,6 +39,8 @@ pub mod i2c;
 | 
				
			|||||||
#[cfg(crc)]
 | 
					#[cfg(crc)]
 | 
				
			||||||
pub mod crc;
 | 
					pub mod crc;
 | 
				
			||||||
pub mod flash;
 | 
					pub mod flash;
 | 
				
			||||||
 | 
					#[cfg(all(spi_v1, rcc_f4))]
 | 
				
			||||||
 | 
					pub mod i2s;
 | 
				
			||||||
#[cfg(stm32wb)]
 | 
					#[cfg(stm32wb)]
 | 
				
			||||||
pub mod ipcc;
 | 
					pub mod ipcc;
 | 
				
			||||||
pub mod pwm;
 | 
					pub mod pwm;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,6 +212,17 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
 | 
				
			|||||||
        Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config)
 | 
					        Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub(crate) fn new_internal(
 | 
				
			||||||
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        txdma: impl Peripheral<P = Tx> + 'd,
 | 
				
			||||||
 | 
					        rxdma: impl Peripheral<P = Rx> + 'd,
 | 
				
			||||||
 | 
					        freq: Hertz,
 | 
				
			||||||
 | 
					        config: Config,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn new_inner(
 | 
					    fn new_inner(
 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        sck: Option<PeripheralRef<'d, AnyPin>>,
 | 
					        sck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
@@ -1044,6 +1055,10 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
 | 
				
			|||||||
pin_trait!(SckPin, Instance);
 | 
					pin_trait!(SckPin, Instance);
 | 
				
			||||||
pin_trait!(MosiPin, Instance);
 | 
					pin_trait!(MosiPin, Instance);
 | 
				
			||||||
pin_trait!(MisoPin, Instance);
 | 
					pin_trait!(MisoPin, Instance);
 | 
				
			||||||
 | 
					pin_trait!(CsPin, Instance);
 | 
				
			||||||
 | 
					pin_trait!(MckPin, Instance);
 | 
				
			||||||
 | 
					pin_trait!(CkPin, Instance);
 | 
				
			||||||
 | 
					pin_trait!(WsPin, Instance);
 | 
				
			||||||
dma_trait!(RxDma, Instance);
 | 
					dma_trait!(RxDma, Instance);
 | 
				
			||||||
dma_trait!(TxDma, Instance);
 | 
					dma_trait!(TxDma, Instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,13 +13,13 @@ pub struct IndependentWatchdog<'d, T: Instance> {
 | 
				
			|||||||
const MAX_RL: u16 = 0xFFF;
 | 
					const MAX_RL: u16 = 0xFFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler
 | 
					/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler
 | 
				
			||||||
const fn max_timeout(prescaler: u16) -> u32 {
 | 
					const fn get_timeout_us(prescaler: u16, reload_value: u16) -> u32 {
 | 
				
			||||||
    1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32)
 | 
					    1_000_000 * (reload_value + 1) as u32 / (LSI_FREQ.0 / prescaler as u32)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Calculates watchdog reload value for the given prescaler and desired timeout
 | 
					/// Calculates watchdog reload value for the given prescaler and desired timeout
 | 
				
			||||||
const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {
 | 
					const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {
 | 
				
			||||||
    (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16
 | 
					    (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 - 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: Instance> IndependentWatchdog<'d, T> {
 | 
					impl<'d, T: Instance> IndependentWatchdog<'d, T> {
 | 
				
			||||||
@@ -34,7 +34,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
 | 
				
			|||||||
        // This iterates from 4 (2^2) to 256 (2^8).
 | 
					        // This iterates from 4 (2^2) to 256 (2^8).
 | 
				
			||||||
        let psc_power = unwrap!((2..=8).find(|psc_power| {
 | 
					        let psc_power = unwrap!((2..=8).find(|psc_power| {
 | 
				
			||||||
            let psc = 2u16.pow(*psc_power);
 | 
					            let psc = 2u16.pow(*psc_power);
 | 
				
			||||||
            timeout_us <= max_timeout(psc)
 | 
					            timeout_us <= get_timeout_us(psc, MAX_RL)
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Prescaler value
 | 
					        // Prescaler value
 | 
				
			||||||
@@ -54,6 +54,14 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
 | 
				
			|||||||
            wdg.rlr().write(|w| w.set_rl(rl));
 | 
					            wdg.rlr().write(|w| w.set_rl(rl));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        trace!(
 | 
				
			||||||
 | 
					            "Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})",
 | 
				
			||||||
 | 
					            get_timeout_us(psc, rl),
 | 
				
			||||||
 | 
					            timeout_us,
 | 
				
			||||||
 | 
					            pr,
 | 
				
			||||||
 | 
					            rl
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        IndependentWatchdog {
 | 
					        IndependentWatchdog {
 | 
				
			||||||
            wdg: PhantomData::default(),
 | 
					            wdg: PhantomData::default(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -87,3 +95,27 @@ foreach_peripheral!(
 | 
				
			|||||||
        impl Instance for crate::peripherals::$inst {}
 | 
					        impl Instance for crate::peripherals::$inst {}
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn can_compute_timeout_us() {
 | 
				
			||||||
 | 
					        assert_eq!(125, get_timeout_us(4, 0));
 | 
				
			||||||
 | 
					        assert_eq!(512_000, get_timeout_us(4, MAX_RL));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(8_000, get_timeout_us(256, 0));
 | 
				
			||||||
 | 
					        assert_eq!(32768_000, get_timeout_us(256, MAX_RL));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(8000_000, get_timeout_us(64, 3999));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn can_compute_reload_value() {
 | 
				
			||||||
 | 
					        assert_eq!(0xFFF, reload_value(4, 512_000));
 | 
				
			||||||
 | 
					        assert_eq!(0xFFF, reload_value(256, 32768_000));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(3999, reload_value(64, 8000_000));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								examples/stm32f4/src/bin/i2s_dma.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								examples/stm32f4/src/bin/i2s_dma.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use core::fmt::Write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_stm32::i2s::{Config, I2S};
 | 
				
			||||||
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
 | 
					use heapless::String;
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let p = embassy_stm32::init(Default::default());
 | 
				
			||||||
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut i2s = I2S::new(
 | 
				
			||||||
 | 
					        p.SPI2,
 | 
				
			||||||
 | 
					        p.PC3,  // sd
 | 
				
			||||||
 | 
					        p.PB12, // ws
 | 
				
			||||||
 | 
					        p.PB10, // ck
 | 
				
			||||||
 | 
					        p.PC6,  // mck
 | 
				
			||||||
 | 
					        p.DMA1_CH4,
 | 
				
			||||||
 | 
					        p.DMA1_CH3,
 | 
				
			||||||
 | 
					        Hertz(1_000_000),
 | 
				
			||||||
 | 
					        Config::default(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for n in 0u32.. {
 | 
				
			||||||
 | 
					        let mut write: String<128> = String::new();
 | 
				
			||||||
 | 
					        core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
 | 
				
			||||||
 | 
					        i2s.write(&mut write.as_bytes()).await.ok();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user