Skip to content

Commit a8ff64f

Browse files
authored
Merge pull request #462 from Dirbaio/spi-delay
spi: add Operation::DelayUs(u32).
2 parents 3620083 + d07d39e commit a8ff64f

File tree

8 files changed

+160
-31
lines changed

8 files changed

+160
-31
lines changed

embedded-hal-async/src/spi.rs

+26-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub use embedded_hal::spi::{
88
Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
99
};
1010

11+
use crate::delay::DelayUs;
12+
1113
/// SPI device trait
1214
///
1315
/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
@@ -195,30 +197,32 @@ where
195197
///
196198
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
197199
/// ideal for when no sharing is required (only one SPI device is present on the bus).
198-
pub struct ExclusiveDevice<BUS, CS> {
200+
pub struct ExclusiveDevice<BUS, CS, D> {
199201
bus: BUS,
200202
cs: CS,
203+
delay: D,
201204
}
202205

203-
impl<BUS, CS> ExclusiveDevice<BUS, CS> {
206+
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
204207
/// Create a new ExclusiveDevice
205-
pub fn new(bus: BUS, cs: CS) -> Self {
206-
Self { bus, cs }
208+
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
209+
Self { bus, cs, delay }
207210
}
208211
}
209212

210-
impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS>
213+
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
211214
where
212215
BUS: ErrorType,
213216
CS: OutputPin,
214217
{
215218
type Error = ExclusiveDeviceError<BUS::Error, CS::Error>;
216219
}
217220

218-
impl<Word: Copy + 'static, BUS, CS> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS>
221+
impl<Word: Copy + 'static, BUS, CS, D> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
219222
where
220223
BUS: blocking::SpiBus<Word>,
221224
CS: OutputPin,
225+
D: embedded_hal::delay::DelayUs,
222226
{
223227
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
224228
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
@@ -230,6 +234,13 @@ where
230234
Operation::Write(buf) => self.bus.write(buf),
231235
Operation::Transfer(read, write) => self.bus.transfer(read, write),
232236
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
237+
Operation::DelayUs(us) => match self.bus.flush() {
238+
Err(e) => Err(e),
239+
Ok(()) => {
240+
self.delay.delay_us(*us);
241+
Ok(())
242+
}
243+
},
233244
};
234245
if let Err(e) = res {
235246
break 'ops Err(e);
@@ -250,10 +261,11 @@ where
250261
}
251262
}
252263

