|
1 | 1 | //! Blocking SPI API
|
| 2 | +//! |
| 3 | +//! # Bus vs Device |
| 4 | +//! |
| 5 | +//! SPI allows sharing a single bus between many SPI devices. The SCK, MOSI and MISO lines are |
| 6 | +//! wired in parallel to all the devices, and each device gets a dedicated CS line from the MCU, like this: |
| 7 | +//! |
| 8 | +#![doc=include_str!("shared-bus.svg")] |
| 9 | +//! |
| 10 | +//! CS is usually active-low. When CS is high (not asserted), SPI devices ignore all incoming data, and |
| 11 | +//! don't drive MISO. When CS is low (asserted), the device is active: reacts to incoming data on MOSI and |
| 12 | +//! drives MISO with the response data. By asserting one CS or another, the MCU can choose to which |
| 13 | +//! SPI device it "talks" to on the shared bus. |
| 14 | +//! |
| 15 | +//! This bus sharing is common when having multiple SPI devices in the same board, since it uses fewer MCU |
| 16 | +//! pins (n+3 instead of 4*n), and fewer MCU SPI peripherals (1 instead of n). |
| 17 | +//! |
| 18 | +//! However, it poses a challenge when building portable drivers for SPI devices. The driver needs to |
| 19 | +//! be able to talk to its device on the bus, while not interfering with other drivers talking to other |
| 20 | +//! devices. |
| 21 | +//! |
| 22 | +//! To solve this, `embedded-hal` has two kinds of SPI traits: **SPI bus** and **SPI device**. |
| 23 | +//! |
| 24 | +//! ## Bus |
| 25 | +//! |
| 26 | +//! SPI bus traits represent **exclusive ownership** over the whole SPI bus. This is usually the entire |
| 27 | +//! SPI MCU peripheral, plus the SCK, MOSI and MISO pins. |
| 28 | +//! |
| 29 | +//! Owning an instance of an SPI bus guarantees exclusive access, this is, we have the guarantee no other |
| 30 | +//! piece of code will try to use the bus while we own it. |
| 31 | +//! |
| 32 | +//! There's 3 bus traits, depending on the bus capabilities. |
| 33 | +//! |
| 34 | +//! - [`SpiBus`]: Read-write access. This is the most commonly used. |
| 35 | +//! - [`SpiBusRead`]: Read-only access, for example a bus with a MISO pin but no MOSI pin. |
| 36 | +//! - [`SpiBusWrite`]: Read-write access, for example a bus with a MOSI pin but no MISO pin. |
| 37 | +//! |
| 38 | +//! ## Device |
| 39 | +//! |
| 40 | +//! [`SpiDevice`] represents **ownership over a single SPI device selected by a CS pin** in a (possibly shared) bus. This is typically: |
| 41 | +//! |
| 42 | +//! - Exclusive ownership of the **CS pin**. |
| 43 | +//! - Access to the **underlying SPI bus**. If shared, it'll be behind some kind of lock/mutex. |
| 44 | +//! |
| 45 | +//! An [`SpiDevice`] allows initiating [transactions](SpiDevice::transaction) against the target device on the bus. A transaction |
| 46 | +//! consists in asserting CS, then doing one or more transfers, then deasserting CS. For the entire duration of the transaction, the [`SpiDevice`] |
| 47 | +//! impl will ensure no other transaction can be opened on the same bus. This is the key that allows correct sharing of the bus. |
| 48 | +//! |
| 49 | +//! The capabilities of the bus (read-write, read-only or read-write) are determined by which of the [`SpiBus`], [`SpiBusRead`] [`SpiBusWrite`] |
| 50 | +//! are implemented for the [`Bus`](SpiDevice::Bus) associated type. |
| 51 | +//! |
| 52 | +//! # For driver authors |
| 53 | +//! |
| 54 | +//! When implementing a driver, it's crucial to pick the right trait, to ensure correct operation |
| 55 | +//! with maximum interoperability. Here are some guidelines depending on the device you're implementing a driver for: |
| 56 | +//! |
| 57 | +//! If your device **has a CS pin**, use [`SpiDevice`]. Do not manually manage the CS pin, the [`SpiDevice`] impl will do it for you. |
| 58 | +//! Add bounds like `where T::Bus: SpiBus`, `where T::Bus: SpiBusRead`, `where T::Bus: SpiBusWrite` to specify the kind of access you need. |
| 59 | +//! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus. |
| 60 | +//! |
| 61 | +//! ``` |
| 62 | +//! # use embedded_hal::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; |
| 63 | +//! pub struct MyDriver<SPI> { |
| 64 | +//! spi: SPI, |
| 65 | +//! } |
| 66 | +//! |
| 67 | +//! impl<SPI> MyDriver<SPI> |
| 68 | +//! where |
| 69 | +//! SPI: SpiDevice, |
| 70 | +//! SPI::Bus: SpiBus, // or SpiBusRead/SpiBusWrite if you only need to read or only write. |
| 71 | +//! { |
| 72 | +//! pub fn new(spi: SPI) -> Self { |
| 73 | +//! Self { spi } |
| 74 | +//! } |
| 75 | +//! |
| 76 | +//! pub fn read_foo(&mut self) -> [u8; 2] { |
| 77 | +//! let mut buf = [0; 2]; |
| 78 | +//! |
| 79 | +//! // `transaction` asserts and deasserts CS for us. No need to do it manually! |
| 80 | +//! self.spi.transaction(|bus| { |
| 81 | +//! bus.write(&[0x90]).unwrap(); |
| 82 | +//! bus.read(&mut buf).unwrap(); |
| 83 | +//! }).unwrap(); |
| 84 | +//! |
| 85 | +//! buf |
| 86 | +//! } |
| 87 | +//! } |
| 88 | +//! ``` |
| 89 | +//! |
| 90 | +//! If your device **does not have a CS pin**, use [`SpiBus`] (or [`SpiBusRead`], [`SpiBusWrite`]). This will ensure |
| 91 | +//! your driver has exclusive access to the bus, so no other drivers can interfere. It's not possible to safely share |
| 92 | +//! a bus without CS pins. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. |
| 93 | +//! |
| 94 | +//! ``` |
| 95 | +//! # use embedded_hal::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite}; |
| 96 | +//! pub struct MyDriver<SPI> { |
| 97 | +//! spi: SPI, |
| 98 | +//! } |
| 99 | +//! |
| 100 | +//! impl<SPI> MyDriver<SPI> |
| 101 | +//! where |
| 102 | +//! SPI: SpiBus, // or SpiBusRead/SpiBusWrite if you only need to read or only write. |
| 103 | +//! { |
| 104 | +//! pub fn new(spi: SPI) -> Self { |
| 105 | +//! Self { spi } |
| 106 | +//! } |
| 107 | +//! |
| 108 | +//! pub fn read_foo(&mut self) -> [u8; 2] { |
| 109 | +//! let mut buf = [0; 2]; |
| 110 | +//! self.spi.write(&[0x90]).unwrap(); |
| 111 | +//! self.spi.read(&mut buf).unwrap(); |
| 112 | +//! buf |
| 113 | +//! } |
| 114 | +//! } |
| 115 | +//! ``` |
| 116 | +//! |
| 117 | +//! If you're (ab)using SPI to **implement other protocols** by bitbanging (WS2812B, onewire, generating arbitrary waveforms...), use [`SpiBus`]. |
| 118 | +//! SPI bus sharing doesn't make sense at all in this case. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. |
| 119 | +//! |
| 120 | +//! # For HAL authors |
| 121 | +//! |
| 122 | +//! HALs **must** implement [`SpiBus`], [`SpiBusRead`], [`SpiBusWrite`]. Users can combine the bus together with the CS pin (which should |
| 123 | +//! impl [`OutputPin`]) using HAL-independent [`SpiDevice`] impls such as [`ExclusiveDevice`]. |
| 124 | +//! |
| 125 | +//! HALs may additionally implement [`SpiDevice`] to **take advantage of hardware CS management**, which may provide some performance |
| 126 | +//! benefits. (There's no point in a HAL implementing [`SpiDevice`] if the CS management is software-only, this task is better left to |
| 127 | +//! the HAL-independent implementations). |
| 128 | +//! |
| 129 | +//! HALs **must not** add infrastructure for sharing at the [`SpiBus`] level. User code owning a [`SpiBus`] must have the guarantee |
| 130 | +//! of exclusive access. |
2 | 131 |
|
3 |
| -use super::ErrorType; |
| 132 | +use core::fmt::Debug; |
4 | 133 |
|
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`. |
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 |
| -} |
| 134 | +use crate::{digital::blocking::OutputPin, spi::ErrorType}; |
17 | 135 |
|
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) |
21 |
| - } |
22 |
| -} |
| 136 | +use super::{Error, ErrorKind}; |
23 | 137 |
|
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>; |
| 138 | +/// SPI device trait |
| 139 | +/// |
| 140 | +/// SpiDevice represents ownership over a single SPI device on a (possibly shared) bus, selected |
| 141 | +/// with a CS pin. |
| 142 | +/// |
| 143 | +/// See the [module-level documentation](self) for important usage information. |
| 144 | +pub trait SpiDevice: ErrorType { |
| 145 | + /// SPI Bus type for this device. |
| 146 | + type Bus: ErrorType; |
| 147 | + |
| 148 | + /// Start a transaction against the device. |
| 149 | + /// |
| 150 | + /// - Locks the bus |
| 151 | + /// - Asserts the CS (Chip Select) pin. |
| 152 | + /// - Calls `f` with an exclusive reference to the bus, which can then be used to do transfers against the device. |
| 153 | + /// - Deasserts the CS pin. |
| 154 | + /// - Unlocks the bus, |
| 155 | + /// |
| 156 | + /// The lock mechanism is implementation-defined. The only requirement is it must prevent two |
| 157 | + /// transactions from executing concurrently against the same bus. Examples of implementations are: |
| 158 | + /// critical sections, blocking mutexes, or returning an error or panicking if the bus is already busy. |
| 159 | + fn transaction<R>(&mut self, f: impl FnOnce(&mut Self::Bus) -> R) -> Result<R, Self::Error>; |
30 | 160 | }
|
31 | 161 |
|
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) |
| 162 | +impl<T: SpiDevice> SpiDevice for &mut T { |
| 163 | + type Bus = T::Bus; |
| 164 | + fn transaction<R>(&mut self, f: impl FnOnce(&mut Self::Bus) -> R) -> Result<R, Self::Error> { |
| 165 | + T::transaction(self, f) |
35 | 166 | }
|
36 | 167 | }
|
37 | 168 |
|
38 |
| -/// Blocking read |
39 |
| -pub trait Read<Word: Copy = u8>: ErrorType { |
| 169 | +/// Read-only SPI bus |
| 170 | +pub trait SpiBusRead<Word: Copy = u8>: ErrorType { |
40 | 171 | /// Reads `words` from the slave.
|
41 | 172 | ///
|
42 | 173 | /// The word value sent on MOSI during reading is implementation-defined,
|
43 | 174 | /// typically `0x00`, `0xFF`, or configurable.
|
44 | 175 | fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
|
45 | 176 | }
|
46 | 177 |
|
47 |
| -impl<T: Read<Word>, Word: Copy> Read<Word> for &mut T { |
| 178 | +impl<T: SpiBusRead<Word>, Word: Copy> SpiBusRead<Word> for &mut T { |
48 | 179 | fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
49 | 180 | T::read(self, words)
|
50 | 181 | }
|
51 | 182 | }
|
52 | 183 |
|
53 |
| -/// Blocking write |
54 |
| -pub trait Write<Word: Copy = u8>: ErrorType { |
| 184 | +/// Write-only SPI bus |
| 185 | +pub trait SpiBusWrite<Word: Copy = u8>: ErrorType { |
55 | 186 | /// Writes `words` to the slave, ignoring all the incoming words
|
56 | 187 | fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
|
57 | 188 | }
|
58 | 189 |
|
59 |
| -impl<T: Write<Word>, Word: Copy> Write<Word> for &mut T { |
| 190 | +impl<T: SpiBusWrite<Word>, Word: Copy> SpiBusWrite<Word> for &mut T { |
60 | 191 | fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
|
61 | 192 | T::write(self, words)
|
62 | 193 | }
|
63 | 194 | }
|
64 | 195 |
|
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>; |
| 196 | +/// Read-write SPI bus |
| 197 | +/// |
| 198 | +/// SpiBus represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. |
| 199 | +/// |
| 200 | +/// See the [module-level documentation](self) for important information on SPI Bus vs Device traits. |
| 201 | +pub trait SpiBus<Word: Copy = u8>: SpiBusRead<Word> + SpiBusWrite<Word> { |
| 202 | + /// Writes and reads simultaneously. `write` is written to the slave on MOSI and |
| 203 | + /// words received on MISO are stored in `read`. |
| 204 | + /// |
| 205 | + /// It is allowed for `read` and `write` to have different lengths, even zero length. |
| 206 | + /// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter, |
| 207 | + /// incoming words after `read` has been filled will be discarded. If `write` is shorter, |
| 208 | + /// the value of words sent in MOSI after all `write` has been sent is implementation-defined, |
| 209 | + /// typically `0x00`, `0xFF`, or configurable. |
| 210 | + fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; |
| 211 | + |
| 212 | + /// Writes and reads simultaneously. The contents of `words` are |
| 213 | + /// written to the slave, and the received words are stored into the same |
| 214 | + /// `words` buffer, overwriting it. |
| 215 | + fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; |
71 | 216 | }
|
72 | 217 |
|
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) |
| 218 | +impl<T: SpiBus<Word>, Word: Copy> SpiBus<Word> for &mut T { |
| 219 | + fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { |
| 220 | + T::transfer(self, read, write) |
| 221 | + } |
| 222 | + |
| 223 | + fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { |
| 224 | + T::transfer_in_place(self, words) |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +/// Error type for [`ExclusiveDevice`] operations. |
| 229 | +#[derive(Copy, Clone, Eq, PartialEq, Debug)] |
| 230 | +pub enum ExclusiveDeviceError<BUS, CS> { |
| 231 | + /// An inner SPI bus operation failed |
| 232 | + Spi(BUS), |
| 233 | + /// Asserting or deasserting CS failed |
| 234 | + Cs(CS), |
| 235 | +} |
| 236 | + |
| 237 | +impl<BUS, CS> Error for ExclusiveDeviceError<BUS, CS> |
| 238 | +where |
| 239 | + BUS: Error + Debug, |
| 240 | + CS: Debug, |
| 241 | +{ |
| 242 | + fn kind(&self) -> ErrorKind { |
| 243 | + match self { |
| 244 | + Self::Spi(e) => e.kind(), |
| 245 | + Self::Cs(_) => ErrorKind::Other, |
| 246 | + } |
79 | 247 | }
|
80 | 248 | }
|
81 | 249 |
|
82 |
| -/// Operation for transactional SPI trait |
| 250 | +/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). |
83 | 251 | ///
|
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]), |
| 252 | +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], |
| 253 | +/// ideal for when no sharing is required (only one SPI device is present on the bus). |
| 254 | +pub struct ExclusiveDevice<BUS, CS> { |
| 255 | + bus: BUS, |
| 256 | + cs: CS, |
| 257 | +} |
| 258 | + |
| 259 | +impl<BUS, CS> ExclusiveDevice<BUS, CS> { |
| 260 | + /// Create a new ExclusiveDevice |
| 261 | + pub fn new(bus: BUS, cs: CS) -> Self { |
| 262 | + Self { bus, cs } |
| 263 | + } |
95 | 264 | }
|
96 | 265 |
|
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>; |
| 266 | +impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS> |
| 267 | +where |
| 268 | + BUS: ErrorType, |
| 269 | + CS: OutputPin, |
| 270 | +{ |
| 271 | + type Error = ExclusiveDeviceError<BUS::Error, CS::Error>; |
102 | 272 | }
|
103 | 273 |
|
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) |
| 274 | +impl<BUS, CS> SpiDevice for ExclusiveDevice<BUS, CS> |
| 275 | +where |
| 276 | + BUS: ErrorType, |
| 277 | + CS: OutputPin, |
| 278 | +{ |
| 279 | + type Bus = BUS; |
| 280 | + |
| 281 | + fn transaction<R>(&mut self, f: impl FnOnce(&mut Self::Bus) -> R) -> Result<R, Self::Error> { |
| 282 | + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; |
| 283 | + let res = f(&mut self.bus); |
| 284 | + self.cs.set_high().map_err(ExclusiveDeviceError::Cs)?; |
| 285 | + Ok(res) |
107 | 286 | }
|
108 | 287 | }
|
0 commit comments