From fa7668ba6487eb6ea828596a86bc17909adaf519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Thu, 23 Jan 2020 23:23:47 +0100 Subject: [PATCH 01/12] Implement a polling async SPI interface --- nrf52-hal-common/src/gpio.rs | 8 + nrf52-hal-common/src/spim.rs | 523 +++++++++++++++++++++++++++-------- 2 files changed, 414 insertions(+), 117 deletions(-) diff --git a/nrf52-hal-common/src/gpio.rs b/nrf52-hal-common/src/gpio.rs index 477860dd..883b4118 100644 --- a/nrf52-hal-common/src/gpio.rs +++ b/nrf52-hal-common/src/gpio.rs @@ -5,25 +5,32 @@ use core::marker::PhantomData; /// Input mode (type state) +#[derive(Debug)] pub struct Input { _mode: PhantomData, } /// Floating input (type state) +#[derive(Debug)] pub struct Floating; /// Pulled down input (type state) +#[derive(Debug)] pub struct PullDown; /// Pulled up input (type state) +#[derive(Debug)] pub struct PullUp; /// Output mode (type state) +#[derive(Debug)] pub struct Output { _mode: PhantomData, } /// Push pull output (type state) +#[derive(Debug)] pub struct PushPull; /// Open drain output (type state) +#[derive(Debug)] pub struct OpenDrain; // /// Alternate function @@ -43,6 +50,7 @@ pub enum Level { // across all of the possible pins // =============================================================== /// Generic $PX pin +#[derive(Debug)] pub struct Pin { pub pin: u8, #[cfg(feature = "52840")] diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index 3d178881..cd1caf93 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -13,8 +13,6 @@ use crate::target::{spim0, SPIM0}; pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use spim0::frequency::FREQUENCYW as Frequency; -use core::iter::repeat_with; - #[cfg(any(feature = "52832", feature = "52840"))] use crate::target::{SPIM1, SPIM2}; @@ -29,8 +27,67 @@ use embedded_hal::digital::v2::OutputPin; /// - The SPIM instances share the same address space with instances of SPIS, /// SPI, TWIM, TWIS, and TWI. You need to make sure that conflicting instances /// are disabled before using `Spim`. See product specification, section 15.2. +#[derive(Debug)] pub struct Spim(T); +/// An ongoing transfer that was initiated by a call to `.transfer_polling()`. +/// +/// This transfer must be polled to completion. Failing to poll it until completed might leave the +/// peripheral in an inconsistent state. +#[derive(Debug)] +#[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] +pub struct SpiTransfer<'a, T> { + state: TransferState<'a, T>, + chip_select: &'a mut Pin>, + chunks: core::slice::ChunksMut<'a, u8>, +} + +/// An ongoing transfer that was initiated by a call to `.transfer_even_polling()`. +/// +/// This transfer must be polled to completion. Failing to poll it until completed might leave the +/// peripheral in an inconsistent state. +#[derive(Debug)] +#[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] +pub struct SpiEvenTransfer<'a, T> { + state: TransferState<'a, T>, + chip_select: &'a mut Pin>, + chunks: core::iter::Zip, core::slice::ChunksMut<'a, u8>>, +} + +/// An ongoing transfer that was initiated by a call to `.transfer_uneven_polling()`. +/// +/// This transfer must be polled to completion. Failing to poll it until completed might leave the +/// peripheral in an inconsistent state. +#[derive(Debug)] +#[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] +pub struct SpiUnevenTransfer<'a, T> { + state: TransferState<'a, T>, + chip_select: &'a mut Pin>, + chunks_tx: core::slice::Chunks<'a, u8>, + chunks_rx: core::slice::ChunksMut<'a, u8>, +} + +/// The state of a more advanced transfer. +#[derive(Debug)] +enum TransferState<'a, T> { + /// The spim is idle and awaiting the next chunk, no transfer is happening. + Idle(&'a mut T), + /// The spim is currently performing the specified transfer. + Ongoing(SpiSingleTransfer<'a, T>), + /// The spim transfer misbehaved, errored or panicked in a way that it cannot be completed. + Inconsistent, + /// The spim is done, and a new transfer can be started. + Done, +} + +/// An internal structure corresponding to a single in-progress EasyDMA transfer +#[derive(Debug)] +struct SpiSingleTransfer<'a, T> { + spim: &'a mut T, + tx_len: u32, + rx_len: u32, +} + impl embedded_hal::blocking::spi::Transfer for Spim where T: Instance, @@ -75,6 +132,7 @@ where words.chunks(chunk_sz).try_for_each(|c| step(self, c)) } } + impl Spim where T: Instance, @@ -158,61 +216,12 @@ where /// Internal helper function to setup and execute SPIM DMA transfer fn do_spi_dma_transfer(&mut self, tx: DmaSlice, rx: DmaSlice) -> Result<(), Error> { - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // before any DMA action has started - compiler_fence(SeqCst); - - // Set up the DMA write - self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(tx.ptr) }); - - self.0.txd.maxcnt.write(|w| - // Note that that nrf52840 maxcnt is a wider - // type than a u8, so we use a `_` cast rather than a `u8` cast. - // The MAXCNT field is thus at least 8 bits wide and accepts the full - // range of values that fit in a `u8`. - unsafe { w.maxcnt().bits(tx.len as _ ) }); - - // Set up the DMA read - self.0.rxd.ptr.write(|w| - // This is safe for the same reasons that writing to TXD.PTR is - // safe. Please refer to the explanation there. - unsafe { w.ptr().bits(rx.ptr) }); - self.0.rxd.maxcnt.write(|w| - // This is safe for the same reasons that writing to TXD.MAXCNT is - // safe. Please refer to the explanation there. - unsafe { w.maxcnt().bits(rx.len as _) }); - - // Start SPI transaction - self.0.tasks_start.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); + let mut transfer = SpiSingleTransfer::new(&mut self.0, tx, rx); - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // after all possible DMA actions have completed - compiler_fence(SeqCst); - - // Wait for END event - // - // This event is triggered once both transmitting and receiving are - // done. - while self.0.events_end.read().bits() == 0 {} - - // Reset the event, otherwise it will always read `1` from now on. - self.0.events_end.write(|w| w); - - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // after all possible DMA actions have completed - compiler_fence(SeqCst); - - if self.0.txd.amount.read().bits() != tx.len { - return Err(Error::Transmit); - } - if self.0.rxd.amount.read().bits() != rx.len { - return Err(Error::Receive); + while !transfer.poll_complete()? { + core::sync::atomic::spin_loop_hint(); } + Ok(()) } @@ -242,18 +251,34 @@ where chip_select: &mut Pin>, buffer: &mut [u8], ) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + let mut transfer = SpiTransfer::new(&mut self.0, chip_select, buffer)?; - chip_select.set_low().unwrap(); - - // Don't return early, as we must reset the CS pin - let res = buffer.chunks(EASY_DMA_SIZE).try_for_each(|chunk| { - self.do_spi_dma_transfer(DmaSlice::from_slice(chunk), DmaSlice::from_slice(chunk)) - }); + while !transfer.poll_complete()? { + core::sync::atomic::spin_loop_hint(); + } - chip_select.set_high().unwrap(); + Ok(()) + } - res + /// Read and write from a SPI slave, using a single buffer. + /// + /// This is an async polling version of `transfer()`. You need to call + /// `.poll_complete()` on the returned object until it returns `true`. A + /// good time to do that would be after receiving a SPI interrupt, for + /// example. + /// + /// This method implements a complete read transaction, which consists of + /// the master transmitting what it wishes to read, and the slave responding + /// with the requested data. + /// + /// Uses the provided chip select pin to initiate the transaction. Transmits + /// all bytes in `buffer`, then receives an equal number of bytes. + pub fn transfer_polling<'a>( + &'a mut self, + chip_select: &'a mut Pin>, + buffer: &'a mut [u8], + ) -> Result, Error> { + SpiTransfer::new(&mut self.0, chip_select, buffer) } /// Read and write from a SPI slave, using separate read and write buffers @@ -273,23 +298,38 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable - // slice can only be built from data located in RAM - slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; - - let txi = tx_buffer.chunks(EASY_DMA_SIZE); - let rxi = rx_buffer.chunks_mut(EASY_DMA_SIZE); - - chip_select.set_low().unwrap(); + let mut transfer = SpiEvenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer)?; - // Don't return early, as we must reset the CS pin - let res = txi.zip(rxi).try_for_each(|(t, r)| { - self.do_spi_dma_transfer(DmaSlice::from_slice(t), DmaSlice::from_slice(r)) - }); + while !transfer.poll_complete()? { + core::sync::atomic::spin_loop_hint(); + } - chip_select.set_high().unwrap(); + Ok(()) + } - res + /// Read and write from a SPI slave, using separate read and write buffers + /// + /// This is an async polling version of `transfer_split_even()`. You need to + /// call `.poll_complete()` on the returned object until it returns `true`. + /// A good time to do that would be after receiving a SPI interrupt, for + /// example. + /// + /// This method implements a complete read transaction, which consists of + /// the master transmitting what it wishes to read, and the slave responding + /// with the requested data. + /// + /// Uses the provided chip select pin to initiate the transaction. Transmits + /// all bytes in `tx_buffer`, then receives bytes until `rx_buffer` is full. + /// + /// If `tx_buffer.len() != rx_buffer.len()`, the transaction will stop at the + /// smaller of either buffer. + pub fn transfer_split_even_polling<'a>( + &'a mut self, + chip_select: &'a mut Pin>, + tx_buffer: &'a [u8], + rx_buffer: &'a mut [u8], + ) -> Result, Error> { + SpiEvenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer) } /// Read and write from a SPI slave, using separate read and write buffers @@ -311,48 +351,40 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable - // slice can only be built from data located in RAM - slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; - - // For the tx and rx, we want to return Some(chunk) - // as long as there is data to send. We then chain a repeat to - // the end so once all chunks have been exhausted, we will keep - // getting Nones out of the iterators - let txi = tx_buffer - .chunks(EASY_DMA_SIZE) - .map(|c| Some(c)) - .chain(repeat_with(|| None)); - - let rxi = rx_buffer - .chunks_mut(EASY_DMA_SIZE) - .map(|c| Some(c)) - .chain(repeat_with(|| None)); - - chip_select.set_low().unwrap(); + let mut transfer = SpiUnevenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer)?; - // We then chain the iterators together, and once BOTH are feeding - // back Nones, then we are done sending and receiving - // - // Don't return early, as we must reset the CS pin - let res = txi - .zip(rxi) - .take_while(|(t, r)| t.is_some() && r.is_some()) - // We also turn the slices into either a DmaSlice (if there was data), or a null - // DmaSlice (if there is no data) - .map(|(t, r)| { - ( - t.map(|t| DmaSlice::from_slice(t)) - .unwrap_or_else(|| DmaSlice::null()), - r.map(|r| DmaSlice::from_slice(r)) - .unwrap_or_else(|| DmaSlice::null()), - ) - }) - .try_for_each(|(t, r)| self.do_spi_dma_transfer(t, r)); + while !transfer.poll_complete()? { + core::sync::atomic::spin_loop_hint(); + } - chip_select.set_high().unwrap(); + Ok(()) + } - res + /// Read and write from a SPI slave, using separate read and write buffers + /// + /// This is an async polling version of `transfer_split_uneven()`. You need to + /// call `.poll_complete()` on the returned object until it returns `true`. + /// A good time to do that would be after receiving a SPI interrupt, for + /// example. + /// + /// This method implements a complete read transaction, which consists of + /// the master transmitting what it wishes to read, and the slave responding + /// with the requested data. + /// + /// Uses the provided chip select pin to initiate the transaction. Transmits + /// all bytes in `tx_buffer`, then receives bytes until `rx_buffer` is full. + /// + /// This method is more complicated than the other `transfer` methods because + /// it is allowed to perform transactions where `tx_buffer.len() != rx_buffer.len()`. + /// If this occurs, extra incoming bytes will be discarded, OR extra outgoing bytes + /// will be filled with the `orc` value. + pub fn transfer_split_uneven_polling<'a>( + &'a mut self, + chip_select: &'a mut Pin>, + tx_buffer: &'a [u8], + rx_buffer: &'a mut [u8], + ) -> Result, Error> { + SpiUnevenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer) } /// Write to an SPI slave @@ -375,6 +407,261 @@ where } } +impl<'a, T> SpiTransfer<'a, T> +where + T: Instance, +{ + pub fn new( + spim: &'a mut T, + chip_select: &'a mut Pin>, + buffer: &'a mut [u8], + ) -> Result { + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + + let chunks = buffer.chunks_mut(EASY_DMA_SIZE); + + chip_select.set_low().unwrap(); + + let state = TransferState::new(spim); + + Ok(Self { + state, + chip_select, + chunks, + }) + } + + pub fn poll_complete(&mut self) -> Result { + let chunks = &mut self.chunks; + self.state.advance(|| { + chunks + .next() + .map(|chunk| (DmaSlice::from_slice(chunk), DmaSlice::from_slice(chunk))) + }) + } +} + +impl<'a, T> Drop for SpiTransfer<'a, T> { + fn drop(&mut self) { + self.chip_select.set_high().unwrap(); + } +} + +impl<'a, T> SpiEvenTransfer<'a, T> +where + T: Instance, +{ + pub fn new( + spim: &'a mut T, + chip_select: &'a mut Pin>, + tx_buffer: &'a [u8], + rx_buffer: &'a mut [u8], + ) -> Result { + // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable + // slice can only be built from data located in RAM + slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; + + let txi = tx_buffer.chunks(EASY_DMA_SIZE); + let rxi = rx_buffer.chunks_mut(EASY_DMA_SIZE); + let chunks = txi.zip(rxi); + + chip_select.set_low().unwrap(); + + let state = TransferState::new(spim); + + Ok(Self { + state, + chip_select, + chunks, + }) + } + + pub fn poll_complete(&mut self) -> Result { + let chunks = &mut self.chunks; + self.state.advance(|| { + chunks + .next() + .map(|(tx, rx)| (DmaSlice::from_slice(tx), DmaSlice::from_slice(rx))) + }) + } +} + +impl<'a, T> Drop for SpiEvenTransfer<'a, T> { + fn drop(&mut self) { + self.chip_select.set_high().unwrap(); + } +} + +impl<'a, T> SpiUnevenTransfer<'a, T> +where + T: Instance, +{ + pub fn new( + spim: &'a mut T, + chip_select: &'a mut Pin>, + tx_buffer: &'a [u8], + rx_buffer: &'a mut [u8], + ) -> Result { + // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable + // slice can only be built from data located in RAM + slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; + + let chunks_tx = tx_buffer.chunks(EASY_DMA_SIZE); + let chunks_rx = rx_buffer.chunks_mut(EASY_DMA_SIZE); + + chip_select.set_low().unwrap(); + + let state = TransferState::new(spim); + + Ok(Self { + state, + chip_select, + chunks_tx, + chunks_rx, + }) + } + + pub fn poll_complete(&mut self) -> Result { + let chunks_tx = &mut self.chunks_tx; + let chunks_rx = &mut self.chunks_rx; + self.state + .advance(|| match (chunks_tx.next(), chunks_rx.next()) { + (None, None) => None, + (tx, rx) => Some(( + tx.map(|tx| DmaSlice::from_slice(tx)) + .unwrap_or(DmaSlice::null()), + rx.map(|rx| DmaSlice::from_slice(rx)) + .unwrap_or(DmaSlice::null()), + )), + }) + } +} + +impl<'a, T> Drop for SpiUnevenTransfer<'a, T> { + fn drop(&mut self) { + self.chip_select.set_high().unwrap(); + } +} + +impl<'a, T> TransferState<'a, T> +where + T: Instance, +{ + fn new(spim: &'a mut T) -> Self { + TransferState::Idle(spim) + } + + fn advance( + &mut self, + mut next_chunk: impl FnMut() -> Option<(DmaSlice, DmaSlice)>, + ) -> Result { + loop { + match core::mem::replace(self, TransferState::Inconsistent) { + TransferState::Idle(spim) => match next_chunk() { + Some((tx, rx)) => { + let transfer = SpiSingleTransfer::new(spim, tx, rx); + *self = TransferState::Ongoing(transfer); + return Ok(false); + } + None => *self = TransferState::Done, + }, + TransferState::Ongoing(mut transfer) => { + if transfer.poll_complete()? { + *self = TransferState::Idle(transfer.spim) + } else { + *self = TransferState::Ongoing(transfer); + return Ok(false); + } + } + TransferState::Done => { + *self = TransferState::Done; + return Ok(true); + } + TransferState::Inconsistent => return Err(Error::InconsistentState), + } + } + } +} + +impl<'a, T> SpiSingleTransfer<'a, T> +where + T: Instance, +{ + fn new(spim: &'a mut T, tx: DmaSlice, rx: DmaSlice) -> Self { + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started + compiler_fence(SeqCst); + + // Set up the DMA write + spim.txd.ptr.write(|w| unsafe { w.ptr().bits(tx.ptr) }); + + spim.txd.maxcnt.write(|w| + // Note that that nrf52840 maxcnt is a wider + // type than a u8, so we use a `_` cast rather than a `u8` cast. + // The MAXCNT field is thus at least 8 bits wide and accepts the full + // range of values that fit in a `u8`. + unsafe { w.maxcnt().bits(tx.len as _) }); + + // Set up the DMA read + spim.rxd.ptr.write(|w| + // This is safe for the same reasons that writing to TXD.PTR is + // safe. Please refer to the explanation there. + unsafe { w.ptr().bits(rx.ptr) }); + spim.rxd.maxcnt.write(|w| + // This is safe for the same reasons that writing to TXD.MAXCNT is + // safe. Please refer to the explanation there. + unsafe { w.maxcnt().bits(rx.len as _) }); + + // Start SPI transaction + spim.tasks_start.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed + compiler_fence(SeqCst); + + let tx_len = tx.len; + let rx_len = rx.len; + + SpiSingleTransfer { + spim, + tx_len, + rx_len, + } + } + + fn poll_complete(&mut self) -> Result { + // Check for END event + // + // This event is triggered once both transmitting and receiving are + // done. + if self.spim.events_end.read().bits() == 0 { + // Reset the event, otherwise it will always read `1` from now on. + self.spim.events_end.write(|w| w); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed + compiler_fence(SeqCst); + + if self.spim.txd.amount.read().bits() != self.tx_len { + return Err(Error::Transmit); + } + + if self.spim.rxd.amount.read().bits() != self.rx_len { + return Err(Error::Receive); + } + + Ok(true) + } else { + Ok(false) + } + } +} + /// GPIO pins for SPIM interface pub struct Pins { /// SPI clock @@ -397,6 +684,8 @@ pub enum Error { DMABufferNotInDataMemory, Transmit, Receive, + /// The peripheral is in an inconsistent state, because of encountering an error mid-transfer. + InconsistentState, } /// Implemented by all SPIM instances From b69e5d7acd318d64024e7c67aeb25be1f3051b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 25 Jan 2020 12:03:27 +0100 Subject: [PATCH 02/12] Enable transactional polling transfers --- nrf52-hal-common/src/spim.rs | 321 +++++++++++++++++------------------ 1 file changed, 154 insertions(+), 167 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index cd1caf93..e2b09817 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -30,6 +30,13 @@ use embedded_hal::digital::v2::OutputPin; #[derive(Debug)] pub struct Spim(T); +/// An ongoing SPI transaction that is holding the CS pin low. +#[derive(Debug)] +pub struct SpiTransaction<'a, T> { + chip_select: &'a mut Pin>, + spim: &'a mut T, +} + /// An ongoing transfer that was initiated by a call to `.transfer_polling()`. /// /// This transfer must be polled to completion. Failing to poll it until completed might leave the @@ -37,8 +44,8 @@ pub struct Spim(T); #[derive(Debug)] #[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] pub struct SpiTransfer<'a, T> { - state: TransferState<'a, T>, - chip_select: &'a mut Pin>, + spim: &'a mut T, + state: TransferState, chunks: core::slice::ChunksMut<'a, u8>, } @@ -49,8 +56,8 @@ pub struct SpiTransfer<'a, T> { #[derive(Debug)] #[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] pub struct SpiEvenTransfer<'a, T> { - state: TransferState<'a, T>, - chip_select: &'a mut Pin>, + spim: &'a mut T, + state: TransferState, chunks: core::iter::Zip, core::slice::ChunksMut<'a, u8>>, } @@ -61,29 +68,26 @@ pub struct SpiEvenTransfer<'a, T> { #[derive(Debug)] #[must_use = "This transfer must be polled to completion. Failing to poll it until completed might leave the peripheral in an inconsistent state."] pub struct SpiUnevenTransfer<'a, T> { - state: TransferState<'a, T>, - chip_select: &'a mut Pin>, + spim: &'a mut T, + state: TransferState, chunks_tx: core::slice::Chunks<'a, u8>, chunks_rx: core::slice::ChunksMut<'a, u8>, } /// The state of a more advanced transfer. #[derive(Debug)] -enum TransferState<'a, T> { +enum TransferState { /// The spim is idle and awaiting the next chunk, no transfer is happening. - Idle(&'a mut T), + Done, /// The spim is currently performing the specified transfer. - Ongoing(SpiSingleTransfer<'a, T>), + Ongoing(SpiSingleTransfer), /// The spim transfer misbehaved, errored or panicked in a way that it cannot be completed. Inconsistent, - /// The spim is done, and a new transfer can be started. - Done, } /// An internal structure corresponding to a single in-progress EasyDMA transfer #[derive(Debug)] -struct SpiSingleTransfer<'a, T> { - spim: &'a mut T, +struct SpiSingleTransfer { tx_len: u32, rx_len: u32, } @@ -218,7 +222,7 @@ where fn do_spi_dma_transfer(&mut self, tx: DmaSlice, rx: DmaSlice) -> Result<(), Error> { let mut transfer = SpiSingleTransfer::new(&mut self.0, tx, rx); - while !transfer.poll_complete()? { + while !transfer.poll_complete(&mut self.0)? { core::sync::atomic::spin_loop_hint(); } @@ -238,6 +242,16 @@ where self.transfer_split_uneven(chip_select, tx_buffer, rx_buffer) } + /// Creates a new SPI transaction. CS will be held low during the entire transaction, allowing + /// you to perform several operations in the meantime. This also gives access to async polling + /// versions of the API. + pub fn transaction<'a>( + &'a mut self, + chip_select: &'a mut Pin>, + ) -> SpiTransaction<'a, T> { + SpiTransaction::new(&mut self.0, chip_select) + } + /// Read and write from a SPI slave, using a single buffer /// /// This method implements a complete read transaction, which consists of @@ -251,7 +265,8 @@ where chip_select: &mut Pin>, buffer: &mut [u8], ) -> Result<(), Error> { - let mut transfer = SpiTransfer::new(&mut self.0, chip_select, buffer)?; + let mut tx = self.transaction(chip_select); + let mut transfer = tx.transfer_polling(buffer)?; while !transfer.poll_complete()? { core::sync::atomic::spin_loop_hint(); @@ -260,27 +275,6 @@ where Ok(()) } - /// Read and write from a SPI slave, using a single buffer. - /// - /// This is an async polling version of `transfer()`. You need to call - /// `.poll_complete()` on the returned object until it returns `true`. A - /// good time to do that would be after receiving a SPI interrupt, for - /// example. - /// - /// This method implements a complete read transaction, which consists of - /// the master transmitting what it wishes to read, and the slave responding - /// with the requested data. - /// - /// Uses the provided chip select pin to initiate the transaction. Transmits - /// all bytes in `buffer`, then receives an equal number of bytes. - pub fn transfer_polling<'a>( - &'a mut self, - chip_select: &'a mut Pin>, - buffer: &'a mut [u8], - ) -> Result, Error> { - SpiTransfer::new(&mut self.0, chip_select, buffer) - } - /// Read and write from a SPI slave, using separate read and write buffers /// /// This method implements a complete read transaction, which consists of @@ -298,7 +292,8 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - let mut transfer = SpiEvenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer)?; + let mut tx = self.transaction(chip_select); + let mut transfer = tx.transfer_split_even_polling(tx_buffer, rx_buffer)?; while !transfer.poll_complete()? { core::sync::atomic::spin_loop_hint(); @@ -307,31 +302,6 @@ where Ok(()) } - /// Read and write from a SPI slave, using separate read and write buffers - /// - /// This is an async polling version of `transfer_split_even()`. You need to - /// call `.poll_complete()` on the returned object until it returns `true`. - /// A good time to do that would be after receiving a SPI interrupt, for - /// example. - /// - /// This method implements a complete read transaction, which consists of - /// the master transmitting what it wishes to read, and the slave responding - /// with the requested data. - /// - /// Uses the provided chip select pin to initiate the transaction. Transmits - /// all bytes in `tx_buffer`, then receives bytes until `rx_buffer` is full. - /// - /// If `tx_buffer.len() != rx_buffer.len()`, the transaction will stop at the - /// smaller of either buffer. - pub fn transfer_split_even_polling<'a>( - &'a mut self, - chip_select: &'a mut Pin>, - tx_buffer: &'a [u8], - rx_buffer: &'a mut [u8], - ) -> Result, Error> { - SpiEvenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer) - } - /// Read and write from a SPI slave, using separate read and write buffers /// /// This method implements a complete read transaction, which consists of @@ -351,7 +321,8 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - let mut transfer = SpiUnevenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer)?; + let mut tx = self.transaction(chip_select); + let mut transfer = tx.transfer_split_uneven_polling(tx_buffer, rx_buffer)?; while !transfer.poll_complete()? { core::sync::atomic::spin_loop_hint(); @@ -360,6 +331,78 @@ where Ok(()) } + /// Write to an SPI slave + /// + /// This method uses the provided chip select pin to initiate the + /// transaction, then transmits all bytes in `tx_buffer`. All incoming + /// bytes are discarded. + pub fn write( + &mut self, + chip_select: &mut Pin>, + tx_buffer: &[u8], + ) -> Result<(), Error> { + slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; + self.transfer_split_uneven(chip_select, tx_buffer, &mut [0u8; 0]) + } + + /// Return the raw interface to the underlying SPIM peripheral + pub fn free(self) -> T { + self.0 + } +} + +impl<'a, T> SpiTransaction<'a, T> +where + T: Instance, +{ + fn new(spim: &'a mut T, chip_select: &'a mut Pin>) -> Self { + Self { spim, chip_select } + } + + /// Read and write from a SPI slave, using a single buffer. + /// + /// This is an async polling version of `transfer()`. You need to call + /// `.poll_complete()` on the returned object until it returns `true`. A + /// good time to do that would be after receiving a SPI interrupt, for + /// example. + /// + /// This method implements a complete read transaction, which consists of + /// the master transmitting what it wishes to read, and the slave responding + /// with the requested data. + /// + /// Uses the provided chip select pin to initiate the transaction. Transmits + /// all bytes in `buffer`, then receives an equal number of bytes. + pub fn transfer_polling<'b>( + &'b mut self, + buffer: &'b mut [u8], + ) -> Result, Error> { + SpiTransfer::new(self.spim, buffer) + } + + /// Read and write from a SPI slave, using separate read and write buffers + /// + /// This is an async polling version of `transfer_split_even()`. You need to + /// call `.poll_complete()` on the returned object until it returns `true`. + /// A good time to do that would be after receiving a SPI interrupt, for + /// example. + /// + /// This method implements a complete read transaction, which consists of + /// the master transmitting what it wishes to read, and the slave responding + /// with the requested data. + /// + /// Uses the provided chip select pin to initiate the transaction. Transmits + /// all bytes in `tx_buffer`, then receives bytes until `rx_buffer` is full. + /// + /// If `tx_buffer.len() != rx_buffer.len()`, the transaction will stop at the + /// smaller of either buffer. + pub fn transfer_split_even_polling<'b>( + &'b mut self, + tx_buffer: &'b [u8], + rx_buffer: &'b mut [u8], + ) -> Result, Error> { + SpiEvenTransfer::new(self.spim, tx_buffer, rx_buffer) + } + /// Read and write from a SPI slave, using separate read and write buffers /// /// This is an async polling version of `transfer_split_uneven()`. You need to @@ -378,32 +421,18 @@ where /// it is allowed to perform transactions where `tx_buffer.len() != rx_buffer.len()`. /// If this occurs, extra incoming bytes will be discarded, OR extra outgoing bytes /// will be filled with the `orc` value. - pub fn transfer_split_uneven_polling<'a>( - &'a mut self, - chip_select: &'a mut Pin>, - tx_buffer: &'a [u8], - rx_buffer: &'a mut [u8], - ) -> Result, Error> { - SpiUnevenTransfer::new(&mut self.0, chip_select, tx_buffer, rx_buffer) - } - - /// Write to an SPI slave - /// - /// This method uses the provided chip select pin to initiate the - /// transaction, then transmits all bytes in `tx_buffer`. All incoming - /// bytes are discarded. - pub fn write( - &mut self, - chip_select: &mut Pin>, - tx_buffer: &[u8], - ) -> Result<(), Error> { - slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; - self.transfer_split_uneven(chip_select, tx_buffer, &mut [0u8; 0]) + pub fn transfer_split_uneven_polling<'b>( + &'b mut self, + tx_buffer: &'b [u8], + rx_buffer: &'b mut [u8], + ) -> Result, Error> { + SpiUnevenTransfer::new(self.spim, tx_buffer, rx_buffer) } +} - /// Return the raw interface to the underlying SPIM peripheral - pub fn free(self) -> T { - self.0 +impl<'a, T> Drop for SpiTransaction<'a, T> { + fn drop(&mut self) { + self.chip_select.set_high().unwrap(); } } @@ -411,29 +440,23 @@ impl<'a, T> SpiTransfer<'a, T> where T: Instance, { - pub fn new( - spim: &'a mut T, - chip_select: &'a mut Pin>, - buffer: &'a mut [u8], - ) -> Result { + fn new(spim: &'a mut T, buffer: &'a mut [u8]) -> Result { slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; let chunks = buffer.chunks_mut(EASY_DMA_SIZE); - chip_select.set_low().unwrap(); - - let state = TransferState::new(spim); + let state = TransferState::new(); Ok(Self { + spim, state, - chip_select, chunks, }) } pub fn poll_complete(&mut self) -> Result { let chunks = &mut self.chunks; - self.state.advance(|| { + self.state.advance(self.spim, || { chunks .next() .map(|chunk| (DmaSlice::from_slice(chunk), DmaSlice::from_slice(chunk))) @@ -441,22 +464,11 @@ where } } -impl<'a, T> Drop for SpiTransfer<'a, T> { - fn drop(&mut self) { - self.chip_select.set_high().unwrap(); - } -} - impl<'a, T> SpiEvenTransfer<'a, T> where T: Instance, { - pub fn new( - spim: &'a mut T, - chip_select: &'a mut Pin>, - tx_buffer: &'a [u8], - rx_buffer: &'a mut [u8], - ) -> Result { + fn new(spim: &'a mut T, tx_buffer: &'a [u8], rx_buffer: &'a mut [u8]) -> Result { // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable // slice can only be built from data located in RAM slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; @@ -465,20 +477,18 @@ where let rxi = rx_buffer.chunks_mut(EASY_DMA_SIZE); let chunks = txi.zip(rxi); - chip_select.set_low().unwrap(); - - let state = TransferState::new(spim); + let state = TransferState::new(); Ok(Self { + spim, state, - chip_select, chunks, }) } pub fn poll_complete(&mut self) -> Result { let chunks = &mut self.chunks; - self.state.advance(|| { + self.state.advance(self.spim, || { chunks .next() .map(|(tx, rx)| (DmaSlice::from_slice(tx), DmaSlice::from_slice(rx))) @@ -486,22 +496,11 @@ where } } -impl<'a, T> Drop for SpiEvenTransfer<'a, T> { - fn drop(&mut self) { - self.chip_select.set_high().unwrap(); - } -} - impl<'a, T> SpiUnevenTransfer<'a, T> where T: Instance, { - pub fn new( - spim: &'a mut T, - chip_select: &'a mut Pin>, - tx_buffer: &'a [u8], - rx_buffer: &'a mut [u8], - ) -> Result { + fn new(spim: &'a mut T, tx_buffer: &'a [u8], rx_buffer: &'a mut [u8]) -> Result { // NOTE: RAM slice check for `rx_buffer` is not necessary, as a mutable // slice can only be built from data located in RAM slice_in_ram_or(tx_buffer, Error::DMABufferNotInDataMemory)?; @@ -509,13 +508,11 @@ where let chunks_tx = tx_buffer.chunks(EASY_DMA_SIZE); let chunks_rx = rx_buffer.chunks_mut(EASY_DMA_SIZE); - chip_select.set_low().unwrap(); - - let state = TransferState::new(spim); + let state = TransferState::new(); Ok(Self { + spim, state, - chip_select, chunks_tx, chunks_rx, }) @@ -525,7 +522,7 @@ where let chunks_tx = &mut self.chunks_tx; let chunks_rx = &mut self.chunks_rx; self.state - .advance(|| match (chunks_tx.next(), chunks_rx.next()) { + .advance(self.spim, || match (chunks_tx.next(), chunks_rx.next()) { (None, None) => None, (tx, rx) => Some(( tx.map(|tx| DmaSlice::from_slice(tx)) @@ -537,27 +534,22 @@ where } } -impl<'a, T> Drop for SpiUnevenTransfer<'a, T> { - fn drop(&mut self) { - self.chip_select.set_high().unwrap(); +impl TransferState { + fn new() -> Self { + TransferState::Done } -} -impl<'a, T> TransferState<'a, T> -where - T: Instance, -{ - fn new(spim: &'a mut T) -> Self { - TransferState::Idle(spim) - } - - fn advance( + fn advance( &mut self, + spim: &mut T, mut next_chunk: impl FnMut() -> Option<(DmaSlice, DmaSlice)>, - ) -> Result { + ) -> Result + where + T: Instance, + { loop { match core::mem::replace(self, TransferState::Inconsistent) { - TransferState::Idle(spim) => match next_chunk() { + TransferState::Done => match next_chunk() { Some((tx, rx)) => { let transfer = SpiSingleTransfer::new(spim, tx, rx); *self = TransferState::Ongoing(transfer); @@ -566,28 +558,24 @@ where None => *self = TransferState::Done, }, TransferState::Ongoing(mut transfer) => { - if transfer.poll_complete()? { - *self = TransferState::Idle(transfer.spim) + if transfer.poll_complete(spim)? { + *self = TransferState::Done; } else { *self = TransferState::Ongoing(transfer); return Ok(false); } } - TransferState::Done => { - *self = TransferState::Done; - return Ok(true); - } TransferState::Inconsistent => return Err(Error::InconsistentState), } } } } -impl<'a, T> SpiSingleTransfer<'a, T> -where - T: Instance, -{ - fn new(spim: &'a mut T, tx: DmaSlice, rx: DmaSlice) -> Self { +impl SpiSingleTransfer { + fn new(spim: &mut T, tx: DmaSlice, rx: DmaSlice) -> Self + where + T: Instance, + { // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // before any DMA action has started @@ -626,32 +614,31 @@ where let tx_len = tx.len; let rx_len = rx.len; - SpiSingleTransfer { - spim, - tx_len, - rx_len, - } + SpiSingleTransfer { tx_len, rx_len } } - fn poll_complete(&mut self) -> Result { + fn poll_complete(&mut self, spim: &mut T) -> Result + where + T: Instance, + { // Check for END event // // This event is triggered once both transmitting and receiving are // done. - if self.spim.events_end.read().bits() == 0 { + if spim.events_end.read().bits() == 0 { // Reset the event, otherwise it will always read `1` from now on. - self.spim.events_end.write(|w| w); + spim.events_end.write(|w| w); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // after all possible DMA actions have completed compiler_fence(SeqCst); - if self.spim.txd.amount.read().bits() != self.tx_len { + if spim.txd.amount.read().bits() != self.tx_len { return Err(Error::Transmit); } - if self.spim.rxd.amount.read().bits() != self.rx_len { + if spim.rxd.amount.read().bits() != self.rx_len { return Err(Error::Receive); } From 6d39b970f9b1bf9a8a6b809bc5cbd1a349505750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 15 Feb 2020 10:54:36 +0100 Subject: [PATCH 03/12] Tweak error description Co-Authored-By: Jonas Schievink --- nrf52-hal-common/src/spim.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index e2b09817..f11eefd9 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -671,7 +671,7 @@ pub enum Error { DMABufferNotInDataMemory, Transmit, Receive, - /// The peripheral is in an inconsistent state, because of encountering an error mid-transfer. + /// The peripheral is in an inconsistent state, because it encountered an error mid-transfer. InconsistentState, } From 3c4bd54cf8c48ba3b395d1a59e17f3b656bd8a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 15 Feb 2020 11:26:11 +0100 Subject: [PATCH 04/12] Simplify slice/iter imports --- nrf52-hal-common/src/spim.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index f11eefd9..233b4f29 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -1,7 +1,9 @@ //! HAL interface to the SPIM peripheral //! //! See product specification, chapter 31. +use core::iter; use core::ops::Deref; +use core::slice; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; #[cfg(feature = "9160")] @@ -46,7 +48,7 @@ pub struct SpiTransaction<'a, T> { pub struct SpiTransfer<'a, T> { spim: &'a mut T, state: TransferState, - chunks: core::slice::ChunksMut<'a, u8>, + chunks: slice::ChunksMut<'a, u8>, } /// An ongoing transfer that was initiated by a call to `.transfer_even_polling()`. @@ -58,7 +60,7 @@ pub struct SpiTransfer<'a, T> { pub struct SpiEvenTransfer<'a, T> { spim: &'a mut T, state: TransferState, - chunks: core::iter::Zip, core::slice::ChunksMut<'a, u8>>, + chunks: iter::Zip, slice::ChunksMut<'a, u8>>, } /// An ongoing transfer that was initiated by a call to `.transfer_uneven_polling()`. @@ -70,8 +72,8 @@ pub struct SpiEvenTransfer<'a, T> { pub struct SpiUnevenTransfer<'a, T> { spim: &'a mut T, state: TransferState, - chunks_tx: core::slice::Chunks<'a, u8>, - chunks_rx: core::slice::ChunksMut<'a, u8>, + chunks_tx: slice::Chunks<'a, u8>, + chunks_rx: slice::ChunksMut<'a, u8>, } /// The state of a more advanced transfer. From 76a16b358e05cdef070e530b620acd2d3bf63230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 15 Feb 2020 11:26:28 +0100 Subject: [PATCH 05/12] Import core::mem --- nrf52-hal-common/src/spim.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index 233b4f29..f80b3fdb 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -2,6 +2,7 @@ //! //! See product specification, chapter 31. use core::iter; +use core::mem; use core::ops::Deref; use core::slice; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; @@ -550,7 +551,7 @@ impl TransferState { T: Instance, { loop { - match core::mem::replace(self, TransferState::Inconsistent) { + match mem::replace(self, TransferState::Inconsistent) { TransferState::Done => match next_chunk() { Some((tx, rx)) => { let transfer = SpiSingleTransfer::new(spim, tx, rx); From 1f44af8627e696c39f89da2455939184cba81695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 15 Feb 2020 11:26:48 +0100 Subject: [PATCH 06/12] Import spin_loop_hint --- nrf52-hal-common/src/spim.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index f80b3fdb..97b7a636 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -5,7 +5,7 @@ use core::iter; use core::mem; use core::ops::Deref; use core::slice; -use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; +use core::sync::atomic::{compiler_fence, spin_loop_hint, Ordering::SeqCst}; #[cfg(feature = "9160")] use crate::target::{spim0_ns as spim0, SPIM0_NS as SPIM0}; @@ -226,7 +226,7 @@ where let mut transfer = SpiSingleTransfer::new(&mut self.0, tx, rx); while !transfer.poll_complete(&mut self.0)? { - core::sync::atomic::spin_loop_hint(); + spin_loop_hint(); } Ok(()) @@ -272,7 +272,7 @@ where let mut transfer = tx.transfer_polling(buffer)?; while !transfer.poll_complete()? { - core::sync::atomic::spin_loop_hint(); + spin_loop_hint(); } Ok(()) @@ -299,7 +299,7 @@ where let mut transfer = tx.transfer_split_even_polling(tx_buffer, rx_buffer)?; while !transfer.poll_complete()? { - core::sync::atomic::spin_loop_hint(); + spin_loop_hint(); } Ok(()) @@ -328,7 +328,7 @@ where let mut transfer = tx.transfer_split_uneven_polling(tx_buffer, rx_buffer)?; while !transfer.poll_complete()? { - core::sync::atomic::spin_loop_hint(); + spin_loop_hint(); } Ok(()) From 8c0bd1f4a13b85da89348edc0eb32740305e0c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Sat, 15 Feb 2020 11:26:59 +0100 Subject: [PATCH 07/12] Remember to set CS line low at the start of a transaction --- nrf52-hal-common/src/spim.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index 97b7a636..ce3a1793 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -359,6 +359,7 @@ where T: Instance, { fn new(spim: &'a mut T, chip_select: &'a mut Pin>) -> Self { + chip_select.set_low().unwrap(); Self { spim, chip_select } } From 3807ba7178306b6c6b1f8f8ea33d6d504db964ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Fri, 21 Feb 2020 21:16:54 +0100 Subject: [PATCH 08/12] Fix method references in doc comments --- nrf52-hal-common/src/spim.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index ce3a1793..f6166458 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -40,7 +40,7 @@ pub struct SpiTransaction<'a, T> { spim: &'a mut T, } -/// An ongoing transfer that was initiated by a call to `.transfer_polling()`. +/// An ongoing transfer that was initiated by a call to `SpiTransaction::transfer_polling()`. /// /// This transfer must be polled to completion. Failing to poll it until completed might leave the /// peripheral in an inconsistent state. @@ -52,7 +52,8 @@ pub struct SpiTransfer<'a, T> { chunks: slice::ChunksMut<'a, u8>, } -/// An ongoing transfer that was initiated by a call to `.transfer_even_polling()`. +/// An ongoing transfer that was initiated by a call to +/// `SpiTransaction::transfer_split_even_polling()`. /// /// This transfer must be polled to completion. Failing to poll it until completed might leave the /// peripheral in an inconsistent state. @@ -64,7 +65,8 @@ pub struct SpiEvenTransfer<'a, T> { chunks: iter::Zip, slice::ChunksMut<'a, u8>>, } -/// An ongoing transfer that was initiated by a call to `.transfer_uneven_polling()`. +/// An ongoing transfer that was initiated by a call to +/// `SpiTransaction::transfer_split_uneven_polling()`. /// /// This transfer must be polled to completion. Failing to poll it until completed might leave the /// peripheral in an inconsistent state. From 85fb403e90931771d6df4fe320169acacc4dde9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Fri, 21 Feb 2020 21:18:10 +0100 Subject: [PATCH 09/12] Extract block_until_complete methods for transfer types --- nrf52-hal-common/src/spim.rs | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index f6166458..776e6821 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -270,14 +270,9 @@ where chip_select: &mut Pin>, buffer: &mut [u8], ) -> Result<(), Error> { - let mut tx = self.transaction(chip_select); - let mut transfer = tx.transfer_polling(buffer)?; - - while !transfer.poll_complete()? { - spin_loop_hint(); - } - - Ok(()) + self.transaction(chip_select) + .transfer_polling(buffer)? + .block_until_complete() } /// Read and write from a SPI slave, using separate read and write buffers @@ -297,14 +292,9 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - let mut tx = self.transaction(chip_select); - let mut transfer = tx.transfer_split_even_polling(tx_buffer, rx_buffer)?; - - while !transfer.poll_complete()? { - spin_loop_hint(); - } - - Ok(()) + self.transaction(chip_select) + .transfer_split_even_polling(tx_buffer, rx_buffer)? + .block_until_complete() } /// Read and write from a SPI slave, using separate read and write buffers @@ -326,14 +316,9 @@ where tx_buffer: &[u8], rx_buffer: &mut [u8], ) -> Result<(), Error> { - let mut tx = self.transaction(chip_select); - let mut transfer = tx.transfer_split_uneven_polling(tx_buffer, rx_buffer)?; - - while !transfer.poll_complete()? { - spin_loop_hint(); - } - - Ok(()) + self.transaction(chip_select) + .transfer_split_uneven_polling(tx_buffer, rx_buffer)? + .block_until_complete() } /// Write to an SPI slave @@ -460,6 +445,11 @@ where }) } + pub fn block_until_complete(&mut self) -> Result<(), Error> { + while !self.poll_complete()? {} + Ok(()) + } + pub fn poll_complete(&mut self) -> Result { let chunks = &mut self.chunks; self.state.advance(self.spim, || { @@ -492,6 +482,11 @@ where }) } + pub fn block_until_complete(&mut self) -> Result<(), Error> { + while !self.poll_complete()? {} + Ok(()) + } + pub fn poll_complete(&mut self) -> Result { let chunks = &mut self.chunks; self.state.advance(self.spim, || { @@ -524,6 +519,11 @@ where }) } + pub fn block_until_complete(&mut self) -> Result<(), Error> { + while !self.poll_complete()? {} + Ok(()) + } + pub fn poll_complete(&mut self) -> Result { let chunks_tx = &mut self.chunks_tx; let chunks_rx = &mut self.chunks_rx; From ef708548314487f2f72b0f3508d0233ae5b0d60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Fri, 21 Feb 2020 21:21:40 +0100 Subject: [PATCH 10/12] Remove spin_loop_hints --- nrf52-hal-common/src/spim.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index 776e6821..9dd8064f 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -5,7 +5,7 @@ use core::iter; use core::mem; use core::ops::Deref; use core::slice; -use core::sync::atomic::{compiler_fence, spin_loop_hint, Ordering::SeqCst}; +use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; #[cfg(feature = "9160")] use crate::target::{spim0_ns as spim0, SPIM0_NS as SPIM0}; @@ -227,9 +227,7 @@ where fn do_spi_dma_transfer(&mut self, tx: DmaSlice, rx: DmaSlice) -> Result<(), Error> { let mut transfer = SpiSingleTransfer::new(&mut self.0, tx, rx); - while !transfer.poll_complete(&mut self.0)? { - spin_loop_hint(); - } + while !transfer.poll_complete(&mut self.0)? {} Ok(()) } From 37511f092fa43def618895f8087592ff467c2be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Fri, 21 Feb 2020 21:22:03 +0100 Subject: [PATCH 11/12] Update doc comments to mirror the new code structure --- nrf52-hal-common/src/spim.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index 9dd8064f..a94ef4a4 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -307,7 +307,7 @@ where /// This method is more complicated than the other `transfer` methods because /// it is allowed to perform transactions where `tx_buffer.len() != rx_buffer.len()`. /// If this occurs, extra incoming bytes will be discarded, OR extra outgoing bytes - /// will be filled with the `orc` value. + /// will be filled with the `orc` value supplied in `new()`. pub fn transfer_split_uneven( &mut self, chip_select: &mut Pin>, @@ -350,10 +350,10 @@ where /// Read and write from a SPI slave, using a single buffer. /// - /// This is an async polling version of `transfer()`. You need to call - /// `.poll_complete()` on the returned object until it returns `true`. A - /// good time to do that would be after receiving a SPI interrupt, for - /// example. + /// This is an async polling version of `Spim::transfer()`. You need to call + /// `.poll_complete()` or `.block_until_complete()` on the returned object + /// until it returns `true`. A good time to do that would be after receiving a SPI + /// interrupt, for example. /// /// This method implements a complete read transaction, which consists of /// the master transmitting what it wishes to read, and the slave responding @@ -370,10 +370,10 @@ where /// Read and write from a SPI slave, using separate read and write buffers /// - /// This is an async polling version of `transfer_split_even()`. You need to - /// call `.poll_complete()` on the returned object until it returns `true`. - /// A good time to do that would be after receiving a SPI interrupt, for - /// example. + /// This is an async polling version of `Spim::transfer_split_even()`. You need to + /// call `.poll_complete()` or `.block_until_complete()` on the returned object + /// until it returns `true`. A good time to do that would be after receiving a SPI + /// interrupt, for example. /// /// This method implements a complete read transaction, which consists of /// the master transmitting what it wishes to read, and the slave responding @@ -394,10 +394,10 @@ where /// Read and write from a SPI slave, using separate read and write buffers /// - /// This is an async polling version of `transfer_split_uneven()`. You need to - /// call `.poll_complete()` on the returned object until it returns `true`. - /// A good time to do that would be after receiving a SPI interrupt, for - /// example. + /// This is an async polling version of `Spim::transfer_split_uneven()`. You need to + /// call `.poll_complete()` or `.block_until_complete()` on the returned object + /// until it returns `true`. A good time to do that would be after receiving a SPI + /// interrupt, for example. /// /// This method implements a complete read transaction, which consists of /// the master transmitting what it wishes to read, and the slave responding @@ -409,7 +409,7 @@ where /// This method is more complicated than the other `transfer` methods because /// it is allowed to perform transactions where `tx_buffer.len() != rx_buffer.len()`. /// If this occurs, extra incoming bytes will be discarded, OR extra outgoing bytes - /// will be filled with the `orc` value. + /// will be filled with the `orc` value supplied in `Spim::new()`. pub fn transfer_split_uneven_polling<'b>( &'b mut self, tx_buffer: &'b [u8], From d196441fb459029c0e52c6079ded2c08bf63933c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Fri, 21 Feb 2020 21:41:38 +0100 Subject: [PATCH 12/12] Make block_until_complete consume self --- nrf52-hal-common/src/spim.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nrf52-hal-common/src/spim.rs b/nrf52-hal-common/src/spim.rs index a94ef4a4..c61c1a73 100644 --- a/nrf52-hal-common/src/spim.rs +++ b/nrf52-hal-common/src/spim.rs @@ -443,7 +443,7 @@ where }) } - pub fn block_until_complete(&mut self) -> Result<(), Error> { + pub fn block_until_complete(mut self) -> Result<(), Error> { while !self.poll_complete()? {} Ok(()) } @@ -480,7 +480,7 @@ where }) } - pub fn block_until_complete(&mut self) -> Result<(), Error> { + pub fn block_until_complete(mut self) -> Result<(), Error> { while !self.poll_complete()? {} Ok(()) } @@ -517,7 +517,7 @@ where }) } - pub fn block_until_complete(&mut self) -> Result<(), Error> { + pub fn block_until_complete(mut self) -> Result<(), Error> { while !self.poll_complete()? {} Ok(()) }