Skip to content

Commit dcc96b9

Browse files
committed
WIP spi unify + device/bus traits.
1 parent 61285e4 commit dcc96b9

File tree

3 files changed

+102
-84
lines changed

3 files changed

+102
-84
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1414
- Fixed blanket impl of `DelayUs` not covering the `delay_ms` method.
1515
### Changed
1616
- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type.
17+
- `spi/blocking`: unified traits into `Read`, `Write`, `ReadWrite`.
18+
- `spi/blocking`: renamed Transactional `exec` to `batch`.
19+
- `spi/blocking`: Added `read_batch`, `write_batch` methods.
1720

1821
### Changed
1922
- `digital`: traits now enforce all impls on the same struct have the same `Error` type.

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@
406406
407407
#![deny(missing_docs)]
408408
#![no_std]
409+
#![feature(generic_associated_types)]
409410

410411
pub mod fmt;
411412
pub use nb;

src/spi/blocking.rs

+98-84
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,122 @@
11
//! Blocking SPI API
22
3-
use super::ErrorType;
3+
/// SPI device traits.
4+
pub mod device {
5+
use super::bus;
6+
use crate::spi::ErrorType;
47

5-
/// Blocking transfer with separate buffers
6-
pub trait Transfer<Word = u8>: ErrorType {
7-
/// Writes and reads simultaneously. `write` is written to the slave on MOSI and
8-
/// words received on MISO are stored in `read`.
8+
/// SPI device base trait
99
///
10-
/// It is allowed for `read` and `write` to have different lengths, even zero length.
11-
/// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
12-
/// incoming words after `read` has been filled will be discarded. If `write` is shorter,
13-
/// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
14-
/// typically `0x00`, `0xFF`, or configurable.
15-
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
16-
}
10+
/// If you're writing a driver, require [`SpiDevice`] (read-write), [`SpiReadonlyDevice`], [`SpiWriteonlyDevice`]
11+
/// to specify the kind of access to the device.
12+
pub trait SpiDeviceBase: ErrorType {
13+
/// Transaction type for this device.
14+
type Transaction<'a>: ErrorType<Error = Self::Error>
15+
where
16+
Self: 'a;
1717

18-
impl<T: Transfer<Word>, Word: Copy> Transfer<Word> for &mut T {
19-
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
20-
T::transfer(self, read, write)
18+
/// Start a transaction against this device.
19+
///
20+
/// This locks the bus, maybe asserts CS if [`ManagedChipSelect`] is implemented, and returns a
21+
/// [`Transaction`](SpiDeviceBase::Transaction) instance that you can then use to do transfers
22+
/// against the device.
23+
///
24+
/// The transaction lasts as long as the returned [`Transaction`](SpiDeviceBase::Transaction). Dropping
25+
/// it automatically ends the transaction, which deasserts CS (if [`ManagedChipSelect`]) and unlocks the bus.
26+
fn transaction<'a>(&'a mut self) -> Result<Self::Transaction<'a>, Self::Error>;
2127
}
22-
}
2328

24-
/// Blocking transfer with single buffer (in-place)
25-
pub trait TransferInplace<Word: Copy = u8>: ErrorType {
26-
/// Writes and reads simultaneously. The contents of `words` are
27-
/// written to the slave, and the received words are stored into the same
28-
/// `words` buffer, overwriting it.
29-
fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
30-
}
29+
/// Managed chip select marker trait.
30+
///
31+
/// If this trait is implemented for an SPI device, the chip select line
32+
/// is automatically managed by the implementation. In particular:
33+
///
34+
/// - CS is asserted (driven low) when calling [`SpiDeviceBase::transaction()`]
35+
/// - CS is deasserted (driven high) when dropping the returned [`SpiDeviceBase::Transaction`]
36+
pub trait ManagedChipSelect: SpiDeviceBase {}
3137

32-
impl<T: TransferInplace<Word>, Word: Copy> TransferInplace<Word> for &mut T {
33-
fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
34-
T::transfer_inplace(self, words)
38+
/// SPI device
39+
pub trait SpiDevice<Word: Copy = u8>: SpiDeviceBase
40+
where
41+
for<'a> Self::Transaction<'a>: bus::SpiBus<Word>,
42+
{
3543
}
36-
}
3744

38-
/// Blocking read
39-
pub trait Read<Word: Copy = u8>: ErrorType {
40-
/// Reads `words` from the slave.
41-
///
42-
/// The word value sent on MOSI during reading is implementation-defined,
43-
/// typically `0x00`, `0xFF`, or configurable.
44-
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
45-
}
45+
/// SPI device with read-only access
46+
pub trait SpiReadonlyDevice<Word: Copy = u8>: SpiDeviceBase
47+
where
48+
for<'a> Self::Transaction<'a>: bus::SpiReadonlyBus<Word>,
49+
{
50+
}
4651

