Skip to content

Commit 3d069fa

Browse files
authored
Merge pull request #474 from Dirbaio/bus-stuff
spi: move async ExclusiveDevice to embedded-hal-bus.
2 parents 8c73e4d + 5f8f7f9 commit 3d069fa

File tree

9 files changed

+197
-152
lines changed

9 files changed

+197
-152
lines changed

.github/workflows/rustdoc.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
on:
2+
push: # Run CI for all branches except GitHub merge queue tmp branches
3+
branches-ignore:
4+
- "gh-readonly-queue/**"
5+
pull_request: # Run CI for PRs on any branch
6+
merge_group: # Run CI for the GitHub merge queue
7+
8+
name: Rustdoc check
9+
jobs:
10+
clippy:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- uses: dtolnay/rust-toolchain@master
15+
with:
16+
toolchain: nightly-2023-07-03
17+
- run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features

embedded-hal-async/src/spi.rs

Lines changed: 0 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
//! SPI master mode traits.
22
3-
use core::fmt::Debug;
4-
5-
use embedded_hal::digital::OutputPin;
6-
use embedded_hal::spi as blocking;
73
pub use embedded_hal::spi::{
84
Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
95
};
106

11-
use crate::delay::DelayUs;
12-
137
/// SPI device trait
148
///
159
/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
@@ -170,149 +164,3 @@ impl<T: SpiBus<Word> + ?Sized, Word: 'static + Copy> SpiBus<Word> for &mut T {
170164
T::flush(self).await
171165
}
172166
}
173-
174-
/// Error type for [`ExclusiveDevice`] operations.
175-
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
176-
pub enum ExclusiveDeviceError<BUS, CS> {
177-
/// An inner SPI bus operation failed
178-
Spi(BUS),
179-
/// Asserting or deasserting CS failed
180-
Cs(CS),
181-
}
182-
183-
impl<BUS, CS> Error for ExclusiveDeviceError<BUS, CS>
184-
where
185-
BUS: Error + Debug,
186-
CS: Debug,
187-
{
188-
fn kind(&self) -> ErrorKind {
189-
match self {
190-
Self::Spi(e) => e.kind(),
191-
Self::Cs(_) => ErrorKind::ChipSelectFault,
192-
}
193-
}
194-
}
195-
196-
/// [`SpiDevice`] implementation with exclusive access to the bus (not shared).
197-
///
198-
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
199-
/// ideal for when no sharing is required (only one SPI device is present on the bus).
200-
pub struct ExclusiveDevice<BUS, CS, D> {
201-
bus: BUS,
202-
cs: CS,
203-
delay: D,
204-
}
205-
206-
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
207-
/// Create a new ExclusiveDevice
208-
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
209-
Self { bus, cs, delay }
210-
}
211-
212-
/// Returns a reference to the underlying bus object.
213-
pub fn bus(&self) -> &BUS {
214-
&self.bus
215-
}
216-
217-
/// Returns a mutable reference to the underlying bus object.
218-
pub fn bus_mut(&mut self) -> &mut BUS {
219-
&mut self.bus
220-
}
221-
}
222-
223-
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
224-
where
225-
BUS: ErrorType,
226-
CS: OutputPin,
227-
{
228-
type Error = ExclusiveDeviceError<BUS::Error, CS::Error>;
229-
}
230-
231-
impl<Word: Copy + 'static, BUS, CS, D> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
232-
where
233-
BUS: blocking::SpiBus<Word>,
234-
CS: OutputPin,
235-
D: embedded_hal::delay::DelayUs,
236-
{
237-
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
238-
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
239-
240-
let op_res = 'ops: {
241-
for op in operations {
242-
let res = match op {
243-
Operation::Read(buf) => self.bus.read(buf),
244-
Operation::Write(buf) => self.bus.write(buf),
245-
Operation::Transfer(read, write) => self.bus.transfer(read, write),
246-
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
247-
Operation::DelayUs(us) => match self.bus.flush() {
248-
Err(e) => Err(e),
249-
Ok(()) => {
250-
self.delay.delay_us(*us);
251-
Ok(())
252-
}
253-
},
254-
};
255-
if let Err(e) = res {
256-
break 'ops Err(e);
257-
}
258-
}
259-
Ok(())
260-
};
261-
262-
// On failure, it's important to still flush and deassert CS.
263-
let flush_res = self.bus.flush();
264-
let cs_res = self.cs.set_high();
265-
266-
op_res.map_err(ExclusiveDeviceError::Spi)?;
267-
flush_res.map_err(ExclusiveDeviceError::Spi)?;
268-
cs_res.map_err(ExclusiveDeviceError::Cs)?;
269-
270-
Ok(())
271-
}
272-
}
273-
274-
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
275-
where
276-
BUS: SpiBus<Word>,
277-
CS: OutputPin,
278-
D: DelayUs,
279-
{
280-
async fn transaction(
281-
&mut self,
282-
operations: &mut [Operation<'_, Word>],
283-
) -> Result<(), Self::Error> {
284-
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
285-
286-
let op_res = 'ops: {
287-
for op in operations {
288-
let res = match op {
289-
Operation::Read(buf) => self.bus.read(buf).await,
290-
Operation::Write(buf) => self.bus.write(buf).await,
291-
Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
292-
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
293-
Operation::DelayUs(us) => match self.bus.flush().await {
294-
Err(e) => Err(e),
295-
Ok(()) => {
296-
self.delay.delay_us(*us).await;
297-
Ok(())
298-
}
299-
},
300-
};
301-
if let Err(e) = res {
302-
break 'ops Err(e);
303-
}
304-
}
305-
Ok(())
306-
};
307-
308-
// On failure, it's important to still flush and deassert CS.
309-
let flush_res = self.bus.flush().await;
310-
let cs_res = self.cs.set_high();
311-
312-
op_res.map_err(ExclusiveDeviceError::Spi)?;
313-
flush_res.map_err(ExclusiveDeviceError::Spi)?;
314-
cs_res.map_err(ExclusiveDeviceError::Cs)?;
315-
316-
Ok(())
317-
}
318-
}

