diff --git a/src/lib.rs b/src/lib.rs index f3fee473b..42093d0b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -403,7 +403,6 @@ //! //! # fn main() {} //! ``` - #![deny(missing_docs)] #![no_std] diff --git a/src/spi/blocking.rs b/src/spi/blocking.rs index 756cc4f1d..35fef528f 100644 --- a/src/spi/blocking.rs +++ b/src/spi/blocking.rs @@ -15,6 +15,7 @@ pub trait Transfer: ErrorType { fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; } +#[cfg(conflicting)] impl, Word: Copy> Transfer for &mut T { fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) @@ -29,6 +30,7 @@ pub trait TransferInplace: ErrorType { fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; } +#[cfg(conflicting)] impl, Word: Copy> TransferInplace for &mut T { fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_inplace(self, words) @@ -44,6 +46,7 @@ pub trait Read: ErrorType { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; } +#[cfg(conflicting)] impl, Word: Copy> Read for &mut T { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) @@ -56,6 +59,7 @@ pub trait Write: ErrorType { fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; } +#[cfg(conflicting)] impl, Word: Copy> Write for &mut T { fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words) @@ -101,8 +105,241 @@ pub trait Transactional: ErrorType { fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error>; } +#[cfg(conflicting)] impl, Word: 'static + Copy> Transactional for &mut T { fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error> { T::exec(self, operations) } } + +/// SPI Managed CS trait +/// +/// This uses a bunch of magic to manage CS for all SPI methods, and conflicts with the `&mut T` impls for each trait. +/// +/// ``` +/// use embedded_hal::spi::blocking::{ManagedCs, Write}; +/// +/// // Automatic CS assertion +/// fn spi_write_auto_cs(spi: &mut SPI) { +/// let _ = spi.write(&[0xaa, 0xbb, 0xcc]); +/// } +/// // Manual CS assertion +/// fn spi_write_manual_cs, P: Write>(spi: &mut SPI) { +/// let _ = spi.with_cs(|d|{ +/// let _ = d.write(&[0xaa, 0xbb, 0xcc]); +/// Ok(()) +/// }); +/// } +/// ``` +pub trait ManagedCs: ErrorType { + /// Inner SPI type + type Inner: ErrorType; + + /// Execute the provided closure within a CS assertion + fn with_cs( + &mut self, + f: impl FnMut(&mut Self::Inner) -> Result, + ) -> Result; +} + +/// Alternate managed CS trait +/// +/// This one doesn't require any defaults / provide any magic, if you want +/// to do things within a CS assertion you call `with_cs`. +/// +/// ``` +/// use embedded_hal::spi::blocking::{ManagedCsAlt, Write}; +/// +/// // Manual CS assertion +/// fn spi_write_manual_cs(spi: &mut SPI) { +/// let _ = spi.with_cs(|d|{ +/// let _ = d.write(&[0xaa, 0xbb, 0xcc]); +/// Ok(()) +/// }); +/// } +/// ``` +pub trait ManagedCsAlt: ErrorType { + /// Execute the provided closure within a CS assertion + fn with_cs( + &mut self, + f: impl FnMut(&mut Self) -> Result, + ) -> Result; +} + +/// These defaults conflict with the &mut impls, we could just not have them / require folks to always call [`ManagedCs::with_cs`]? +mod defaults { + use super::*; + + /// Default blocking [`Transfer`] with CS management + impl Transfer for T + where + T: ManagedCs + ErrorType, + I: Transfer + ErrorType, + Word: Copy + 'static, + { + fn transfer<'a>(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + self.with_cs(|i: &mut I| i.transfer(read, write)) + } + } + + /// Default blocking [`TransferInplace`] with CS management + impl TransferInplace for T + where + T: ManagedCs + ErrorType, + I: TransferInplace + ErrorType, + Word: Copy + 'static, + { + fn transfer_inplace<'a>(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.with_cs(|i: &mut I| i.transfer_inplace(words)) + } + } + + /// Default blocking [`Read`] with CS management + impl Read for T + where + T: ManagedCs + ErrorType, + I: Read + ErrorType, + Word: Copy + 'static, + { + fn read<'a>(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.with_cs(|i: &mut I| i.read(words)) + } + } + + /// Default blocking [`Write`] with CS management + impl Write for T + where + T: ManagedCs + ErrorType, + I: Write + ErrorType, + Word: Copy + 'static, + { + fn write<'a>(&mut self, words: &[Word]) -> Result<(), Self::Error> { + self.with_cs(|i: &mut I| i.write(words)) + } + } + + /// Default blocking [`Transactional`] with CS management + impl Transactional for T + where + T: ManagedCs + ErrorType, + I: Transactional + ErrorType, + Word: Copy + 'static, + { + fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error> { + self.with_cs(|i: &mut I| i.exec(operations)) + } + } +} + +/// [`SpiWithCs`] wraps an SPI implementation with Chip Select (CS) +/// pin management for exclusive (non-shared) use. +/// For sharing SPI between peripherals, see [shared-bus](https://crates.io/crates/shared-bus) +pub struct SpiWithCs { + spi: Spi, + cs: Pin, +} + +/// Wrapper for errors returned by [`SpiWithCs`] +#[derive(Clone, Debug, PartialEq)] +pub enum SpiWithCsError { + /// Underlying SPI communication error + Spi(SpiError), + /// Underlying chip-select pin state setting error + Pin(PinError), +} + +/// [`ErrorType`] implementation for [`SpiWithCs`] wrapper +impl ErrorType for SpiWithCs +where + Spi: ErrorType, + Pin: crate::digital::blocking::OutputPin + crate::digital::ErrorType, +{ + type Error = + SpiWithCsError<::Error, ::Error>; +} + +impl SpiWithCs +where + Spi: ErrorType, + Pin: crate::digital::blocking::OutputPin + crate::digital::ErrorType, +{ + /// Create a new SpiWithCs wrapper using the provided SPI peripheral and CS pin + pub fn new(spi: Spi, cs: Pin) -> Self { + Self { spi, cs } + } + + /// Fetch a reference to the inner SPI object without manipulating CS + pub fn inner(&mut self) -> &mut Spi { + &mut self.spi + } +} + +/// [`ManagedCs`] implementation for [`SpiWithCs`] wrapper. +/// Provides `with_cs` function that asserts and deasserts CS +impl ManagedCs for SpiWithCs +where + Spi: ErrorType, + Pin: crate::digital::blocking::OutputPin + crate::digital::ErrorType, +{ + type Inner = Spi; + + /// Executes the provided closure within a CS assertion + fn with_cs( + &mut self, + mut f: impl FnMut(&mut Self::Inner) -> Result, + ) -> Result { + self.cs.set_low().map_err(SpiWithCsError::Pin)?; + + let r = f(&mut self.spi); + + self.cs.set_high().map_err(SpiWithCsError::Pin)?; + + r + } +} + +impl ManagedCs for &mut T { + type Inner = T::Inner; + + /// Executes the provided closure within a CS assertion + fn with_cs( + &mut self, + f: impl FnMut(&mut Self::Inner) -> Result, + ) -> Result { + T::with_cs(self, f) + } +} + +/// [`ManagedCs`] implementation for [`SpiWithCs`] wrapper. +/// Provides `with_cs` function that asserts and deasserts CS +impl ManagedCsAlt for SpiWithCs +where + Spi: ErrorType, + Pin: crate::digital::blocking::OutputPin + crate::digital::ErrorType, +{ + /// Executes the provided closure within a CS assertion + fn with_cs( + &mut self, + mut f: impl FnMut(&mut Self) -> Result, + ) -> Result { + self.cs.set_low().map_err(SpiWithCsError::Pin)?; + + let r = f(self); + + self.cs.set_high().map_err(SpiWithCsError::Pin)?; + + r + } +} + +/// [`super::Error`] implementation for [`SpiWithCsError`] +impl super::Error + for SpiWithCsError +{ + fn kind(&self) -> super::ErrorKind { + match self { + SpiWithCsError::Spi(spi) => spi.kind(), + SpiWithCsError::Pin(_pin) => super::ErrorKind::Other, + } + } +}