Skip to content

Commit 75e2fcf

Browse files
committed
Split Spidev into device and bus structs. Closes rust-embedded#99
1 parent 27daf71 commit 75e2fcf

File tree

2 files changed

+102
-15
lines changed

2 files changed

+102
-15
lines changed

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ pub use crate::delay::Delay;
5353
pub use crate::i2c::{I2CError, I2cdev};
5454
pub use crate::serial::{Serial, SerialError};
5555
#[cfg(feature = "spi")]
56-
pub use crate::spi::{SPIError, Spidev};
56+
pub use crate::spi::{SPIError, SpidevBus, SpidevDevice};
5757
pub use crate::timer::{CountDown, Periodic, SysTimer};

src/spi.rs

+101-14
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,118 @@ use std::io;
99
use std::ops;
1010
use std::path::Path;
1111

12-
/// Newtype around [`spidev::Spidev`] that implements the `embedded-hal` traits
12+
/// Spidev wrapper providing the embedded-hal [`SpiDevice`] trait.
1313
///
14-
/// [Delay operations][delay] are capped to 65535 microseconds.
14+
/// Use this struct when you want a single spidev device, using a Linux-managed CS pin,
15+
/// which is already defined in your devicetree. Linux will handle sharing the bus
16+
/// between different SPI devices, even between different processes.
1517
///
16-
/// [`spidev::Spidev`]: https://docs.rs/spidev/0.5.2/spidev/struct.Spidev.html
17-
/// [delay]: embedded_hal::spi::Operation::DelayUs
18-
pub struct Spidev(pub spidev::Spidev);
18+
/// You get an object that implements [`SpiDevice`], which is what most drivers require,
19+
/// but does not implement [`SpiBus`]. In some rare cases, you may require [`SpiBus`]
20+
/// instead; for that refer to [`SpidevBus`] below. You may also want to use [`SpiBus`]
21+
/// if you want to handle all the CS pins yourself using GPIO.
22+
///
23+
/// This struct wraps a [`spidev::Spidev`] struct, so it can be constructed directly
24+
/// and the inner struct accessed if needed, for example to (re)configure the SPI settings.
25+
///
26+
/// Note that [delay operations] on this device are capped to 65535 microseconds.
27+
///
28+
/// [`SpiDevice`]: embedded_hal::spi::SpiDevice
29+
/// [`SpiBus`]: embedded_hal::spi::SpiBus
30+
/// [`spidev::Spidev`]: spidev::Spidev
31+
/// [delay operations]: embedded_hal::spi::Operation::DelayUs
32+
pub struct SpidevDevice(pub spidev::Spidev);
33+
34+
/// Spidev wrapper providing the embedded-hal [`SpiBus`] trait.
35+
///
36+
/// Use this struct when you require direct access to the underlying SPI bus, for
37+
/// example when you want to use GPIOs as software-controlled CS pins to share the
38+
/// bus with multiple devices, or because a driver requires the entire bus (for
39+
/// example to drive smart LEDs).
40+
///
41+
/// Do not use this struct if you're accessing SPI devices that already appear in your
42+
/// device tree; you will not be able to drive CS pins that are already used by spidev
43+
/// as GPIOs. Instead use [`SpidevDevice`].
44+
///
45+
/// This struct must still be created from a [`spidev::Spidev`] device, but there are two
46+
/// important notes:
47+
///
48+
/// 1. The CS pin associated with this spidev device will be driven whenever any device accesses
49+
/// this bus, so it should be an unconnected or unused pin.
50+
/// 2. No other spidev device on the same bus may be used as long as this `SpidevBus` exists,
51+
/// as Linux will _not_ do anything to ensure this bus has exclusive access.
52+
///
53+
/// It is recommended to use a dummy spidev device associated with an unused CS pin, and then use
54+
/// regular GPIOs as CS pins if required. If you are planning to share this bus using GPIOs, the
55+
/// [`embedded-hal-bus`] crate may be of interest.
56+
///
57+
/// If necessary, you can [configure] the underlying [`spidev::Spidev`] instance with the
58+
/// [`SPI_NO_CS`] flag set to prevent any CS pin activity.
59+
///
60+
/// [`SpiDevice`]: embedded_hal::spi::SpiDevice
61+
/// [`SpiBus`]: embedded_hal::spi::SpiBus
62+
/// [`embedded-hal-bus`]: https://docs.rs/embedded-hal-bus/
63+
/// [`spidev::Spidev`]: spidev::Spidev
64+
/// [delay operations]: embedded_hal::spi::Operation::DelayUs
65+
/// [configure]: spidev::Spidev::configure
66+
/// [`SPI_NO_CS`]: spidev::SpiModeFlags::SPI_NO_CS
67+
pub struct SpidevBus(pub spidev::Spidev);
68+
69+
impl SpidevDevice {
70+
/// See [`spidev::Spidev::open`] for details.
71+
///
72+
/// The provided `path` is for the specific device you wish to access.
73+
/// Access to the bus is shared with other devices via the Linux kernel.
74+
pub fn open<P>(path: P) -> Result<Self, SPIError>
75+
where
76+
P: AsRef<Path>,
77+
{
78+
spidev::Spidev::open(path)
79+
.map(SpidevDevice)
80+
.map_err(|e| e.into())
81+
}
82+
}
1983

