Skip to content

Commit 9840fe8

Browse files
committed
Add SPI ManagedCS marker, SpiWithCs helper wrapper
ManagedCS indicates the CS pin is managed by the underlying driver, enabling safe bus sharing between SPI devices. SpiWithCs provides a wrapper around blocking SPI traits and an output pin to simplify use of this trait in unshared contexts.
1 parent 683e41e commit 9840fe8

File tree

3 files changed

+159
-5
lines changed

3 files changed

+159
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
implement these `Error` traits, which implies providing a conversion to a common
1313
set of error kinds. Generic drivers using these interfaces can then convert the errors
1414
to this common set to act upon them.
15+
- `ManagedCS` trait for SPI, signals that CS is managed by the driver to allow
16+
shared use of an SPI bus, as well as an `SpiWithCs` wrapper implementing this
17+
over an Spi and Pin implementation.
1518

1619
### Removed
1720
- Removed `DelayMs` in favor of `DelayUs` with `u32` as type for clarity.

src/spi/blocking.rs

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
//! Blocking SPI API
2-
//!
3-
//! In some cases it's possible to implement these blocking traits on top of one of the core HAL
4-
//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided.
5-
//! Implementing that marker trait will opt in your type into a blanket implementation.
6-
72
/// Blocking transfer
83
pub trait Transfer<W> {
94
/// Error type
@@ -90,3 +85,147 @@ impl<T: Transactional<W>, W: 'static> Transactional<W> for &mut T {
9085
T::exec(self, operations)
9186
}
9287
}
88+
89+
/// Provides SpiWithCS wrapper using for spi::* types combined with an OutputPin
90+
pub use spi_with_cs::{SpiWithCs, SpiWithCsError};
91+
mod spi_with_cs {
92+
93+
use core::fmt::Debug;
94+
95+
use super::{Transfer, Write, WriteIter};
96+
use crate::digital::blocking::OutputPin;
97+
use crate::spi::{ErrorKind, ManagedChipSelect};
98+
99+
/// [`SpiWithCs`] wraps an SPI implementation with Chip Select (CS)
100+
/// pin management for exclusive (non-shared) use.
101+
/// For sharing SPI between peripherals, see [shared-bus](https://crates.io/crates/shared-bus)
102+
pub struct SpiWithCs<Spi, Pin> {
103+
spi: Spi,
104+
cs: Pin,
105+
}
106+
107+
/// Wrapper for errors returned by [`SpiWithCs`]
108+
#[derive(Clone, Debug, PartialEq)]
109+
pub enum SpiWithCsError<SpiError, PinError> {
110+
/// Underlying SPI communication error
111+
Spi(SpiError),
112+
/// Underlying chip-select pin state setting error
113+
Pin(PinError),
114+
}
115+
116+
/// Implement [`crate::spi::Error`] for wrapped types
117+
impl<SpiError, PinError> crate::spi::Error for SpiWithCsError<SpiError, PinError>
118+
where
119+
SpiError: crate::spi::Error + Debug,
120+
PinError: Debug,
121+
{
122+
fn kind(&self) -> ErrorKind {
123+
match self {
124+
SpiWithCsError::Spi(e) => e.kind(),
125+
SpiWithCsError::Pin(_e) => ErrorKind::Other,
126+
}
127+
}
128+
}
129+
130+
/// [`ManagedChipSelect`] marker trait indicates Chip Select (CS) pin management is automatic
131+
impl<Spi, Pin> ManagedChipSelect for SpiWithCs<Spi, Pin> {}
132+
133+
impl<Spi, Pin> SpiWithCs<Spi, Pin>
134+
where
135+
Pin: OutputPin,
136+
{
137+
/// Create a new SpiWithCS wrapper with the provided Spi and Pin
138+
pub fn new(spi: Spi, cs: Pin) -> Self {
139+
Self { spi, cs }
140+
}
141+
142+
/// Fetch references to the inner Spi and Pin types.
143+
/// Note that using these directly will violate the `ManagedChipSelect` constraint.
144+
pub fn inner(&mut self) -> (&mut Spi, &mut Pin) {
145+
(&mut self.spi, &mut self.cs)
146+
}
147+
148+
/// Destroy the SpiWithCs wrapper, returning the bus and pin objects
149+
pub fn destroy(self) -> (Spi, Pin) {
150+
(self.spi, self.cs)
151+
}
152+
}
153+
154+
impl<Spi, Pin, W> Transfer<W> for SpiWithCs<Spi, Pin>
155+
where
156+
Spi: Transfer<W>,
157+
Pin: OutputPin,
158+
<Spi as Transfer<W>>::Error: Debug,
159+
<Pin as OutputPin>::Error: Debug,
160+
{
161+
type Error = SpiWithCsError<<Spi as Transfer<W>>::Error, <Pin as OutputPin>::Error>;
162+
163+
/// Attempt an SPI transfer with automated CS assert/deassert
164+
fn transfer<'w>(&mut self, data: &'w mut [W]) -> Result<(), Self::Error> {
165+
// First assert CS
166+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
167+
168+
// Attempt the transfer, storing the result for later
169+
let spi_result = self.spi.transfer(data).map_err(SpiWithCsError::Spi);
170+
171+
// Deassert CS
172+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
173+
174+
// Return failures
175+
spi_result
176+
}
177+
}
178+
179+
impl<Spi, Pin, W> Write<W> for SpiWithCs<Spi, Pin>
180+
where
181+
Spi: Write<W>,
182+
Pin: OutputPin,
183+
<Spi as Write<W>>::Error: Debug,
184+
<Pin as OutputPin>::Error: Debug,
185+
{
186+
type Error = SpiWithCsError<<Spi as Write<W>>::Error, <Pin as OutputPin>::Error>;
187+
188+
/// Attempt an SPI write with automated CS assert/deassert
189+
fn write<'w>(&mut self, data: &'w [W]) -> Result<(), Self::Error> {
190+
// First assert CS
191+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
192+
193+
// Attempt the transfer, storing the result for later
194+
let spi_result = self.spi.write(data).map_err(SpiWithCsError::Spi);
195+
196+
// Deassert CS
197+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
198+
199+
// Return failures
200+
spi_result
201+
}
202+
}
203+
204+
impl<Spi, Pin, W> WriteIter<W> for SpiWithCs<Spi, Pin>
205+
where
206+
Spi: WriteIter<W>,
207+
Pin: OutputPin,
208+
<Spi as WriteIter<W>>::Error: Debug,
209+
<Pin as OutputPin>::Error: Debug,
210+
{
211+
type Error = SpiWithCsError<<Spi as WriteIter<W>>::Error, <Pin as OutputPin>::Error>;
212+
213+
/// Attempt an SPI write_iter with automated CS assert/deassert
214+
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
215+
where
216+
WI: IntoIterator<Item = W>,
217+
{
218+
// First assert CS
219+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
220+
221+
// Attempt the transfer, storing the result for later
222+
let spi_result = self.spi.write_iter(words).map_err(SpiWithCsError::Spi);
223+
224+
// Deassert CS
225+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
226+
227+
// Return failures
228+
spi_result
229+
}
230+
}
231+
}

src/spi/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,15 @@ impl core::fmt::Display for ErrorKind {
107107
}
108108
}
109109
}
110+
111+
/// ManagedChipSelect marker trait indicates the CS pin is managed by the underlying driver.
112+
///
113+
/// This specifies that `spi` operations will be grouped:
114+
/// preceded by asserting the CS pin, and followed by
115+
/// de-asserting the CS pin, prior to returning from the method.
116+
///
117+
/// This is important for shared bus access to ensure that only one CS can be asserted at a given time.
118+
/// To support shared use, drivers should require this (and not manage their own CS pins).
119+
///
120+
/// For usage examples see: [`crate::spi::blocking::SpiWithCs`].
121+
pub trait ManagedChipSelect {}

0 commit comments

Comments
 (0)