47-
impl<T: Read<Word>, Word: Copy> Read<Word> for &mut T {
48-
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
49-
T::read(self, words)
52+
/// SPI device with read-write access
53+
pub trait SpiWriteonlyDevice<Word: Copy = u8>: SpiDeviceBase
54+
where
55+
for<'a> Self::Transaction<'a>: bus::SpiWriteonlyBus<Word>,
56+
{
5057
}
51-
}
5258

53-
/// Blocking write
54-
pub trait Write<Word: Copy = u8>: ErrorType {
55-
/// Writes `words` to the slave, ignoring all the incoming words
56-
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
59+
// TODO: blanket impls for &mut
5760
}
5861

59-
impl<T: Write<Word>, Word: Copy> Write<Word> for &mut T {
60-
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
61-
T::write(self, words)
62+
/// SPI bus traits.
63+
///
64+
/// Owning an instance of them means owning the entire bus.
65+
pub mod bus {
66+
use crate::spi::ErrorType;
67+
68+
/// Blocking read-only SPI
69+
pub trait SpiReadonlyBus<Word: Copy = u8>: ErrorType {
70+
/// Reads `words` from the slave.
71+
///
72+
/// The word value sent on MOSI during reading is implementation-defined,
73+
/// typically `0x00`, `0xFF`, or configurable.
74+
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
6275
}
63-
}
6476

65-
/// Blocking write (iterator version)
66-
pub trait WriteIter<Word: Copy = u8>: ErrorType {
67-
/// Writes `words` to the slave, ignoring all the incoming words
68-
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
69-
where
70-
WI: IntoIterator<Item = Word>;
71-
}
77+
impl<T: SpiReadonlyBus<Word>, Word: Copy> SpiReadonlyBus<Word> for &mut T {
78+
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
79+
T::read(self, words)
80+
}
81+
}
7282

73-
impl<T: WriteIter<Word>, Word: Copy> WriteIter<Word> for &mut T {
74-
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
75-
where
76-
WI: IntoIterator<Item = Word>,
77-
{
78-
T::write_iter(self, words)
83+
/// Blocking write-only SPI
84+
pub trait SpiWriteonlyBus<Word: Copy = u8>: ErrorType {
85+
/// Writes `words` to the slave, ignoring all the incoming words
86+
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
7987
}
80-
}
8188

82-
/// Operation for transactional SPI trait
83-
///
84-
/// This allows composition of SPI operations into a single bus transaction
85-
#[derive(Debug, PartialEq)]
86-
pub enum Operation<'a, Word: 'static + Copy = u8> {
87-
/// Read data into the provided buffer.
88-
Read(&'a mut [Word]),
89-
/// Write data from the provided buffer, discarding read data
90-
Write(&'a [Word]),
91-
/// Write data out while reading data into the provided buffer
92-
Transfer(&'a mut [Word], &'a [Word]),
93-
/// Write data out while reading data into the provided buffer
94-
TransferInplace(&'a mut [Word]),
95-
}
89+
impl<T: SpiWriteonlyBus<Word>, Word: Copy> SpiWriteonlyBus<Word> for &mut T {
90+
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
91+
T::write(self, words)
92+
}
93+
}
9694

97-
/// Transactional trait allows multiple actions to be executed
98-
/// as part of a single SPI transaction
99-
pub trait Transactional<Word: 'static + Copy = u8>: ErrorType {
100-
/// Execute the provided transactions
101-
fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error>;
102-
}
95+
/// Blocking read-write SPI
96+
pub trait SpiBus<Word: Copy = u8>: SpiReadonlyBus<Word> + SpiWriteonlyBus<Word> {
97+
/// Writes and reads simultaneously. `write` is written to the slave on MOSI and
98+
/// words received on MISO are stored in `read`.
99+
///
100+
/// It is allowed for `read` and `write` to have different lengths, even zero length.
101+
/// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
102+
/// incoming words after `read` has been filled will be discarded. If `write` is shorter,
103+
/// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
104+
/// typically `0x00`, `0xFF`, or configurable.
105+
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
106+
107+
/// Writes and reads simultaneously. The contents of `words` are
108+
/// written to the slave, and the received words are stored into the same
109+
/// `words` buffer, overwriting it.
110+
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
111+
}
112+
113+
impl<T: SpiBus<Word>, Word: Copy> SpiBus<Word> for &mut T {
114+
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
115+
T::transfer(self, read, write)
116+
}
103117

104-
impl<T: Transactional<Word>, Word: 'static + Copy> Transactional<Word> for &mut T {
105-
fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error> {
106-
T::exec(self, operations)
118+
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
119+
T::transfer_in_place(self, words)
120+
}
107121
}
108122
}

0 commit comments

Comments
 (0)