use kaptan_spi::{CompletionError, SpiCompletion}; use stm32f7::stm32f722::{dma2, spi1}; const QUIESCE_POLLS: usize = 1024; pub(super) fn bus_finished(spi: &spi1::RegisterBlock) -> bool { let status = spi.sr().read(); status.ftlvl().is_empty() && status.bsy().is_not_busy() } pub(super) fn complete_transfer( spi: &spi1::RegisterBlock, rx: &dma2::ST, tx: &dma2::ST, completion: SpiCompletion, has_fault: impl FnOnce() -> bool, ) -> Result { spi.cr2() .modify(|_, write| write.rxdmaen().clear_bit().txdmaen().clear_bit()); rx.cr().modify(|_, write| write.en().clear_bit()); tx.cr().modify(|_, write| write.en().clear_bit()); // Leave SPI enabled while queued frames drain. Writing EN=0 does not // establish that DMA stopped or that the final SPI clock completed. let quiescent = (0..QUIESCE_POLLS).any(|_| { rx.cr().read().en().is_disabled() && tx.cr().read().en().is_disabled() && bus_finished(spi) }); spi.cr1().modify(|_, write| write.spe().clear_bit()); cortex_m::asm::dsb(); if !quiescent { return Err(CompletionError::Quarantined); } let complete = completion == SpiCompletion::Complete && rx.ndtr().read().bits() == 0 && tx.ndtr().read().bits() == 0 && spi.sr().read().frlvl().is_empty() && !has_fault(); // Cancellation can leave old frames in the four-byte RX FIFO. Check // faults before draining: the DR/SR reads can clear an overrun. for _ in 0..4 { if spi.sr().read().frlvl().is_empty() { break; } let _ = spi.dr().read().bits(); } if !spi.sr().read().frlvl().is_empty() { return Err(CompletionError::Quarantined); } Ok(if complete { SpiCompletion::Complete } else { SpiCompletion::TransferFault }) }