253-
impl<Word: Copy + 'static, BUS, CS> SpiDevice<Word> for ExclusiveDevice<BUS, CS>
264+
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
254265
where
255266
BUS: SpiBus<Word>,
256267
CS: OutputPin,
268+
D: DelayUs,
257269
{
258270
async fn transaction(
259271
&mut self,
@@ -268,6 +280,13 @@ where
268280
Operation::Write(buf) => self.bus.write(buf).await,
269281
Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
270282
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
283+
Operation::DelayUs(us) => match self.bus.flush().await {
284+
Err(e) => Err(e),
285+
Ok(()) => {
286+
self.delay.delay_us(*us).await;
287+
Ok(())
288+
}
289+
},
271290
};
272291
if let Err(e) = res {
273292
break 'ops Err(e);

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::cell::RefCell;
22
use critical_section::Mutex;
3+
use embedded_hal::delay::DelayUs;
34
use embedded_hal::digital::OutputPin;
45
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
56

@@ -15,30 +16,48 @@ use super::DeviceError;
1516
/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely
1617
/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using
1718
/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections.
18-
pub struct CriticalSectionDevice<'a, BUS, CS> {
19+
pub struct CriticalSectionDevice<'a, BUS, CS, D> {
1920
bus: &'a Mutex<RefCell<BUS>>,
2021
cs: CS,
22+
delay: D,
2123
}
2224

23-
impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> {
25+
impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> {
2426
/// Create a new ExclusiveDevice
25-
pub fn new(bus: &'a Mutex<RefCell<BUS>>, cs: CS) -> Self {
26-
Self { bus, cs }
27+
pub fn new(bus: &'a Mutex<RefCell<BUS>>, cs: CS, delay: D) -> Self {
28+
Self { bus, cs, delay }
2729
}
2830
}
2931

30-
impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS>
32+
impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> {
33+
/// Create a new CriticalSectionDevice without support for in-transaction delays.
34+
///
35+
/// # Panics
36+
///
37+
/// The returned device will panic if you try to execute a transaction
38+
/// that contains any operations of type `Operation::DelayUs`.
39+
pub fn new_no_delay(bus: &'a Mutex<RefCell<BUS>>, cs: CS) -> Self {
40+
Self {
41+
bus,
42+
cs,
43+
delay: super::NoDelay,
44+
}
45+
}
46+
}
47+
48+
impl<'a, BUS, CS, D> ErrorType for CriticalSectionDevice<'a, BUS, CS, D>
3149
where
3250
BUS: ErrorType,
3351
CS: OutputPin,
3452
{
3553
type Error = DeviceError<BUS::Error, CS::Error>;
3654
}
3755

38-
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for CriticalSectionDevice<'a, BUS, CS>
56+
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for CriticalSectionDevice<'a, BUS, CS, D>
3957
where
4058
BUS: SpiBus<Word>,
4159
CS: OutputPin,
60+
D: DelayUs,
4261
{
4362
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
4463
critical_section::with(|cs| {
@@ -51,6 +70,11 @@ where
5170
Operation::Write(buf) => bus.write(buf),
5271
Operation::Transfer(read, write) => bus.transfer(read, write),
5372
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
73+
Operation::DelayUs(us) => {
74+
bus.flush()?;
75+
self.delay.delay_us(*us);
76+
Ok(())
77+
}
5478
});
5579

5680
// On failure, it's important to still flush and deassert CS.

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! SPI bus sharing mechanisms.
22
3+
use embedded_hal::delay::DelayUs;
34
use embedded_hal::digital::OutputPin;
45
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
56

@@ -9,30 +10,48 @@ use super::DeviceError;
910
///
1011
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus),
1112
/// ideal for when no sharing is required (only one SPI device is present on the bus).
12-
pub struct ExclusiveDevice<BUS, CS> {
13+
pub struct ExclusiveDevice<BUS, CS, D> {
1314
bus: BUS,
1415
cs: CS,
16+
delay: D,
1517
}
1618

17-
impl<BUS, CS> ExclusiveDevice<BUS, CS> {
19+
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
1820
/// Create a new ExclusiveDevice
19-
pub fn new(bus: BUS, cs: CS) -> Self {
20-
Self { bus, cs }
21+
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
22+
Self { bus, cs, delay }
2123
}
2224
}
2325

24-
impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS>
26+
impl<BUS, CS> ExclusiveDevice<BUS, CS, super::NoDelay> {
27+
/// Create a new ExclusiveDevice without support for in-transaction delays.
28+
///
29+
/// # Panics
30+
///
31+
/// The returned device will panic if you try to execute a transaction
32+
/// that contains any operations of type `Operation::DelayUs`.
33+
pub fn new_no_delay(bus: BUS, cs: CS) -> Self {
34+
Self {
35+
bus,
36+
cs,
37+
delay: super::NoDelay,
38+
}
39+
}
40+
}
41+
42+
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
2543
where
2644
BUS: ErrorType,
2745
CS: OutputPin,
2846
{
2947
type Error = DeviceError<BUS::Error, CS::Error>;
3048
}
3149

32-
impl<Word: Copy + 'static, BUS, CS> SpiDevice<Word> for ExclusiveDevice<BUS, CS>
50+
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
3351
where
3452
BUS: SpiBus<Word>,
3553
CS: OutputPin,
54+
D: DelayUs,
3655
{
3756
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
3857
self.cs.set_low().map_err(DeviceError::Cs)?;
@@ -42,6 +61,11 @@ where
4261
Operation::Write(buf) => self.bus.write(buf),
4362
Operation::Transfer(read, write) => self.bus.transfer(read, write),
4463
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
64+
Operation::DelayUs(us) => {
65+
self.bus.flush()?;
66+
self.delay.delay_us(*us);
67+
Ok(())
68+
}
4569
});
4670

4771
// On failure, it's important to still flush and deassert CS.

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

+9
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,12 @@ where
3535
}
3636
}
3737
}
38+
39+
/// Dummy `DelayUs` implementation that panics on use.
40+
pub struct NoDelay;
41+
42+
impl embedded_hal::delay::DelayUs for NoDelay {
43+
fn delay_us(&mut self, _us: u32) {
44+
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.")
45+
}
46+
}

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use embedded_hal::delay::DelayUs;
12
use embedded_hal::digital::OutputPin;
23
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
34
use std::sync::Mutex;
@@ -12,30 +13,48 @@ use super::DeviceError;
1213
/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads,
1314
/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is
1415
/// it is only available in `std` targets.
15-
pub struct MutexDevice<'a, BUS, CS> {
16+
pub struct MutexDevice<'a, BUS, CS, D> {
1617
bus: &'a Mutex<BUS>,
1718
cs: CS,
19+
delay: D,
1820
}
1921

