diff --git a/rtic-sync/CHANGELOG.md b/rtic-sync/CHANGELOG.md index 9fcb798e24..c51a4c5483 100644 --- a/rtic-sync/CHANGELOG.md +++ b/rtic-sync/CHANGELOG.md @@ -14,6 +14,7 @@ For each category, _Added_, _Changed_, _Fixed_ add new entries at the top! ### Added +- Add `arbiter::{i2c, spi}::BlockingArbiterDevice` that helps during initialization of RTIC apps. After initialization is complete, convert an `BlockingArbiterDevice` into an `ArbiterDevice` using `BlockingArbiterDevice::into_non_blocking()` - `defmt v0.3` derives added and forwarded to `embedded-hal(-x)` crates. - signal structure diff --git a/rtic-sync/src/arbiter.rs b/rtic-sync/src/arbiter.rs index a173e2429b..e03c78b3bd 100644 --- a/rtic-sync/src/arbiter.rs +++ b/rtic-sync/src/arbiter.rs @@ -195,9 +195,10 @@ impl<'a, T> DerefMut for ExclusiveAccess<'a, T> { pub mod spi { use super::Arbiter; use embedded_hal::digital::OutputPin; + use embedded_hal::spi::SpiBus as BlockingSpiBus; use embedded_hal_async::{ delay::DelayNs, - spi::{ErrorType, Operation, SpiBus, SpiDevice}, + spi::{ErrorType, Operation, SpiBus as AsyncSpiBus, SpiDevice}, }; use embedded_hal_bus::spi::DeviceError; @@ -226,7 +227,7 @@ pub mod spi { impl<'a, Word, BUS, CS, D> SpiDevice for ArbiterDevice<'a, BUS, CS, D> where Word: Copy + 'static, - BUS: SpiBus, + BUS: AsyncSpiBus, CS: OutputPin, D: DelayNs, { @@ -271,6 +272,89 @@ pub mod spi { Ok(()) } } + + /// [`Arbiter`]-based shared bus implementation. + pub struct BlockingArbiterDevice<'a, BUS, CS, D> { + bus: &'a Arbiter, + cs: CS, + delay: D, + } + + impl<'a, BUS, CS, D> BlockingArbiterDevice<'a, BUS, CS, D> { + /// Create a new [`BlockingArbiterDevice`]. + pub fn new(bus: &'a Arbiter, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } + } + + /// Create an `ArbiterDevice` from an `BlockingArbiterDevice`. + pub fn into_non_blocking(self) -> ArbiterDevice<'a, BUS, CS, D> + where + BUS: AsyncSpiBus, + { + ArbiterDevice { + bus: self.bus, + cs: self.cs, + delay: self.delay, + } + } + } + + impl<'a, BUS, CS, D> ErrorType for BlockingArbiterDevice<'a, BUS, CS, D> + where + BUS: ErrorType, + CS: OutputPin, + { + type Error = DeviceError; + } + + impl<'a, Word, BUS, CS, D> SpiDevice for BlockingArbiterDevice<'a, BUS, CS, D> + where + Word: Copy + 'static, + BUS: BlockingSpiBus, + CS: OutputPin, + D: DelayNs, + { + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), DeviceError> { + let mut bus = self.bus.access().await; + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = 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), + Operation::DelayNs(ns) => match bus.flush() { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_ns(*ns).await; + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // 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(()) + } + } } /// I2C bus sharing using [`Arbiter`] @@ -323,8 +407,8 @@ pub mod spi { /// ``` pub mod i2c { use super::Arbiter; - use embedded_hal::i2c::{AddressMode, ErrorType, Operation}; - use embedded_hal_async::i2c::I2c; + use embedded_hal::i2c::{AddressMode, ErrorType, I2c as BlockingI2c, Operation}; + use embedded_hal_async::i2c::I2c as AsyncI2c; /// [`Arbiter`]-based shared bus implementation for I2C. pub struct ArbiterDevice<'a, BUS> { @@ -345,9 +429,9 @@ pub mod i2c { type Error = BUS::Error; } - impl<'a, BUS, A> I2c for ArbiterDevice<'a, BUS> + impl<'a, BUS, A> AsyncI2c for ArbiterDevice<'a, BUS> where - BUS: I2c, + BUS: AsyncI2c, A: AddressMode, { async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { @@ -379,6 +463,68 @@ pub mod i2c { bus.transaction(address, operations).await } } + + /// [`Arbiter`]-based shared bus implementation for I2C. + pub struct BlockingArbiterDevice<'a, BUS> { + bus: &'a Arbiter, + } + + impl<'a, BUS> BlockingArbiterDevice<'a, BUS> { + /// Create a new [`BlockingArbiterDevice`] for I2C. + pub fn new(bus: &'a Arbiter) -> Self { + Self { bus } + } + + /// Create an `ArbiterDevice` from an `BlockingArbiterDevice`. + pub fn into_non_blocking(self) -> ArbiterDevice<'a, BUS> + where + BUS: AsyncI2c, + { + ArbiterDevice { bus: self.bus } + } + } + + impl<'a, BUS> ErrorType for BlockingArbiterDevice<'a, BUS> + where + BUS: ErrorType, + { + type Error = BUS::Error; + } + + impl<'a, BUS, A> AsyncI2c for BlockingArbiterDevice<'a, BUS> + where + BUS: BlockingI2c, + A: AddressMode, + { + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + let mut bus = self.bus.access().await; + bus.read(address, read) + } + + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + let mut bus = self.bus.access().await; + bus.write(address, write) + } + + async fn write_read( + &mut self, + address: A, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + let mut bus = self.bus.access().await; + bus.write_read(address, write, read) + } + + async fn transaction( + &mut self, + address: A, + operations: &mut [Operation<'_>], + ) -> Result<(), Self::Error> { + let mut bus = self.bus.access().await; + bus.transaction(address, operations) + } + } } #[cfg(test)]