Skip to content

Add ManagedCS marker trait #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
implement these `Error` traits, which implies providing a conversion to a common
set of error kinds. Generic drivers using these interfaces can then convert the errors
to this common set to act upon them.
- `ManagedCS` trait for SPI, signals that CS is managed by the driver to allow
shared use of an SPI bus, as well as an `SpiWithCs` wrapper implementing this
over an Spi and Pin implementation.

### Removed
- Removed `DelayMs` in favor of `DelayUs` with `u32` as type for clarity.
Expand Down
149 changes: 144 additions & 5 deletions src/spi/blocking.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
//! Blocking SPI API
//!
//! In some cases it's possible to implement these blocking traits on top of one of the core HAL
//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided.
//! Implementing that marker trait will opt in your type into a blanket implementation.

/// Blocking transfer
pub trait Transfer<W> {
/// Error type
Expand Down Expand Up @@ -90,3 +85,147 @@ impl<T: Transactional<W>, W: 'static> Transactional<W> for &mut T {
T::exec(self, operations)
}
}

/// Provides SpiWithCS wrapper using for spi::* types combined with an OutputPin
pub use spi_with_cs::{SpiWithCs, SpiWithCsError};
mod spi_with_cs {

use core::fmt::Debug;

use super::{Transfer, Write, WriteIter};
use crate::digital::blocking::OutputPin;
use crate::spi::{ErrorKind, ManagedChipSelect};

/// [`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, Pin> {
spi: Spi,
cs: Pin,
}

/// Wrapper for errors returned by [`SpiWithCs`]
#[derive(Clone, Debug, PartialEq)]
pub enum SpiWithCsError<SpiError, PinError> {
/// Underlying SPI communication error
Spi(SpiError),
/// Underlying chip-select pin state setting error
Pin(PinError),
}

/// Implement [`crate::spi::Error`] for wrapped types
impl<SpiError, PinError> crate::spi::Error for SpiWithCsError<SpiError, PinError>
where
SpiError: crate::spi::Error + Debug,
PinError: Debug,
{
fn kind(&self) -> ErrorKind {
match self {
SpiWithCsError::Spi(e) => e.kind(),
SpiWithCsError::Pin(_e) => ErrorKind::Other,
}
}
}

/// [`ManagedChipSelect`] marker trait indicates Chip Select (CS) pin management is automatic
impl<Spi, Pin> ManagedChipSelect for SpiWithCs<Spi, Pin> {}

impl<Spi, Pin> SpiWithCs<Spi, Pin>
where
Pin: OutputPin,
{
/// Create a new SpiWithCS wrapper with the provided Spi and Pin
pub fn new(spi: Spi, cs: Pin) -> Self {
Self { spi, cs }
}

/// Fetch references to the inner Spi and Pin types.
/// Note that using these directly will violate the `ManagedChipSelect` constraint.
pub fn inner(&mut self) -> (&mut Spi, &mut Pin) {
(&mut self.spi, &mut self.cs)
}

/// Destroy the SpiWithCs wrapper, returning the bus and pin objects
pub fn destroy(self) -> (Spi, Pin) {
(self.spi, self.cs)
}
}

impl<Spi, Pin, W> Transfer<W> for SpiWithCs<Spi, Pin>
where
Spi: Transfer<W>,
Pin: OutputPin,
<Spi as Transfer<W>>::Error: Debug,
<Pin as OutputPin>::Error: Debug,
{
type Error = SpiWithCsError<<Spi as Transfer<W>>::Error, <Pin as OutputPin>::Error>;

/// Attempt an SPI transfer with automated CS assert/deassert
fn transfer<'w>(&mut self, data: &'w mut [W]) -> Result<(), Self::Error> {
// First assert CS
self.cs.set_low().map_err(SpiWithCsError::Pin)?;

// Attempt the transfer, storing the result for later
let spi_result = self.spi.transfer(data).map_err(SpiWithCsError::Spi);

// Deassert CS
self.cs.set_high().map_err(SpiWithCsError::Pin)?;

// Return failures
spi_result
}
}

impl<Spi, Pin, W> Write<W> for SpiWithCs<Spi, Pin>
where
Spi: Write<W>,
Pin: OutputPin,
<Spi as Write<W>>::Error: Debug,
<Pin as OutputPin>::Error: Debug,
{
type Error = SpiWithCsError<<Spi as Write<W>>::Error, <Pin as OutputPin>::Error>;

/// Attempt an SPI write with automated CS assert/deassert
fn write<'w>(&mut self, data: &'w [W]) -> Result<(), Self::Error> {
// First assert CS
self.cs.set_low().map_err(SpiWithCsError::Pin)?;

// Attempt the transfer, storing the result for later
let spi_result = self.spi.write(data).map_err(SpiWithCsError::Spi);

// Deassert CS
self.cs.set_high().map_err(SpiWithCsError::Pin)?;

// Return failures
spi_result
}
}

impl<Spi, Pin, W> WriteIter<W> for SpiWithCs<Spi, Pin>
where
Spi: WriteIter<W>,
Pin: OutputPin,
<Spi as WriteIter<W>>::Error: Debug,
<Pin as OutputPin>::Error: Debug,
{
type Error = SpiWithCsError<<Spi as WriteIter<W>>::Error, <Pin as OutputPin>::Error>;

/// Attempt an SPI write_iter with automated CS assert/deassert
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
where
WI: IntoIterator<Item = W>,
{
// First assert CS
self.cs.set_low().map_err(SpiWithCsError::Pin)?;

// Attempt the transfer, storing the result for later
let spi_result = self.spi.write_iter(words).map_err(SpiWithCsError::Spi);

// Deassert CS
self.cs.set_high().map_err(SpiWithCsError::Pin)?;

// Return failures
spi_result
}
}
}
12 changes: 12 additions & 0 deletions src/spi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,15 @@ impl core::fmt::Display for ErrorKind {
}
}
}

/// ManagedChipSelect marker trait indicates the CS pin is managed by the underlying driver.
///
/// This specifies that `spi` operations will be grouped:
/// preceded by asserting the CS pin, and followed by
/// de-asserting the CS pin, prior to returning from the method.
///
/// This is important for shared bus access to ensure that only one CS can be asserted at a given time.
/// To support shared use, drivers should require this (and not manage their own CS pins).
///
/// For usage examples see: [`crate::spi::blocking::SpiWithCs`].
pub trait ManagedChipSelect {}