embedded-hal-bus/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ version = "0.1.0-alpha.3"
1515

1616
[features]
1717
std = []
18+
async = ["dep:embedded-hal-async"]
1819

1920
[dependencies]
2021
embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" }
22+
embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true }
2123
critical-section = { version = "1.0" }
24+
25+
[package.metadata.docs.rs]
26+
features = ["std", "async"]
27+
rustdoc-args = ["--cfg", "docsrs"]

embedded-hal-bus/src/asynch/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//! Bus sharing utilities for [`embedded-hal-async`]
2+
3+
pub mod spi;
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use embedded_hal::{digital::OutputPin, spi as blocking};
2+
use embedded_hal_async::{
3+
delay::DelayUs,
4+
spi::{ErrorType, Operation, SpiBus, SpiDevice},
5+
};
6+
7+
pub use crate::spi::DeviceError;
8+
9+
/// [`SpiDevice`] implementation with exclusive access to the bus (not shared).
10+
///
11+
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
12+
/// ideal for when no sharing is required (only one SPI device is present on the bus).
13+
pub struct ExclusiveDevice<BUS, CS, D> {
14+
bus: BUS,
15+
cs: CS,
16+
delay: D,
17+
}
18+
19+
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
20+
/// Create a new ExclusiveDevice
21+
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
22+
Self { bus, cs, delay }
23+
}
24+
25+
/// Returns a reference to the underlying bus object.
26+
pub fn bus(&self) -> &BUS {
27+
&self.bus
28+
}
29+
30+
/// Returns a mutable reference to the underlying bus object.
31+
pub fn bus_mut(&mut self) -> &mut BUS {
32+
&mut self.bus
33+
}
34+
}
35+
36+
impl<BUS, CS> ExclusiveDevice<BUS, CS, super::NoDelay> {
37+
/// Create a new ExclusiveDevice without support for in-transaction delays.
38+
///
39+
/// # Panics
40+
///
41+
/// The returned device will panic if you try to execute a transaction
42+
/// that contains any operations of type `Operation::DelayUs`.
43+
pub fn new_no_delay(bus: BUS, cs: CS) -> Self {
44+
Self {
45+
bus,
46+
cs,
47+
delay: super::NoDelay,
48+
}
49+
}
50+
}
51+
52+
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
53+
where
54+
BUS: ErrorType,
55+
CS: OutputPin,
56+
{
57+
type Error = DeviceError<BUS::Error, CS::Error>;
58+
}
59+
60+
impl<Word: Copy + 'static, BUS, CS, D> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
61+
where
62+
BUS: blocking::SpiBus<Word>,
63+
CS: OutputPin,
64+
D: embedded_hal::delay::DelayUs,
65+
{
66+
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
67+
self.cs.set_low().map_err(DeviceError::Cs)?;
68+
69+
let op_res = 'ops: {
70+
for op in operations {
71+
let res = match op {
72+
Operation::Read(buf) => self.bus.read(buf),
73+
Operation::Write(buf) => self.bus.write(buf),
74+
Operation::Transfer(read, write) => self.bus.transfer(read, write),
75+
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
76+
Operation::DelayUs(us) => match self.bus.flush() {
77+
Err(e) => Err(e),
78+
Ok(()) => {
79+
self.delay.delay_us(*us);
80+
Ok(())
81+
}
82+
},
83+
};
84+
if let Err(e) = res {
85+
break 'ops Err(e);
86+
}
87+
}
88+
Ok(())
89+
};
90+
91+
// On failure, it's important to still flush and deassert CS.
92+
let flush_res = self.bus.flush();
93+
let cs_res = self.cs.set_high();
94+
95+
op_res.map_err(DeviceError::Spi)?;
96+
flush_res.map_err(DeviceError::Spi)?;
97+
cs_res.map_err(DeviceError::Cs)?;
98+
99+
Ok(())
100+
}
101+
}
102+
103+
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
104+
where
105+
BUS: SpiBus<Word>,
106+
CS: OutputPin,
107+
D: DelayUs,
108+
{
109+
async fn transaction(
110+
&mut self,
111+
operations: &mut [Operation<'_, Word>],
112+
) -> Result<(), Self::Error> {
113+
self.cs.set_low().map_err(DeviceError::Cs)?;
114+
115+
let op_res = 'ops: {
116+
for op in operations {
117+
let res = match op {
118+
Operation::Read(buf) => self.bus.read(buf).await,
119+
Operation::Write(buf) => self.bus.write(buf).await,
120+
Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
121+
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
122+
Operation::DelayUs(us) => match self.bus.flush().await {
123+
Err(e) => Err(e),
124+
Ok(()) => {
125+
self.delay.delay_us(*us).await;
126+
Ok(())
127+
}
128+
},
129+
};
130+
if let Err(e) = res {
131+
break 'ops Err(e);
132+
}
133+
}
134+
Ok(())
135+
};
136+
137+
// On failure, it's important to still flush and deassert CS.
138+
let flush_res = self.bus.flush().await;
139+
let cs_res = self.cs.set_high();
140+
141+
op_res.map_err(DeviceError::Spi)?;
142+
flush_res.map_err(DeviceError::Spi)?;
143+
cs_res.map_err(DeviceError::Cs)?;
144+
145+
Ok(())
146+
}
147+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! `SpiDevice` implementations.
2+
3+
mod exclusive;
4+
pub use exclusive::*;
5+
6+
pub use crate::spi::NoDelay;
7+
8+
impl embedded_hal_async::delay::DelayUs for NoDelay {
9+
async fn delay_ms(&mut self, _ms: u32) {
10+
panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.")
11+
}
12+
13+
async fn delay_us(&mut self, _us: u32) {
14+
panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.")
15+
}
16+
}

embedded-hal-bus/src/i2c/mutex.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::sync::Mutex;
66
/// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads,
77
/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that
88
/// it is only available in `std` targets.
9+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
910
pub struct MutexDevice<'a, T> {
1011
bus: &'a Mutex<T>,
1112
}

embedded-hal-bus/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#![doc = include_str!("../README.md")]
22
#![warn(missing_docs)]
33
#![cfg_attr(not(feature = "std"), no_std)]
4+
#![cfg_attr(docsrs, feature(doc_cfg))]
5+
#![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))]
46

57
pub mod i2c;
68
pub mod spi;
9+
10+
#[cfg(feature = "async")]
11+
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
12+
pub mod asynch;

embedded-hal-bus/src/spi/mutex.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use super::DeviceError;
1313
/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads,
1414
/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is
1515
/// it is only available in `std` targets.
16+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1617
pub struct MutexDevice<'a, BUS, CS, D> {
1718
bus: &'a Mutex<BUS>,
1819
cs: CS,

0 commit comments

Comments
 (0)