Skip to content

Commit 986e589

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

File tree

1 file changed

+166
-1
lines changed

1 file changed

+166
-1
lines changed

src/blocking/spi.rs

Lines changed: 166 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,19 @@ 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+
3254
/// Operation for transactional SPI trait
3355
///
3456
/// This allows composition of SPI operations into a single bus transaction
@@ -41,11 +63,154 @@ pub enum Operation<'a, W: 'static> {
4163
}
4264

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

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

0 commit comments

Comments
 (0)