use embassy_futures::join::join; use embassy_stm32::{ dma::Transfer, gpio::{self, low_level::AFType, AnyPin}, into_ref, pac::spi::vals, spi::{ self, BitOrder, CsPin, Error, Instance, MisoPin, Mode, MosiPin, Phase, Polarity, RxDma, SckPin, TxDma }, Peripheral, PeripheralRef }; /// SPI configuration. #[non_exhaustive] #[derive(Copy, Clone)] pub struct Config { /// SPI mode. pub mode: Mode, /// Bit order. pub bit_order: BitOrder } impl Default for Config { fn default() -> Self { Self { mode: spi::Config::default().mode, bit_order: BitOrder::MsbFirst } } } impl Config { fn raw_phase(&self) -> vals::Cpha { match self.mode.phase { Phase::CaptureOnSecondTransition => vals::Cpha::SECONDEDGE, Phase::CaptureOnFirstTransition => vals::Cpha::FIRSTEDGE } } fn raw_polarity(&self) -> vals::Cpol { match self.mode.polarity { Polarity::IdleHigh => vals::Cpol::IDLEHIGH, Polarity::IdleLow => vals::Cpol::IDLELOW } } fn raw_byte_order(&self) -> vals::Lsbfirst { match self.bit_order { BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST, BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST } } } pub struct SpiSlave<'d, T: Instance, Tx, Rx> { _peri: PeripheralRef<'d, T>, sck: Option>, mosi: Option>, miso: Option>, cs: Option>, txdma: PeripheralRef<'d, Tx>, rxdma: PeripheralRef<'d, Rx> } impl<'d, T: Instance, Tx, Rx> SpiSlave<'d, T, Tx, Rx> { /// Create a new SPI driver. pub fn new( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, cs: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, config: Config ) -> Self { into_ref!(peri, sck, mosi, miso, cs); sck.set_as_af(sck.af_num(), AFType::Input); sck.set_speed(gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); mosi.set_speed(gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); miso.set_speed(gpio::Speed::VeryHigh); cs.set_as_af(cs.af_num(), AFType::Input); cs.set_speed(gpio::Speed::VeryHigh); Self::new_inner( peri, Some(sck.map_into()), Some(mosi.map_into()), Some(miso.map_into()), Some(cs.map_into()), txdma, rxdma, config ) } fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, mosi: Option>, miso: Option>, cs: Option>, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, config: Config ) -> Self { into_ref!(peri, txdma, rxdma); let cpha = config.raw_phase(); let cpol = config.raw_polarity(); let lsbfirst = config.raw_byte_order(); T::enable_and_reset(); T::REGS.cr2().modify(|w| { w.set_ssoe(false); }); T::REGS.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); w.set_mstr(vals::Mstr::SLAVE); w.set_spe(true); w.set_lsbfirst(lsbfirst); w.set_ssm(false); w.set_crcen(false); w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); if mosi.is_none() { w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); } w.set_dff(vals::Dff::EIGHTBIT); }); Self { _peri: peri, sck, mosi, miso, cs, txdma, rxdma } } /// SPI read, using DMA. pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> where Tx: TxDma, Rx: RxDma { if data.is_empty() { return Ok(()); } T::REGS.cr1().modify(|w| { w.set_spe(false); }); // clear rxfifo while T::REGS.sr().read().rxne() { let _ = T::REGS.dr().read(); } T::REGS.cr2().modify(|w| w.set_rxdmaen(true)); let clock_byte_count = data.len(); let rx_request = self.rxdma.request(); let rx_src = T::REGS.dr().as_ptr() as *mut u8; let rx_f = unsafe { Transfer::new_read( &mut self.rxdma, rx_request, rx_src, data, Default::default() ) }; let tx_request = self.txdma.request(); let tx_dst = T::REGS.dr().as_ptr() as *mut u8; let clock_byte = 0x00u8; let tx_f = unsafe { Transfer::new_write_repeated( &mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst, Default::default() ) }; T::REGS.cr2().modify(|w| w.set_txdmaen(true)); T::REGS.cr1().modify(|w| { w.set_spe(true); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); join(tx_f, rx_f).await; // finish dma while T::REGS.sr().read().bsy() {} T::REGS.cr1().modify(|w| { w.set_spe(false); }); T::REGS.cr2().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); }); Ok(()) } }