|
1 | 1 | //! Blocking SPI API
|
2 | 2 |
|
3 |
| -use super::ErrorType; |
| 3 | +/// SPI device traits. |
| 4 | +pub mod device { |
| 5 | + use super::bus; |
| 6 | + use crate::spi::ErrorType; |
4 | 7 |
|
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 |
9 | 9 | ///
|
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; |
17 | 17 |
|
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>; |
21 | 27 | }
|
22 |
| -} |
23 | 28 |
|
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 {} |
31 | 37 |
|
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 | + { |
35 | 43 | }
|
36 |
| -} |
37 | 44 |
|
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 | + } |
46 | 51 |
|
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 | + { |
50 | 57 | }
|
51 |
| -} |
52 | 58 |
|
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 |
57 | 60 | }
|
58 | 61 |
|
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>; |
62 | 75 | }
|
63 |
| -} |
64 | 76 |
|
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 | + } |
72 | 82 |
|
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>; |
79 | 87 | }
|
80 |
| -} |
81 | 88 |
|
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 | + } |
96 | 94 |
|
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 | + } |
103 | 117 |
|
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 | + } |
107 | 121 | }
|
108 | 122 | }
|
0 commit comments