diff --git a/examples/rtic_spi_blocking.rs b/examples/rtic_spi_blocking.rs index 85fffb23..147659ca 100644 --- a/examples/rtic_spi_blocking.rs +++ b/examples/rtic_spi_blocking.rs @@ -163,6 +163,22 @@ mod app { delay(); } + + { + use eh1::spi::{ + Operation::{Read, TransferInPlace}, + SpiDevice, + }; + + let mut read = [0u8; 7]; + let mut xfer = [0u8; 16]; + for idx in 0..xfer.len() { + xfer[idx] = idx as u8; + } + + spi.transaction(&mut [TransferInPlace(&mut xfer), Read(&mut read)]) + .unwrap(); + } } } } diff --git a/src/chip/dma.rs b/src/chip/dma.rs index 4fa17b68..063b8a00 100644 --- a/src/chip/dma.rs +++ b/src/chip/dma.rs @@ -141,6 +141,12 @@ impl
lpuart::Lpuart
{ // LPSPI use crate::lpspi; +impl
lpspi::Lpspi
{ + pub(crate) fn wait_for_transmit_fifo_space(&self) -> Result<(), lpspi::LpspiError> { + crate::spin_on(self.spin_for_fifo_space()) + } +} + unsafe impl
peripheral::Source {
fn source_signal(&self) -> u32 {
LPSPI_DMA_RX_MAPPING[N as usize - 1]
diff --git a/src/common/lpspi.rs b/src/common/lpspi.rs
index 93493943..74861d04 100644
--- a/src/common/lpspi.rs
+++ b/src/common/lpspi.rs
@@ -16,9 +16,9 @@
//! Blocking full-duplex transfers and writes will observe an asserted chip select while data
//! frames are exchanged / written.
//!
-//! This driver generally assumes that you're using the peripheral-controlled chip select. If
-//! you instead want to manage chip select in software, you should be able to multiplex your own
-//! pins, then construct the driver [`without_pins`](Lpspi::without_pins).
+//! If you instead want to manage chip select in software, you can use [`NoChipSelect`] as
+//! the CS pin. You can also construct the driver [`without_pins`](Lpspi::without_pins) and
+//! multiplex your own pins.
//!
//! # Device support
//!
@@ -75,15 +75,14 @@
//!
//! # Limitations
//!
-//! Due to [a hardware defect][1], this driver does not yet support the EH02 SPI transaction API.
-//! An early iteration of this driver reproduced the issue discussed in that forum. This driver may
-//! be able to work around the defect in software, but it hasn't been explored.
+//! The current implementation of the EH1 `SpiDevice` trait does not support the `DelayNs`
+//! operation. Implementing this was impossible while keeping backwards compatibility.
+//! This may change in a future release.
//!
-//! [1]: https://community.nxp.com/t5/i-MX-RT/RT1050-LPSPI-last-bit-not-completing-in-continuous-mode/m-p/898460
+//! If you need support for the `DelayNs` operation you can use one of the devices from
+//! [`embedded_hal_bus::spi`][1].
//!
-//! [`Transaction`] exposes the continuous / continuing flags, so you're free to model advanced
-//! transactions. However, keep in mind that disabling the receiver during a continuous transaction
-//! may not work as expected.
+//! [1]: https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/spi/index.html
use core::marker::PhantomData;
use core::task::Poll;
@@ -93,6 +92,13 @@ use crate::ral;
pub use eh02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
+mod private {
+ pub trait Sealed {}
+
+ impl Lpspi {
.await
}
- pub(crate) fn wait_for_transmit_fifo_space(&self) -> Result<(), LpspiError> {
- crate::spin_on(self.spin_for_fifo_space())
- }
-
/// Wait for receive data in a (concurrent) spin loop.
///
/// This future does not care about the RX FIFO watermark. Instead, it
@@ -788,55 +835,137 @@ impl Lpspi {
}
}
- fn exchange eh02::blocking::spi::Transfer {
+impl eh02::blocking::spi::Transfer
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u8]) -> Result<&'a [u8], Self::Error> {
- self.exchange(words)?;
+ eh1::spi::SpiDevice::transfer_in_place(self, words)?;
Ok(words)
}
}
-impl eh02::blocking::spi::Transfer {
+impl eh02::blocking::spi::Transfer
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u16]) -> Result<&'a [u16], Self::Error> {
- self.exchange(words)?;
+ eh1::spi::SpiDevice::transfer_in_place(self, words)?;
Ok(words)
}
}
-impl eh02::blocking::spi::Transfer {
+impl eh02::blocking::spi::Transfer
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u32]) -> Result<&'a [u32], Self::Error> {
- self.exchange(words)?;
+ eh1::spi::SpiDevice::transfer_in_place(self, words)?;
Ok(words)
}
}
-impl eh02::blocking::spi::Write {
+impl eh02::blocking::spi::Write
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
- self.write_no_read(words)
+ eh1::spi::SpiDevice::write(self, words)
}
}
-impl eh02::blocking::spi::Write {
+impl eh02::blocking::spi::Write
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> {
- self.write_no_read(words)
+ eh1::spi::SpiDevice::write(self, words)
}
}
-impl eh02::blocking::spi::Write {
+impl eh02::blocking::spi::Write
+where
+ P: HasChipSelect,
+{
type Error = LpspiError;
fn write(&mut self, words: &[u32]) -> Result<(), Self::Error> {
- self.write_no_read(words)
+ eh1::spi::SpiDevice::write(self, words)
+ }
+}
+
+impl eh02::blocking::spi::Transactional
+where
+ P: HasChipSelect,
+ W: Word + 'static,
+{
+ type Error = LpspiError;
+
+ fn exec(
+ &mut self,
+ operations: &mut [eh02::blocking::spi::Operation<'_, W>],
+ ) -> Result<(), Self::Error> {
+ use eh02::blocking::spi::Operation;
+
+ let operations_len = operations.len();
+
+ if operations_len == 0 {
+ return Ok(());
+ }
+
+ crate::spin_on(async {
+ let mut continuing = false;
+
+ for (i, op) in operations.iter_mut().enumerate() {
+ // For the last transaction `continuous` needs to be set to false.
+ // Otherwise the hardware fails to correctly de-assert CS.
+ let continuous = i + 1 != operations_len;
+ match op {
+ Operation::Write(data) => {
+ self.spin_write_no_read(continuous, continuing, data)
+ .await?
+ }
+ Operation::Transfer(data) => {
+ self.spin_transfer_in_place(continuous, continuing, data)
+ .await?;
+ }
+ }
+
+ continuing = true;
+ }
+
+ Ok(())
+ })
+ .map_err(|err| {
+ self.recover_from_error();
+ err
+ })?;
+
+ self.flush()
+ }
+}
+
+impl eh1::spi::Error for LpspiError {
+ fn kind(&self) -> eh1::spi::ErrorKind {
+ use eh1::spi::ErrorKind;
+
+ match self {
+ LpspiError::Fifo(Direction::Rx) => ErrorKind::Overrun,
+ _ => ErrorKind::Other,
+ }
+ }
+}
+
+impl eh1::spi::ErrorType for Lpspi {
+ type Error = LpspiError;
+}
+
+impl eh1::spi::SpiDevice
+where
+ W: Word + 'static,
+ P: HasChipSelect,
+{
+ fn transaction(
+ &mut self,
+ operations: &mut [eh1::spi::Operation<'_, W>],
+ ) -> Result<(), Self::Error> {
+ use eh1::spi::Operation;
+
+ let operations_len = operations.len();
+
+ if operations_len == 0 {
+ return Ok(());
+ }
+
+ crate::spin_on(async {
+ let mut continuing = false;
+
+ for (i, op) in operations.iter_mut().enumerate() {
+ // For the last transaction `continuous` needs to be set to false.
+ // Otherwise the hardware fails to correctly de-assert CS.
+ let continuous = i + 1 != operations_len;
+ match op {
+ Operation::Read(data) | Operation::Transfer(data, []) => {
+ self.spin_read_no_write(continuous, continuing, data)
+ .await?;
+ }
+ Operation::Write(data) | Operation::Transfer([], data) => {
+ self.spin_write_no_read(continuous, continuing, data)
+ .await?;
+ }
+ Operation::Transfer(read, write) => {
+ self.spin_transfer(continuous, continuing, read, write)
+ .await?;
+ }
+ Operation::TransferInPlace(data) => {
+ self.spin_transfer_in_place(continuous, continuing, data)
+ .await?;
+ }
+ Operation::DelayNs(_) => {
+ panic!(
+ "DelayNs not implemented, please use embedded-hal-bus if you need it."
+ );
+ }
+ }
+
+ continuing = true;
+ }
+
+ Ok(())
+ })
+ .map_err(|err| {
+ self.recover_from_error();
+ err
+ })?;
+
+ self.flush()
}
}
@@ -1463,7 +1774,6 @@ impl<'a, W> ReceiveBuffer<'a, W>
where
W: Word,
{
- #[cfg(test)] // TODO(mciantyre) remove once needed in non-test code.
fn new(buffer: &'a mut [W]) -> Self {
// Safety: pointer offset math meets expectations.
unsafe { Self::from_raw(buffer.as_mut_ptr(), buffer.len()) }