20-
impl Spidev {
21-
/// See [`spidev::Spidev::open`][0] for details.
84+
impl SpidevBus {
85+
/// See [`spidev::Spidev::open`] for details.
2286
///
23-
/// [0]: https://docs.rs/spidev/0.5.2/spidev/struct.Spidev.html#method.open
87+
/// The provided `path` must be the _only_ device in use on its bus,
88+
/// and note its own CS pin will be asserted for all device access,
89+
/// so the path should be to a dummy device used only to access
90+
/// the underlying bus.
2491
pub fn open<P>(path: P) -> Result<Self, SPIError>
2592
where
2693
P: AsRef<Path>,
2794
{
28-
spidev::Spidev::open(path).map(Spidev).map_err(|e| e.into())
95+
spidev::Spidev::open(path)
96+
.map(SpidevBus)
97+
.map_err(|e| e.into())
2998
}
3099
}
31100

32-
impl ops::Deref for Spidev {
101+
impl ops::Deref for SpidevDevice {
33102
type Target = spidev::Spidev;
34103

35104
fn deref(&self) -> &Self::Target {
36105
&self.0
37106
}
38107
}
39108

40-
impl ops::DerefMut for Spidev {
109+
impl ops::DerefMut for SpidevDevice {
110+
fn deref_mut(&mut self) -> &mut Self::Target {
111+
&mut self.0
112+
}
113+
}
114+
115+
impl ops::Deref for SpidevBus {
116+
type Target = spidev::Spidev;
117+
118+
fn deref(&self) -> &Self::Target {
119+
&self.0
120+
}
121+
}
122+
123+
impl ops::DerefMut for SpidevBus {
41124
fn deref_mut(&mut self) -> &mut Self::Target {
42125
&mut self.0
43126
}
@@ -51,11 +134,15 @@ mod embedded_hal_impl {
51134
use std::convert::TryInto;
52135
use std::io::{Read, Write};
53136

54-
impl ErrorType for Spidev {
137+
impl ErrorType for SpidevDevice {
138+
type Error = SPIError;
139+
}
140+
141+
impl ErrorType for SpidevBus {
55142
type Error = SPIError;
56143
}
57144

58-
impl SpiBus<u8> for Spidev {
145+
impl SpiBus<u8> for SpidevBus {
59146
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
60147
self.0.read_exact(words).map_err(|err| SPIError { err })
61148
}
@@ -97,7 +184,7 @@ mod embedded_hal_impl {
97184
}
98185
}
99186

100-
impl SpiDevice for Spidev {
187+
impl SpiDevice for SpidevDevice {
101188
/// Perform a transaction against the device. [Read more][transaction]
102189
///
103190
/// [Delay operations][delay] are capped to 65535 microseconds.

0 commit comments

Comments
 (0)