20-
impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> {
22+
impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> {
2123
/// Create a new ExclusiveDevice
22-
pub fn new(bus: &'a Mutex<BUS>, cs: CS) -> Self {
23-
Self { bus, cs }
24+
pub fn new(bus: &'a Mutex<BUS>, cs: CS, delay: D) -> Self {
25+
Self { bus, cs, delay }
2426
}
2527
}
2628

27-
impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS>
29+
impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> {
30+
/// Create a new MutexDevice without support for in-transaction delays.
31+
///
32+
/// # Panics
33+
///
34+
/// The returned device will panic if you try to execute a transaction
35+
/// that contains any operations of type `Operation::DelayUs`.
36+
pub fn new_no_delay(bus: &'a Mutex<BUS>, cs: CS) -> Self {
37+
Self {
38+
bus,
39+
cs,
40+
delay: super::NoDelay,
41+
}
42+
}
43+
}
44+
45+
impl<'a, BUS, CS, D> ErrorType for MutexDevice<'a, BUS, CS, D>
2846
where
2947
BUS: ErrorType,
3048
CS: OutputPin,
3149
{
3250
type Error = DeviceError<BUS::Error, CS::Error>;
3351
}
3452

35-
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for MutexDevice<'a, BUS, CS>
53+
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for MutexDevice<'a, BUS, CS, D>
3654
where
3755
BUS: SpiBus<Word>,
3856
CS: OutputPin,
57+
D: DelayUs,
3958
{
4059
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
4160
let bus = &mut *self.bus.lock().unwrap();
@@ -47,6 +66,11 @@ where
4766
Operation::Write(buf) => bus.write(buf),
4867
Operation::Transfer(read, write) => bus.transfer(read, write),
4968
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
69+
Operation::DelayUs(us) => {
70+
bus.flush()?;
71+
self.delay.delay_us(*us);
72+
Ok(())
73+
}
5074
});
5175

5276
// On failure, it's important to still flush and deassert CS.

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::cell::RefCell;
2+
use embedded_hal::delay::DelayUs;
23
use embedded_hal::digital::OutputPin;
34
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
45

@@ -12,30 +13,48 @@ use super::DeviceError;
1213
/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`,
1314
/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several
1415
/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead.
15-
pub struct RefCellDevice<'a, BUS, CS> {
16+
pub struct RefCellDevice<'a, BUS, CS, D> {
1617
bus: &'a RefCell<BUS>,
1718
cs: CS,
19+
delay: D,
1820
}
1921

20-
impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> {
22+
impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> {
2123
/// Create a new ExclusiveDevice
22-
pub fn new(bus: &'a RefCell<BUS>, cs: CS) -> Self {
23-
Self { bus, cs }
24+
pub fn new(bus: &'a RefCell<BUS>, cs: CS, delay: D) -> Self {
25+
Self { bus, cs, delay }
2426
}
2527
}
2628

27-
impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS>
29+
impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> {
30+
/// Create a new RefCellDevice without support for in-transaction delays.
31+
///
32+
/// # Panics
33+
///
34+
/// The returned device will panic if you try to execute a transaction
35+
/// that contains any operations of type `Operation::DelayUs`.
36+
pub fn new_no_delay(bus: &'a RefCell<BUS>, cs: CS) -> Self {
37+
Self {
38+
bus,
39+
cs,
40+
delay: super::NoDelay,
41+
}
42+
}
43+
}
44+
45+
impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D>
2846
where
2947
BUS: ErrorType,
3048
CS: OutputPin,
3149
{
3250
type Error = DeviceError<BUS::Error, CS::Error>;
3351
}
3452

35-
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for RefCellDevice<'a, BUS, CS>
53+
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for RefCellDevice<'a, BUS, CS, D>
3654
where
3755
BUS: SpiBus<Word>,
3856
CS: OutputPin,
57+
D: DelayUs,
3958
{
4059
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
4160
let bus = &mut *self.bus.borrow_mut();
@@ -47,6 +66,11 @@ where
4766
Operation::Write(buf) => bus.write(buf),
4867
Operation::Transfer(read, write) => bus.transfer(read, write),
4968
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
69+
Operation::DelayUs(us) => {
70+
bus.flush()?;
71+
self.delay.delay_us(*us);
72+
Ok(())
73+
}
5074
});
5175

5276
// On failure, it's important to still flush and deassert CS.

0 commit comments

Comments
 (0)