Skip to content

Commit 7e6fc37

Browse files
ryankurteeldruin
andcommitted
Added ManagedCS marker trait, SpiWithCs wrapper, per #180
Co-authored-by: Diego Barrios Romero <[email protected]>
1 parent 7f3801e commit 7e6fc37

File tree

1 file changed

+165
-1
lines changed

1 file changed

+165
-1
lines changed

src/blocking/spi.rs

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Blocking SPI API
22
33
/// Blocking transfer
4+
///
5+
/// This API provides no ordering guarantees as operations can be interleaved on the bus.
6+
/// If you need to compose operations use the [Transactional] trait
47
pub trait Transfer<W> {
58
/// Error type
69
type Error;
@@ -10,6 +13,9 @@ pub trait Transfer<W> {
1013
}
1114

1215
/// Blocking write
16+
///
17+
/// This API provides no ordering guarantees as operations can be interleaved on the bus.
18+
/// If you need to compose operations use the [Transactional] trait
1319
pub trait Write<W> {
1420
/// Error type
1521
type Error;
@@ -19,6 +25,9 @@ pub trait Write<W> {
1925
}
2026

2127
/// Blocking write (iterator version)
28+
///
29+
/// This API provides no ordering guarantees as operations can be interleaved on the bus.
30+
/// If you need to compose operations use the [Transactional] trait
2231
pub trait WriteIter<W> {
2332
/// Error type
2433
type Error;
@@ -29,6 +38,18 @@ pub trait WriteIter<W> {
2938
WI: IntoIterator<Item = W>;
3039
}
3140

41+
/// ManagedCS marker trait indicates the CS pin is managed by the underlying driver.
42+
///
43+
/// This specifies that all `spi` operations will be preceded by asserting the CS pin,
44+
/// and followed by de-asserting the CS pin, prior to returning from the method.
45+
///
46+
/// This is important for shared bus access to ensure that only one CS can be asserted
47+
/// at a given time.
48+
/// To chain operations within one transaction see [Transactional].
49+
/// For or a convenience wrapper defining this type for non-shared / exclusive use
50+
/// see [`SpiWithCs`](spi_with_cs::SpiWithCs).
51+
pub trait ManagedCs {}
52+
3253
/// Operation for transactional SPI trait
3354
///
3455
/// This allows composition of SPI operations into a single bus transaction
@@ -41,11 +62,154 @@ pub enum Operation<'a, W: 'static> {
4162
}
4263

4364
/// Transactional trait allows multiple actions to be executed
44-
/// as part of a single SPI transaction
65+
/// as part of a single SPI transaction.
66+
///
67+
/// This API guarantees ordering, ensuring operations from
68+
/// different sources will not be interleaved on a shared bus.
69+
/// [ManagedCs]
4570
pub trait Transactional<W: 'static> {
4671
/// Associated error type
4772
type Error;
4873

4974
/// Execute the provided transactions
5075
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
5176
}
77+
78+
/// Provides SpiWithCS wrapper around an spi::* and OutputPin impl
79+
pub mod spi_with_cs {
80+
81+
use core::fmt::Debug;
82+
use core::marker::PhantomData;
83+
84+
use super::{ManagedCs, Transfer, Write, WriteIter};
85+
use crate::blocking::digital::OutputPin;
86+
87+
/// SpiWithCS wraps an blocking::spi* implementation with Chip Select (CS)
88+
/// pin management for exclusive (non-shared) use.
89+
/// For sharing SPI between peripherals, see [shared-bus](https://crates.io/crates/shared-bus)
90+
pub struct SpiWithCs<Spi, SpiError, Pin, PinError> {
91+
spi: Spi,
92+
cs: Pin,
93+
94+
_spi_err: PhantomData<SpiError>,
95+
_pin_err: PhantomData<PinError>,
96+
}
97+
98+
/// Underlying causes for errors. Either SPI communication or CS pin state setting error
99+
#[derive(Clone, Debug, PartialEq)]
100+
pub enum SpiWithCsError<SpiError, PinError> {
101+
/// Underlying SPI communication error
102+
Spi(SpiError),
103+
/// Underlying chip-select pin state setting error
104+
Pin(PinError),
105+
}
106+
107+
/// ManagedCS marker trait indicates Chip Select management is automatic
108+
impl<Spi, SpiError, Pin, PinError> ManagedCs for SpiWithCs<Spi, SpiError, Pin, PinError> {}
109+
110+
impl<Spi, SpiError, Pin, PinError> SpiWithCs<Spi, SpiError, Pin, PinError>
111+
where
112+
Pin: crate::blocking::digital::OutputPin<Error = PinError>,
113+
SpiError: Debug,
114+
PinError: Debug,
115+
{
116+
/// Create a new SpiWithCS wrapper with the provided Spi and Pin
117+
pub fn new(spi: Spi, cs: Pin) -> Self {
118+
Self {
119+
spi,
120+
cs,
121+
_spi_err: PhantomData,
122+
_pin_err: PhantomData,
123+
}
124+
}
125+
126+
/// Fetch references to the inner Spi and Pin types.
127+
/// Note that using these directly will violate the `ManagedCs` constraint.
128+
pub fn inner(&mut self) -> (&mut Spi, &mut Pin) {
129+
(&mut self.spi, &mut self.cs)
130+
}
131+
132+
/// Destroy the SpiWithCs wrapper, returning the bus and pin objects
133+
pub fn destroy(self) -> (Spi, Pin) {
134+
(self.spi, self.cs)
135+
}
136+
}
137+
138+
impl<Spi, SpiError, Pin, PinError> Transfer<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
139+
where
140+
Spi: Transfer<u8, Error = SpiError>,
141+
Pin: OutputPin<Error = PinError>,
142+
SpiError: Debug,
143+
PinError: Debug,
144+
{
145+
type Error = SpiWithCsError<SpiError, PinError>;
146+
147+
/// Attempt an SPI transfer with automated CS assert/deassert
148+
fn transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
149+
// First assert CS
150+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
151+
152+
// Attempt the transfer, storing the result for later
153+
let spi_result = self.spi.transfer(data).map_err(SpiWithCsError::Spi);
154+
155+
// Deassert CS
156+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
157+
158+
// Return failures
159+
spi_result
160+
}
161+
}
162+
163+
impl<Spi, SpiError, Pin, PinError> Write<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
164+
where
165+
Spi: Write<u8, Error = SpiError>,
166+
Pin: OutputPin<Error = PinError>,
167+
SpiError: Debug,
168+
PinError: Debug,
169+
{
170+
type Error = SpiWithCsError<SpiError, PinError>;
171+
172+
/// Attempt an SPI write with automated CS assert/deassert
173+
fn write<'w>(&mut self, data: &'w [u8]) -> Result<(), Self::Error> {
174+
// First assert CS
175+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
176+
177+
// Attempt the transfer, storing the result for later
178+
let spi_result = self.spi.write(data).map_err(SpiWithCsError::Spi);
179+
180+
// Deassert CS
181+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
182+
183+
// Return failures
184+
spi_result
185+
}
186+
}
187+
188+
impl<Spi, SpiError, Pin, PinError> WriteIter<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
189+
where
190+
Spi: WriteIter<u8, Error = SpiError>,
191+
Pin: OutputPin<Error = PinError>,
192+
SpiError: Debug,
193+
PinError: Debug,
194+
{
195+
type Error = SpiWithCsError<SpiError, PinError>;
196+
197+
/// Attempt an SPI write_iter with automated CS assert/deassert
198+
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
199+
where
200+
WI: IntoIterator<Item = u8>,
201+
{
202+
// First assert CS
203+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
204+
205+
// Attempt the transfer, storing the result for later
206+
let spi_result = self.spi.write_iter(words).map_err(SpiWithCsError::Spi);
207+
208+
// Deassert CS
209+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
210+
211+
// Return failures
212+
spi_result
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)