Skip to content

Commit 37083ec

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

File tree

1 file changed

+167
-1
lines changed

1 file changed

+167
-1
lines changed

src/blocking/spi.rs

Lines changed: 167 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,20 @@ 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. Drivers should require this (and not manage their own CS pins)
48+
/// in order to support shared use.
49+
///
50+
/// To chain operations within one transaction see [Transactional].
51+
/// For or a convenience wrapper defining this type for non-shared / exclusive use
52+
/// see [`SpiWithCs`](spi_with_cs::SpiWithCs).
53+
pub trait ManagedCs {}
54+
3255
/// Operation for transactional SPI trait
3356
///
3457
/// This allows composition of SPI operations into a single bus transaction
@@ -41,11 +64,154 @@ pub enum Operation<'a, W: 'static> {
4164
}
4265

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

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

0 commit comments

Comments
 (0)