Skip to content

Commit f52a0fc

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

File tree

1 file changed

+190
-1
lines changed

1 file changed

+190
-1
lines changed

src/blocking/spi.rs

Lines changed: 190 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,43 @@ 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+
53+
/// Blocking transfer
54+
pub mod transfer {
55+
/// Default implementation of `blocking::spi::Transfer<W>` for implementers of
56+
/// `spi::FullDuplex<W>`
57+
pub trait Default<W>: crate::nb::spi::FullDuplex<W> {}
58+
59+
impl<W, S> crate::blocking::spi::Transfer<W> for S
60+
where
61+
S: Default<W>,
62+
W: Clone,
63+
{
64+
type Error = S::Error;
65+
66+
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], S::Error> {
67+
for word in words.iter_mut() {
68+
nb::block!(self.write(word.clone()))?;
69+
*word = nb::block!(self.read())?;
70+
}
71+
72+
Ok(words)
73+
}
74+
}
75+
}
76+
77+
3278
/// Operation for transactional SPI trait
3379
///
3480
/// This allows composition of SPI operations into a single bus transaction
@@ -41,11 +87,154 @@ pub enum Operation<'a, W: 'static> {
4187
}
4288

4389
/// Transactional trait allows multiple actions to be executed
44-
/// as part of a single SPI transaction
90+
/// as part of a single SPI transaction.
91+
///
92+
/// This API guarantees ordering, ensuring operations from
93+
/// different sources will not be interleaved on a shared bus.
94+
/// [ManagedCs]
4595
pub trait Transactional<W: 'static> {
4696
/// Associated error type
4797
type Error;
4898

4999
/// Execute the provided transactions
50100
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
51101
}
102+
103+
/// Provides SpiWithCS wrapper around an spi::* and OutputPin impl
104+
pub mod spi_with_cs {
105+
106+
use core::fmt::Debug;
107+
use core::marker::PhantomData;
108+
109+
use super::{ManagedCs, Transfer, Write, WriteIter};
110+
use crate::blocking::digital::OutputPin;
111+
112+
/// SpiWithCS wraps an blocking::spi* implementation with Chip Select (CS)
113+
/// pin management for exclusive (non-shared) use.
114+
/// For sharing SPI between peripherals, see [shared-bus](https://crates.io/crates/shared-bus)
115+
pub struct SpiWithCs<Spi, SpiError, Pin, PinError> {
116+
spi: Spi,
117+
cs: Pin,
118+
119+
_spi_err: PhantomData<SpiError>,
120+
_pin_err: PhantomData<PinError>,
121+
}
122+
123+
/// Underlying causes for errors. Either SPI communication or CS pin state setting error
124+
#[derive(Clone, Debug, PartialEq)]
125+
pub enum SpiWithCsError<SpiError, PinError> {
126+
/// Underlying SPI communication error
127+
Spi(SpiError),
128+
/// Underlying chip-select pin state setting error
129+
Pin(PinError),
130+
}
131+
132+
/// ManagedCS marker trait indicates Chip Select management is automatic
133+
impl<Spi, SpiError, Pin, PinError> ManagedCs for SpiWithCs<Spi, SpiError, Pin, PinError> {}
134+
135+
impl<Spi, SpiError, Pin, PinError> SpiWithCs<Spi, SpiError, Pin, PinError>
136+
where
137+
Pin: crate::blocking::digital::OutputPin<Error = PinError>,
138+
SpiError: Debug,
139+
PinError: Debug,
140+
{
141+
/// Create a new SpiWithCS wrapper with the provided Spi and Pin
142+
pub fn new(spi: Spi, cs: Pin) -> Self {
143+
Self {
144+
spi,
145+
cs,
146+
_spi_err: PhantomData,
147+
_pin_err: PhantomData,
148+
}
149+
}
150+
151+
/// Fetch references to the inner Spi and Pin types.
152+
/// Note that using these directly will violate the `ManagedCs` constraint.
153+
pub fn inner(&mut self) -> (&mut Spi, &mut Pin) {
154+
(&mut self.spi, &mut self.cs)
155+
}
156+
157+
/// Destroy the SpiWithCs wrapper, returning the bus and pin objects
158+
pub fn destroy(self) -> (Spi, Pin) {
159+
(self.spi, self.cs)
160+
}
161+
}
162+
163+
impl<Spi, SpiError, Pin, PinError> Transfer<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
164+
where
165+
Spi: Transfer<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 transfer with automated CS assert/deassert
173+
fn transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], 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.transfer(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> Write<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
189+
where
190+
Spi: Write<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 with automated CS assert/deassert
198+
fn write<'w>(&mut self, data: &'w [u8]) -> Result<(), Self::Error> {
199+
// First assert CS
200+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
201+
202+
// Attempt the transfer, storing the result for later
203+
let spi_result = self.spi.write(data).map_err(SpiWithCsError::Spi);
204+
205+
// Deassert CS
206+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
207+
208+
// Return failures
209+
spi_result
210+
}
211+
}
212+
213+
impl<Spi, SpiError, Pin, PinError> WriteIter<u8> for SpiWithCs<Spi, SpiError, Pin, PinError>
214+
where
215+
Spi: WriteIter<u8, Error = SpiError>,
216+
Pin: OutputPin<Error = PinError>,
217+
SpiError: Debug,
218+
PinError: Debug,
219+
{
220+
type Error = SpiWithCsError<SpiError, PinError>;
221+
222+
/// Attempt an SPI write_iter with automated CS assert/deassert
223+
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
224+
where
225+
WI: IntoIterator<Item = u8>,
226+
{
227+
// First assert CS
228+
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
229+
230+
// Attempt the transfer, storing the result for later
231+
let spi_result = self.spi.write_iter(words).map_err(SpiWithCsError::Spi);
232+
233+
// Deassert CS
234+
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
235+
236+
// Return failures
237+
spi_result
238+
}
239+
}
240+
}

0 commit comments

Comments
 (0)