embedded_hal/spi.rs
1//! Blocking SPI master mode traits.
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 chip-select (CS) line from the MCU, like this:
7//!
8#![doc= include_str!("spi-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 (possibly 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//! The [`SpiBus`] trait represents **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//! ## Device
33//!
34//! The [`SpiDevice`] trait represents **ownership over a single SPI device selected by a CS pin** in a (possibly shared) bus. This is typically:
35//!
36//! - Exclusive ownership of the **CS pin**.
37//! - Access to the **underlying SPI bus**. If shared, it'll be behind some kind of lock/mutex.
38//!
39//! An [`SpiDevice`] allows initiating [transactions](SpiDevice::transaction) against the target device on the bus. A transaction
40//! consists of asserting CS, then doing one or more transfers, then deasserting CS. For the entire duration of the transaction, the [`SpiDevice`]
41//! implementation will ensure no other transaction can be opened on the same bus. This is the key that allows correct sharing of the bus.
42//!
43//! # For driver authors
44//!
45//! When implementing a driver, it's crucial to pick the right trait, to ensure correct operation
46//! with maximum interoperability. Here are some guidelines depending on the device you're implementing a driver for:
47//!
48//! If your device **has a CS pin**, use [`SpiDevice`]. Do not manually
49//! manage the CS pin, the [`SpiDevice`] implementation will do it for you.
50//! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus.
51//!
52//! ```
53//! # use embedded_hal::spi::{SpiBus, SpiDevice, Operation};
54//! pub struct MyDriver<SPI> {
55//! spi: SPI,
56//! }
57//!
58//! impl<SPI> MyDriver<SPI>
59//! where
60//! SPI: SpiDevice,
61//! {
62//! pub fn new(spi: SPI) -> Self {
63//! Self { spi }
64//! }
65//!
66//! pub fn read_foo(&mut self) -> Result<[u8; 2], MyError<SPI::Error>> {
67//! let mut buf = [0; 2];
68//!
69//! // `transaction` asserts and deasserts CS for us. No need to do it manually!
70//! self.spi.transaction(&mut [
71//! Operation::Write(&[0x90]),
72//! Operation::Read(&mut buf),
73//! ]).map_err(MyError::Spi)?;
74//!
75//! Ok(buf)
76//! }
77//! }
78//!
79//! #[derive(Copy, Clone, Debug)]
80//! enum MyError<SPI> {
81//! Spi(SPI),
82//! // Add other errors for your driver here.
83//! }
84//! ```
85//!
86//! If your device **does not have a CS pin**, use [`SpiBus`]. This will ensure
87//! your driver has exclusive access to the bus, so no other drivers can interfere. It's not possible to safely share
88//! a bus without CS pins. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation.
89//!
90//! ```
91//! # use embedded_hal::spi::SpiBus;
92//! pub struct MyDriver<SPI> {
93//! spi: SPI,
94//! }
95//!
96//! impl<SPI> MyDriver<SPI>
97//! where
98//! SPI: SpiBus,
99//! {
100//! pub fn new(spi: SPI) -> Self {
101//! Self { spi }
102//! }
103//!
104//! pub fn read_foo(&mut self) -> Result<[u8; 2], MyError<SPI::Error>> {
105//! let mut buf = [0; 2];
106//! self.spi.write(&[0x90]).map_err(MyError::Spi)?;
107//! self.spi.read(&mut buf).map_err(MyError::Spi)?;
108//! Ok(buf)
109//! }
110//! }
111//!
112//! #[derive(Copy, Clone, Debug)]
113//! enum MyError<SPI> {
114//! Spi(SPI),
115//! // Add other errors for your driver here.
116//! }
117//! ```
118//!
119//! If you're (ab)using SPI to **implement other protocols** by bitbanging (WS2812B, onewire, generating arbitrary waveforms...), use [`SpiBus`].
120//! SPI bus sharing doesn't make sense at all in this case. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation.
121//!
122//! # For HAL authors
123//!
124//! HALs **must** implement [`SpiBus`]. Users can combine the bus together with the CS pin (which should
125//! implement [`OutputPin`](crate::digital::OutputPin)) using HAL-independent [`SpiDevice`] implementations such as the ones in [`embedded-hal-bus`](https://crates.io/crates/embedded-hal-bus).
126//!
127//! HALs may additionally implement [`SpiDevice`] to **take advantage of hardware CS management**, which may provide some performance
128//! benefits. (There's no point in a HAL implementing [`SpiDevice`] if the CS management is software-only, this task is better left to
129//! the HAL-independent implementations).
130//!
131//! HALs **must not** add infrastructure for sharing at the [`SpiBus`] level. User code owning a [`SpiBus`] must have the guarantee
132//! of exclusive access.
133//!
134//! # Flushing
135//!
136//! To improve performance, [`SpiBus`] implementations are allowed to return before the operation is finished, i.e. when the bus is still not
137//! idle. This allows pipelining SPI transfers with CPU work.
138//!
139//! When calling another method when a previous operation is still in progress, implementations can either wait for the previous operation
140//! to finish, or enqueue the new one, but they must not return a "busy" error. Users must be able to do multiple method calls in a row
141//! and have them executed "as if" they were done sequentially, without having to check for "busy" errors.
142//!
143//! When using a [`SpiBus`], call [`flush`](SpiBus::flush) to wait for operations to actually finish. Examples of situations
144//! where this is needed are:
145//! - To synchronize SPI activity and GPIO activity, for example before deasserting a CS pin.
146//! - Before deinitializing the hardware SPI peripheral.
147//!
148//! When using a [`SpiDevice`], you can still call [`flush`](SpiBus::flush) on the bus within a transaction.
149//! It's very rarely needed, because [`transaction`](SpiDevice::transaction) already flushes for you
150//! before deasserting CS. For example, you may need it to synchronize with GPIOs other than CS, such as DCX pins
151//! sometimes found in SPI displays.
152//!
153//! For example, for [`write`](SpiBus::write) operations, it is common for hardware SPI peripherals to have a small
154//! FIFO buffer, usually 1-4 bytes. Software writes data to the FIFO, and the peripheral sends it on MOSI at its own pace,
155//! at the specified SPI frequency. It is allowed for an implementation of [`write`](SpiBus::write) to return as soon
156//! as all the data has been written to the FIFO, before it is actually sent. Calling [`flush`](SpiBus::flush) would
157//! wait until all the bits have actually been sent, the FIFO is empty, and the bus is idle.
158//!
159//! This still applies to other operations such as [`read`](SpiBus::read) or [`transfer`](SpiBus::transfer). It is less obvious
160//! why, because these methods can't return before receiving all the read data. However it's still technically possible
161//! for them to return before the bus is idle. For example, assuming SPI mode 0, the last bit is sampled on the first (rising) edge
162//! of SCK, at which point a method could return, but the second (falling) SCK edge still has to happen before the bus is idle.
163//!
164//! # CS-to-clock delays
165//!
166//! Many chips require a minimum delay between asserting CS and the first SCK edge, and the last SCK edge and deasserting CS.
167//! Drivers should *NOT* use [`Operation::DelayNs`] for this, they should instead document that the user should configure the
168//! delays when creating the `SpiDevice` instance, same as they have to configure the SPI frequency and mode. This has a few advantages:
169//!
170//! - Allows implementations that use hardware-managed CS to program the delay in hardware
171//! - Allows the end user more flexibility. For example, they can choose to not configure any delay if their MCU is slow
172//! enough to "naturally" do the delay (very common if the delay is in the order of nanoseconds).
173
174use core::fmt::Debug;
175
176#[cfg(feature = "defmt-03")]
177use crate::defmt;
178
179/// Clock polarity.
180#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
182pub enum Polarity {
183 /// Clock signal low when idle.
184 IdleLow,
185 /// Clock signal high when idle.
186 IdleHigh,
187}
188
189/// Clock phase.
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
191#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
192pub enum Phase {
193 /// Data in "captured" on the first clock transition.
194 CaptureOnFirstTransition,
195 /// Data in "captured" on the second clock transition.
196 CaptureOnSecondTransition,
197}
198
199/// SPI mode.
200#[derive(Clone, Copy, Debug, PartialEq, Eq)]
201#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
202pub struct Mode {
203 /// Clock polarity.
204 pub polarity: Polarity,
205 /// Clock phase.
206 pub phase: Phase,
207}
208
209/// Helper for CPOL = 0, CPHA = 0.
210pub const MODE_0: Mode = Mode {
211 polarity: Polarity::IdleLow,
212 phase: Phase::CaptureOnFirstTransition,
213};
214
215/// Helper for CPOL = 0, CPHA = 1.
216pub const MODE_1: Mode = Mode {
217 polarity: Polarity::IdleLow,
218 phase: Phase::CaptureOnSecondTransition,
219};
220
221/// Helper for CPOL = 1, CPHA = 0.
222pub const MODE_2: Mode = Mode {
223 polarity: Polarity::IdleHigh,
224 phase: Phase::CaptureOnFirstTransition,
225};
226
227/// Helper for CPOL = 1, CPHA = 1.
228pub const MODE_3: Mode = Mode {
229 polarity: Polarity::IdleHigh,
230 phase: Phase::CaptureOnSecondTransition,
231};
232
233/// SPI error.
234pub trait Error: Debug {
235 /// Convert error to a generic SPI error kind.
236 ///
237 /// By using this method, SPI errors freely defined by HAL implementations
238 /// can be converted to a set of generic SPI errors upon which generic
239 /// code can act.
240 fn kind(&self) -> ErrorKind;
241}
242
243impl Error for core::convert::Infallible {
244 #[inline]
245 fn kind(&self) -> ErrorKind {
246 match *self {}
247 }
248}
249
250/// SPI error kind.
251///
252/// This represents a common set of SPI operation errors. HAL implementations are
253/// free to define more specific or additional error types. However, by providing
254/// a mapping to these common SPI errors, generic code can still react to them.
255#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
256#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
257#[non_exhaustive]
258pub enum ErrorKind {
259 /// The peripheral receive buffer was overrun.
260 Overrun,
261 /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup.
262 ModeFault,
263 /// Received data does not conform to the peripheral configuration.
264 FrameFormat,
265 /// An error occurred while asserting or deasserting the Chip Select pin.
266 ChipSelectFault,
267 /// A different error occurred. The original error may contain more information.
268 Other,
269}
270
271impl Error for ErrorKind {
272 #[inline]
273 fn kind(&self) -> ErrorKind {
274 *self
275 }
276}
277
278impl core::fmt::Display for ErrorKind {
279 #[inline]
280 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281 match self {
282 Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
283 Self::ModeFault => write!(
284 f,
285 "Multiple devices on the SPI bus are trying to drive the slave select pin"
286 ),
287 Self::FrameFormat => write!(
288 f,
289 "Received data does not conform to the peripheral configuration"
290 ),
291 Self::ChipSelectFault => write!(
292 f,
293 "An error occurred while asserting or deasserting the Chip Select pin"
294 ),
295 Self::Other => write!(
296 f,
297 "A different error occurred. The original error may contain more information"
298 ),
299 }
300 }
301}
302
303/// SPI error type trait.
304///
305/// This just defines the error type, to be used by the other SPI traits.
306pub trait ErrorType {
307 /// Error type.
308 type Error: Error;
309}
310
311impl<T: ErrorType + ?Sized> ErrorType for &mut T {
312 type Error = T::Error;
313}
314
315/// SPI transaction operation.
316///
317/// This allows composition of SPI operations into a single bus transaction.
318#[derive(Debug, PartialEq, Eq)]
319#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
320pub enum Operation<'a, Word: 'static> {
321 /// Read data into the provided buffer.
322 ///
323 /// Equivalent to [`SpiBus::read`].
324 Read(&'a mut [Word]),
325 /// Write data from the provided buffer, discarding read data.
326 ///
327 /// Equivalent to [`SpiBus::write`].
328 Write(&'a [Word]),
329 /// Read data into the first buffer, while writing data from the second buffer.
330 ///
331 /// Equivalent to [`SpiBus::transfer`].
332 Transfer(&'a mut [Word], &'a [Word]),
333 /// Write data out while reading data into the provided buffer.
334 ///
335 /// Equivalent to [`SpiBus::transfer_in_place`].
336 TransferInPlace(&'a mut [Word]),
337 /// Delay for at least the specified number of nanoseconds.
338 DelayNs(u32),
339}
340
341/// SPI device trait.
342///
343/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
344/// with a CS (Chip Select) pin.
345///
346/// See the [module-level documentation](self) for important usage information.
347pub trait SpiDevice<Word: Copy + 'static = u8>: ErrorType {
348 /// Perform a transaction against the device.
349 ///
350 /// - Locks the bus
351 /// - Asserts the CS (Chip Select) pin.
352 /// - Performs all the operations.
353 /// - [Flushes](SpiBus::flush) the bus.
354 /// - Deasserts the CS pin.
355 /// - Unlocks the bus.
356 ///
357 /// The locking mechanism is implementation-defined. The only requirement is it must prevent two
358 /// transactions from executing concurrently against the same bus. Examples of implementations are:
359 /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy.
360 ///
361 /// On bus errors the implementation should try to deassert CS.
362 /// If an error occurs while deasserting CS the bus error should take priority as the return value.
363 fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>;
364
365 /// Do a read within a transaction.
366 ///
367 /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Read(buf)])`.
368 ///
369 /// See also: [`SpiDevice::transaction`], [`SpiBus::read`]
370 #[inline]
371 fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> {
372 self.transaction(&mut [Operation::Read(buf)])
373 }
374
375 /// Do a write within a transaction.
376 ///
377 /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Write(buf)])`.
378 ///
379 /// See also: [`SpiDevice::transaction`], [`SpiBus::write`]
380 #[inline]
381 fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> {
382 self.transaction(&mut [Operation::Write(buf)])
383 }
384
385 /// Do a transfer within a transaction.
386 ///
387 /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)]`.
388 ///
389 /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`]
390 #[inline]
391 fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
392 self.transaction(&mut [Operation::Transfer(read, write)])
393 }
394
395 /// Do an in-place transfer within a transaction.
396 ///
397 /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)]`.
398 ///
399 /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`]
400 #[inline]
401 fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> {
402 self.transaction(&mut [Operation::TransferInPlace(buf)])
403 }
404}
405
406impl<Word: Copy + 'static, T: SpiDevice<Word> + ?Sized> SpiDevice<Word> for &mut T {
407 #[inline]
408 fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
409 T::transaction(self, operations)
410 }
411
412 #[inline]
413 fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> {
414 T::read(self, buf)
415 }
416
417 #[inline]
418 fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> {
419 T::write(self, buf)
420 }
421
422 #[inline]
423 fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
424 T::transfer(self, read, write)
425 }
426
427 #[inline]
428 fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> {
429 T::transfer_in_place(self, buf)
430 }
431}
432
433/// SPI bus.
434///
435/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins.
436///
437/// See the [module-level documentation](self) for important information on SPI Bus vs Device traits.
438pub trait SpiBus<Word: Copy + 'static = u8>: ErrorType {
439 /// Read `words` from the slave.
440 ///
441 /// The word value sent on MOSI during reading is implementation-defined,
442 /// typically `0x00`, `0xFF`, or configurable.
443 ///
444 /// Implementations are allowed to return before the operation is
445 /// complete. See the [module-level documentation](self) for details.
446 fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
447
448 /// Write `words` to the slave, ignoring all the incoming words.
449 ///
450 /// Implementations are allowed to return before the operation is
451 /// complete. See the [module-level documentation](self) for details.
452 fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
453
454 /// Write and read simultaneously. `write` is written to the slave on MOSI and
455 /// words received on MISO are stored in `read`.
456 ///
457 /// It is allowed for `read` and `write` to have different lengths, even zero length.
458 /// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
459 /// incoming words after `read` has been filled will be discarded. If `write` is shorter,
460 /// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
461 /// typically `0x00`, `0xFF`, or configurable.
462 ///
463 /// Implementations are allowed to return before the operation is
464 /// complete. See the [module-level documentation](self) for details.
465 fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
466
467 /// Write and read simultaneously. The contents of `words` are
468 /// written to the slave, and the received words are stored into the same
469 /// `words` buffer, overwriting it.
470 ///
471 /// Implementations are allowed to return before the operation is
472 /// complete. See the [module-level documentation](self) for details.
473 fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
474
475 /// Wait until all operations have completed and the bus is idle.
476 ///
477 /// See the [module-level documentation](self) for important usage information.
478 fn flush(&mut self) -> Result<(), Self::Error>;
479}
480
481impl<T: SpiBus<Word> + ?Sized, Word: Copy + 'static> SpiBus<Word> for &mut T {
482 #[inline]
483 fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
484 T::read(self, words)
485 }
486
487 #[inline]
488 fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
489 T::write(self, words)
490 }
491
492 #[inline]
493 fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
494 T::transfer(self, read, write)
495 }
496
497 #[inline]
498 fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
499 T::transfer_in_place(self, words)
500 }
501
502 #[inline]
503 fn flush(&mut self) -> Result<(), Self::Error> {
504 T::flush(self)
505 }
506}