From bcdf55cb238b59eeda468581a5029d0b1ea1b367 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sat, 27 Aug 2022 21:49:12 +0300 Subject: [PATCH] optimized Spi::write & move BSY to Spi::disable --- CHANGELOG.md | 3 +++ src/spi.rs | 68 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1165ba60..420c6530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - Bump `stm32f4-staging` + - Optimized version of blocking SPI write [#523] + +[#523]: https://github.com/stm32-rs/stm32f4xx-hal/pull/523 ## [v0.22.1] - 2024-11-03 diff --git a/src/spi.rs b/src/spi.rs index 6b3f52e7..c2e0c05d 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -607,7 +607,7 @@ impl Spi { /// Convert the spi to another mode. fn into_mode(self) -> Spi { let mut spi = Spi::_new(self.inner.spi, self.pins); - spi.enable(false); + spi.disable(); spi.init() } } @@ -624,7 +624,7 @@ impl SpiSlave { /// Convert the spi to another mode. fn into_mode(self) -> SpiSlave { let mut spi = SpiSlave::_new(self.inner.spi, self.pins); - spi.enable(false); + spi.disable(); spi.init() } } @@ -703,12 +703,18 @@ impl Inner { Self { spi } } - /// Enable/disable spi - pub fn enable(&mut self, enable: bool) { - self.spi.cr1().modify(|_, w| { - // spe: enable the SPI bus - w.spe().bit(enable) - }); + /// Enable SPI + pub fn enable(&mut self) { + // spe: enable the SPI bus + self.spi.cr1().modify(|_, w| w.spe().set_bit()); + } + + /// Disable SPI + pub fn disable(&mut self) { + // Wait for !BSY + while self.is_busy() {} + // spe: disable the SPI bus + self.spi.cr1().modify(|_, w| w.spe().clear_bit()); } /// Select which frame format is used for data transfers @@ -752,6 +758,19 @@ impl Inner { self.spi.sr().read().ovr().bit_is_set() } + fn check_errors(&self) -> Result<(), Error> { + let sr = self.spi.sr().read(); + if sr.ovr().bit_is_set() { + Err(Error::Overrun) + } else if sr.modf().bit_is_set() { + Err(Error::ModeFault) + } else if sr.crcerr().bit_is_set() { + Err(Error::Crc) + } else { + Ok(()) + } + } + #[inline] fn bidi_output(&mut self) { self.spi.cr1().modify(|_, w| w.bidioe().set_bit()); @@ -811,23 +830,38 @@ impl Inner { }) } + // Implement write as per the "Transmit only procedure" + // RM SPI::3.5. This is more than twice as fast as the + // default Write<> implementation (which reads and drops each + // received value) fn spi_write( &mut self, words: impl IntoIterator, ) -> Result<(), Error> { if BIDI { self.bidi_output(); - for word in words.into_iter() { - nb::block!(self.check_send(word))?; - } - } else { - for word in words.into_iter() { - nb::block!(self.check_send(word))?; - nb::block!(self.check_read::())?; + } + // Write each word when the tx buffer is empty + for word in words { + loop { + let sr = self.spi.sr().read(); + if sr.txe().bit_is_set() { + self.write_data_reg(word); + if sr.modf().bit_is_set() { + return Err(Error::ModeFault); + } + break; + } } } - - Ok(()) + // Wait for final TXE + while !self.is_tx_empty() {} + if !BIDI { + // Clear OVR set due to dropped received values + let _: W = self.read_data_reg(); + } + let _ = self.spi.sr().read(); + self.check_errors() } fn listen_event(&mut self, disable: Option>, enable: Option>) {