From ac2ec224b0b3f296ee2c8c6ff49bd28cf4b1ae11 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:37:30 +0100 Subject: [PATCH 01/10] Adding AtomicDevice for I2C bus sharing --- embedded-hal-bus/Cargo.toml | 1 + embedded-hal-bus/src/i2c/atomic.rs | 168 +++++++++++++++++++++++++++++ embedded-hal-bus/src/i2c/mod.rs | 2 + 3 files changed, 171 insertions(+) create mode 100644 embedded-hal-bus/src/i2c/atomic.rs diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 5c89c9704..cec1945a8 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -24,6 +24,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } +portable-atomic = {version = "1", default-features = false} [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs new file mode 100644 index 000000000..038676a0b --- /dev/null +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -0,0 +1,168 @@ +use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; +use core::cell::UnsafeCell; + +/// `UnsafeCell`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to `RefCellDevice` instances, but they are `Send`. +/// so it only allows sharing across multiple threads (interrupt priority levels). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +/// # Examples +/// +/// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor +/// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors +/// from a single `i2c` instance. +/// +/// ``` +/// use embedded_hal_bus::i2c; +/// use core::cell::UnsafeCell; +/// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; +/// # pub struct Sensor { +/// # i2c: I2C, +/// # address: u8, +/// # } +/// # impl Sensor { +/// # pub fn new(i2c: I2C, address: u8) -> Self { +/// # Self { i2c, address } +/// # } +/// # } +/// # type PressureSensor = Sensor; +/// # type TemperatureSensor = Sensor; +/// # pub struct I2c0; +/// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// # pub enum Error { } +/// # impl hali2c::Error for Error { +/// # fn kind(&self) -> hali2c::ErrorKind { +/// # ErrorKind::Other +/// # } +/// # } +/// # impl hali2c::ErrorType for I2c0 { +/// # type Error = Error; +/// # } +/// # impl I2c for I2c0 { +/// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +/// # Ok(()) +/// # } +/// # } +/// # struct Hal; +/// # impl Hal { +/// # fn i2c(&self) -> I2c0 { +/// # I2c0 +/// # } +/// # } +/// # let hal = Hal; +/// +/// let i2c = hal.i2c(); +/// let i2c_unsafe_cell = UnsafeCell::new(i2c); +/// let mut temperature_sensor = TemperatureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x20, +/// ); +/// let mut pressure_sensor = PressureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x42, +/// ); +/// ``` +pub struct AtomicDevice<'a, T> { + bus: &'a UnsafeCell, + busy: portable_atomic::AtomicBool, +} + + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked I2C bus manager. +pub enum AtomicError { + /// This error is returned if the I2C bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An I2C-related error occurred, and the internal error should be inspected. + Other(T::Error), +} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +unsafe impl<'a, T> Sync for AtomicDevice<'a, T> {} + +impl<'a, T> AtomicDevice<'a, T> +where T: I2c + ErrorType +{ + /// Create a new `AtomicDevice`. + #[inline] + pub fn new(bus: &'a UnsafeCell) -> Self { + Self { + bus, + busy: portable_atomic::AtomicBool::from(false), + } + } + + fn lock(&self, f: F) -> Result> + where + F: FnOnce(&mut T) -> Result::Error> + { + self.busy.compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ).map_err(|_| AtomicError::::Busy)?; + + let result = f(unsafe { &mut *self.bus.get() }); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(|e| AtomicError::Other(e)) + } +} + +impl<'a, T> ErrorType for AtomicDevice<'a, T> +where + T: I2c + core::fmt::Debug, +{ + type Error = AtomicError; +} + +impl<'a, T> I2c for AtomicDevice<'a, T> +where + T: I2c + core::fmt::Debug, +{ + #[inline] + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.read(address, read)) + } + + #[inline] + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.write(address, write)) + } + + #[inline] + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.write_read(address, write, read)) + } + + #[inline] + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.transaction(address, operations)) + } +} diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 8eaf2882f..724fad636 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,3 +8,5 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +mod atomic; +pub use self::atomic::*; From 91b31f25e85c165473d5ed0cf37203faa0ab9809 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:40:00 +0100 Subject: [PATCH 02/10] fixing format --- embedded-hal-bus/src/i2c/atomic.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 038676a0b..2039d3dd2 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -1,5 +1,5 @@ -use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; use core::cell::UnsafeCell; +use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// `UnsafeCell`-based shared bus [`I2c`] implementation. /// @@ -71,7 +71,6 @@ pub struct AtomicDevice<'a, T> { busy: portable_atomic::AtomicBool, } - #[derive(Debug, Copy, Clone)] /// Wrapper type for errors originating from the atomically-checked I2C bus manager. pub enum AtomicError { @@ -96,7 +95,8 @@ impl Error for AtomicError { unsafe impl<'a, T> Sync for AtomicDevice<'a, T> {} impl<'a, T> AtomicDevice<'a, T> -where T: I2c + ErrorType +where + T: I2c + ErrorType, { /// Create a new `AtomicDevice`. #[inline] @@ -109,14 +109,16 @@ where T: I2c + ErrorType fn lock(&self, f: F) -> Result> where - F: FnOnce(&mut T) -> Result::Error> + F: FnOnce(&mut T) -> Result::Error>, { - self.busy.compare_exchange( - false, - true, - core::sync::atomic::Ordering::SeqCst, - core::sync::atomic::Ordering::SeqCst, - ).map_err(|_| AtomicError::::Busy)?; + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::::Busy)?; let result = f(unsafe { &mut *self.bus.get() }); From b0cd0c3924d7ab7b339100b38a4fcece6c6c902b Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:42:19 +0100 Subject: [PATCH 03/10] Fixing test --- embedded-hal-bus/src/i2c/atomic.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 2039d3dd2..59370c502 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -31,6 +31,7 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// # } /// # type PressureSensor = Sensor; /// # type TemperatureSensor = Sensor; +/// #[derive(Debug)] /// # pub struct I2c0; /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] /// # pub enum Error { } From 219fcafe9103665dea0d5270abcee104743337ed Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:48:36 +0100 Subject: [PATCH 04/10] Fixing clippy, docs --- embedded-hal-bus/src/i2c/atomic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 59370c502..4fc901acf 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -3,7 +3,7 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// `UnsafeCell`-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to `RefCellDevice` instances, but they are `Send`. +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. /// so it only allows sharing across multiple threads (interrupt priority levels). When attempting /// to preempt usage of the bus, a `AtomicError::Busy` error is returned. /// @@ -13,7 +13,7 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// # Examples /// /// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor -/// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors +/// with address `0x20`; [`AtomicDevice`] can be used to give access to both of these sensors /// from a single `i2c` instance. /// /// ``` From 7218137143af1d3487ddec82cc57b57cba18dadd Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:57:49 +0100 Subject: [PATCH 05/10] Cleaning up and fixing bounds --- embedded-hal-bus/src/i2c/atomic.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 4fc901acf..f7e9fd586 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -31,7 +31,6 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// # } /// # type PressureSensor = Sensor; /// # type TemperatureSensor = Sensor; -/// #[derive(Debug)] /// # pub struct I2c0; /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] /// # pub enum Error { } @@ -74,17 +73,17 @@ pub struct AtomicDevice<'a, T> { #[derive(Debug, Copy, Clone)] /// Wrapper type for errors originating from the atomically-checked I2C bus manager. -pub enum AtomicError { +pub enum AtomicError { /// This error is returned if the I2C bus was already in use when an operation was attempted, /// which indicates that the driver requirements are not being met with regard to /// synchronization. Busy, /// An I2C-related error occurred, and the internal error should be inspected. - Other(T::Error), + Other(T), } -impl Error for AtomicError { +impl Error for AtomicError { fn kind(&self) -> ErrorKind { match self { AtomicError::Other(e) => e.kind(), @@ -97,7 +96,7 @@ unsafe impl<'a, T> Sync for AtomicDevice<'a, T> {} impl<'a, T> AtomicDevice<'a, T> where - T: I2c + ErrorType, + T: I2c, { /// Create a new `AtomicDevice`. #[inline] @@ -108,7 +107,7 @@ where } } - fn lock(&self, f: F) -> Result> + fn lock(&self, f: F) -> Result> where F: FnOnce(&mut T) -> Result::Error>, { @@ -119,26 +118,26 @@ where core::sync::atomic::Ordering::SeqCst, core::sync::atomic::Ordering::SeqCst, ) - .map_err(|_| AtomicError::::Busy)?; + .map_err(|_| AtomicError::::Busy)?; let result = f(unsafe { &mut *self.bus.get() }); self.busy.store(false, core::sync::atomic::Ordering::SeqCst); - result.map_err(|e| AtomicError::Other(e)) + result.map_err(AtomicError::Other) } } impl<'a, T> ErrorType for AtomicDevice<'a, T> where - T: I2c + core::fmt::Debug, + T: I2c, { - type Error = AtomicError; + type Error = AtomicError; } impl<'a, T> I2c for AtomicDevice<'a, T> where - T: I2c + core::fmt::Debug, + T: I2c, { #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { From a3d01e117303011fbd225d8b51073d908d5f2926 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 11:56:28 +0100 Subject: [PATCH 06/10] Fixing implementation --- embedded-hal-bus/src/i2c/atomic.rs | 2 +- embedded-hal-bus/src/i2c/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index f7e9fd586..c6905c886 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -92,7 +92,7 @@ impl Error for AtomicError { } } -unsafe impl<'a, T> Sync for AtomicDevice<'a, T> {} +unsafe impl<'a, T> Send for AtomicDevice<'a, T> {} impl<'a, T> AtomicDevice<'a, T> where diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 724fad636..5295492f1 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -9,4 +9,4 @@ pub use mutex::*; mod critical_section; pub use self::critical_section::*; mod atomic; -pub use self::atomic::*; +pub use atomic::*; From 735b635210a599339a4cabd0e99f8e5749a092ce Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 11 Mar 2024 11:17:51 +0100 Subject: [PATCH 07/10] Adding SPI atomic implementation --- embedded-hal-bus/src/spi/atomic.rs | 129 +++++++++++++++++++++++++++++ embedded-hal-bus/src/spi/mod.rs | 2 + 2 files changed, 131 insertions(+) create mode 100644 embedded-hal-bus/src/spi/atomic.rs diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs new file mode 100644 index 000000000..cca4ce02c --- /dev/null +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -0,0 +1,129 @@ +use core::cell::UnsafeCell; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; + +use super::DeviceError; +use crate::spi::shared::transaction; + +/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, and, unlike [`crate::spi::RefCellDevice`], instances are `Send`, +/// so it only allows sharing across multiple threads (interrupt priority level). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +pub struct AtomicDevice<'a, BUS, CS, D> { + bus: &'a UnsafeCell, + cs: CS, + delay: D, + busy: portable_atomic::AtomicBool, +} + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked I2C bus manager. +pub enum AtomicError { + /// This error is returned if the I2C bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An I2C-related error occurred, and the internal error should be inspected. + Other(T), +} + +impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { + /// Create a new [`AtomicDevice`]. + #[inline] + pub fn new(bus: &'a UnsafeCell, cs: CS, delay: D) -> Self { + Self { + bus, + cs, + delay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, super::NoDelay> +where + BUS: ErrorType, + CS: OutputPin, +{ + /// Create a new [`AtomicDevice`] without support for in-transaction delays. + /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type [`Operation::DelayNs`]. + #[inline] + pub fn new_no_delay(bus: &'a UnsafeCell, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for AtomicDevice<'a, BUS, CS, D> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = AtomicError>; +} + +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for AtomicDevice<'a, BUS, CS, D> +where + BUS: SpiBus, + CS: OutputPin, + D: DelayNs, +{ + #[inline] + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::Busy)?; + + let bus = unsafe { &mut *self.bus.get() }; + + let result = transaction(operations, bus, &mut self.delay, &mut self.cs); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(AtomicError::Other) + } +} diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d408bd92f..d55fa5eaf 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,8 +11,10 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; +mod atomic; mod critical_section; mod shared; +pub use atomic::*; pub use self::critical_section::*; From 6c0cbad0dbd5d2720ce89d93c43cc36647bcfef5 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 11 Mar 2024 11:20:06 +0100 Subject: [PATCH 08/10] Fixing docs in SPI --- embedded-hal-bus/Cargo.toml | 2 +- embedded-hal-bus/src/spi/atomic.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index cec1945a8..673037f4a 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -20,7 +20,7 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "1.0.0", path = "../embedded-hal" } +embedded-hal = { version = "1.0.0" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index cca4ce02c..c5db18f09 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -26,14 +26,14 @@ pub struct AtomicDevice<'a, BUS, CS, D> { } #[derive(Debug, Copy, Clone)] -/// Wrapper type for errors originating from the atomically-checked I2C bus manager. +/// Wrapper type for errors originating from the atomically-checked SPI bus manager. pub enum AtomicError { - /// This error is returned if the I2C bus was already in use when an operation was attempted, + /// This error is returned if the SPI bus was already in use when an operation was attempted, /// which indicates that the driver requirements are not being met with regard to /// synchronization. Busy, - /// An I2C-related error occurred, and the internal error should be inspected. + /// An SPI-related error occurred, and the internal error should be inspected. Other(T), } From cf6db3476c176bd9f6a9fb036e5a02c14b6c04ca Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 22 Apr 2024 11:43:13 +0200 Subject: [PATCH 09/10] Fixing unrelated async feature issues --- embedded-hal-bus/src/spi/exclusive.rs | 26 +++++++++++++++++++------- embedded-hal-bus/src/spi/mod.rs | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index f28a10af4..4cabf39f3 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -6,7 +6,10 @@ use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; #[cfg(feature = "async")] use embedded_hal_async::{ delay::DelayNs as AsyncDelayNs, - spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, + spi::{ + ErrorType as AsyncErrorType, Operation as AsyncOperation, SpiBus as AsyncSpiBus, + SpiDevice as AsyncSpiDevice, + }, }; use super::shared::transaction; @@ -89,6 +92,15 @@ where } } +#[cfg(feature = "async")] +impl AsyncErrorType for ExclusiveDevice +where + BUS: AsyncErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] impl AsyncSpiDevice for ExclusiveDevice @@ -100,18 +112,18 @@ where #[inline] async fn transaction( &mut self, - operations: &mut [Operation<'_, Word>], + operations: &mut [AsyncOperation<'_, Word>], ) -> Result<(), Self::Error> { self.cs.set_low().map_err(DeviceError::Cs)?; let op_res = 'ops: { for op in operations { let res = match op { - Operation::Read(buf) => self.bus.read(buf).await, - Operation::Write(buf) => self.bus.write(buf).await, - Operation::Transfer(read, write) => self.bus.transfer(read, write).await, - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayNs(ns) => match self.bus.flush().await { + AsyncOperation::Read(buf) => self.bus.read(buf).await, + AsyncOperation::Write(buf) => self.bus.write(buf).await, + AsyncOperation::Transfer(read, write) => self.bus.transfer(read, write).await, + AsyncOperation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + AsyncOperation::DelayNs(ns) => match self.bus.flush().await { Err(e) => Err(e), Ok(()) => { self.delay.delay_ns(*ns).await; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d55fa5eaf..1ca01ab24 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -3,6 +3,9 @@ use core::fmt::{self, Debug, Display, Formatter}; use embedded_hal::spi::{Error, ErrorKind}; +#[cfg(feature = "async")] +use embedded_hal_async::spi::{Error as AsyncError, ErrorKind as AsyncErrorKind}; + mod exclusive; pub use exclusive::*; mod refcell; @@ -57,6 +60,21 @@ where } } +#[cfg(feature = "async")] +impl AsyncError for DeviceError +where + BUS: AsyncError + Debug, + CS: Debug, +{ + #[inline] + fn kind(&self) -> AsyncErrorKind { + match self { + Self::Spi(e) => e.kind(), + Self::Cs(_) => AsyncErrorKind::ChipSelectFault, + } + } +} + /// Dummy [`DelayNs`](embedded_hal::delay::DelayNs) implementation that panics on use. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] From 5a2846bb0852d5736ac43793eac06e72bd15e4e9 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 22 Apr 2024 11:45:56 +0200 Subject: [PATCH 10/10] Updating CHANGELOG --- embedded-hal-bus/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 04de7aa69..fb44d5656 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +### Added +- Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. + + +### Fixed +- Fixed an issue with SPI `ExclusiveDevice` builds when the `async` feature was enabled. ## [v0.1.0] - 2023-12-28