From 76541e0766257a89d9f300f9b5ee61e4605b883a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Mar 2023 14:43:15 +0200 Subject: [PATCH 1/2] bus/spi: add RefCell, CriticalSection and Mutex shared bus implementations. --- embedded-hal-bus/CHANGELOG.md | 1 + embedded-hal-bus/src/spi.rs | 163 ------------------- embedded-hal-bus/src/spi/critical_section.rs | 133 +++++++++++++++ embedded-hal-bus/src/spi/exclusive.rs | 117 +++++++++++++ embedded-hal-bus/src/spi/mod.rs | 37 +++++ embedded-hal-bus/src/spi/mutex.rs | 124 ++++++++++++++ embedded-hal-bus/src/spi/refcell.rs | 124 ++++++++++++++ 7 files changed, 536 insertions(+), 163 deletions(-) delete mode 100644 embedded-hal-bus/src/spi.rs create mode 100644 embedded-hal-bus/src/spi/critical_section.rs create mode 100644 embedded-hal-bus/src/spi/exclusive.rs create mode 100644 embedded-hal-bus/src/spi/mod.rs create mode 100644 embedded-hal-bus/src/spi/mutex.rs create mode 100644 embedded-hal-bus/src/spi/refcell.rs diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index a173d3769..97ee0d8d1 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - i2c: add bus sharing implementations. +- spi: add bus sharing implementations. ## [v0.1.0-alpha.1] - 2022-09-28 diff --git a/embedded-hal-bus/src/spi.rs b/embedded-hal-bus/src/spi.rs deleted file mode 100644 index c0de23fcb..000000000 --- a/embedded-hal-bus/src/spi.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! SPI bus sharing mechanisms. - -use core::fmt::Debug; -use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - Error, ErrorKind, ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, - SpiDeviceRead, SpiDeviceWrite, -}; - -/// Error type for [`ExclusiveDevice`] operations. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExclusiveDeviceError { - /// An inner SPI bus operation failed - Spi(BUS), - /// Asserting or deasserting CS failed - Cs(CS), -} - -impl Error for ExclusiveDeviceError -where - BUS: Error + Debug, - CS: Debug, -{ - fn kind(&self) -> ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => ErrorKind::ChipSelectFault, - } - } -} - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = ExclusiveDeviceError; -} - -impl SpiDeviceRead for ExclusiveDevice -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDeviceWrite for ExclusiveDevice -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for op in operations { - match op { - Operation::Read(buf) => { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - Operation::Write(buf) => { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - Operation::Transfer(read, write) => { - if let Err(e) = self.bus.transfer(read, write) { - op_res = Err(e); - break; - } - } - Operation::TransferInPlace(buf) => { - if let Err(e) = self.bus.transfer_in_place(buf) { - op_res = Err(e); - break; - } - } - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs new file mode 100644 index 000000000..a327609fd --- /dev/null +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -0,0 +1,133 @@ +use core::cell::RefCell; +use critical_section::Mutex; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// `critical-section`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). +/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely +/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using +/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. +pub struct CriticalSectionDevice<'a, BUS, CS> { + bus: &'a Mutex>, + cs: CS, +} + +impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a Mutex>, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs new file mode 100644 index 000000000..fc7926eeb --- /dev/null +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -0,0 +1,117 @@ +//! SPI bus sharing mechanisms. + +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). +/// +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), +/// ideal for when no sharing is required (only one SPI device is present on the bus). +pub struct ExclusiveDevice { + bus: BUS, + cs: CS, +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice + pub fn new(bus: BUS, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl ErrorType for ExclusiveDevice +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl SpiDeviceRead for ExclusiveDevice +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDeviceWrite for ExclusiveDevice +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => self.bus.read(buf), + Operation::Write(buf) => self.bus.write(buf), + Operation::Transfer(read, write) => self.bus.transfer(read, write), + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs new file mode 100644 index 000000000..6ae803865 --- /dev/null +++ b/embedded-hal-bus/src/spi/mod.rs @@ -0,0 +1,37 @@ +//! `SpiDevice` implementations. + +use core::fmt::Debug; +use embedded_hal::spi::{Error, ErrorKind}; + +mod exclusive; +pub use exclusive::*; +mod refcell; +pub use refcell::*; +#[cfg(feature = "std")] +mod mutex; +#[cfg(feature = "std")] +pub use mutex::*; +mod critical_section; +pub use self::critical_section::*; + +/// Error type for [`ExclusiveDevice`] operations. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum DeviceError { + /// An inner SPI bus operation failed + Spi(BUS), + /// Asserting or deasserting CS failed + Cs(CS), +} + +impl Error for DeviceError +where + BUS: Error + Debug, + CS: Debug, +{ + fn kind(&self) -> ErrorKind { + match self { + Self::Spi(e) => e.kind(), + Self::Cs(_) => ErrorKind::ChipSelectFault, + } + } +} diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs new file mode 100644 index 000000000..1bb693ded --- /dev/null +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -0,0 +1,124 @@ +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; +use std::sync::Mutex; + +use super::DeviceError; + +/// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is +/// it is only available in `std` targets. +pub struct MutexDevice<'a, BUS, CS> { + bus: &'a Mutex, + cs: CS, +} + +impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a Mutex, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for MutexDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for MutexDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for MutexDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs new file mode 100644 index 000000000..47e29a879 --- /dev/null +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -0,0 +1,124 @@ +use core::cell::RefCell; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// `RefCell`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, +/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several +/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. +pub struct RefCellDevice<'a, BUS, CS> { + bus: &'a RefCell, + cs: CS, +} + +impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a RefCell, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} From 756b05519e88a0219bc731573b86c175eda6dc63 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 1 Apr 2023 10:52:48 +0200 Subject: [PATCH 2/2] spi: Add changelog for transaction changes. --- embedded-hal-async/CHANGELOG.md | 1 + embedded-hal/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 771ce2a24..8ee28411f 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: make infallible. - i2c: remove `_iter()` methods. - i2c: add default implementations for all methods based on `transaction()`. +- spi: SpiDevice transaction now takes an operation slice instead of a closure ## [v0.2.0-alpha.0] - 2022-11-23 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index b1e3e53d8..ab6f2c575 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - i2c: remove `_iter()` methods. - i2c: add default implementations for all methods based on `transaction()`. - i2c: document guidelines for shared bus usage. +- spi: SpiDevice transaction now takes an operation slice instead of a closure ## [v1.0.0-alpha.9] - 2022-09-28