From 38c364473c83f7a6214046d157ea5fe92e758732 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 23 Jun 2024 10:33:02 +0200 Subject: Add blocking version of `rtic_sync::arbiter::{i2c,spi}::ArbiterDevice` --- rtic-sync/src/arbiter/spi.rs | 164 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 rtic-sync/src/arbiter/spi.rs (limited to 'rtic-sync/src/arbiter/spi.rs') diff --git a/rtic-sync/src/arbiter/spi.rs b/rtic-sync/src/arbiter/spi.rs new file mode 100644 index 0000000..7ab15ed --- /dev/null +++ b/rtic-sync/src/arbiter/spi.rs @@ -0,0 +1,164 @@ +//! SPI bus sharing using [`Arbiter`] + +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 as AsyncSpiBus, SpiDevice}, +}; +use embedded_hal_bus::spi::DeviceError; + +/// [`Arbiter`]-based shared bus implementation. +pub struct ArbiterDevice<'a, BUS, CS, D> { + bus: &'a Arbiter, + cs: CS, + delay: D, +} + +impl<'a, BUS, CS, D> ArbiterDevice<'a, BUS, CS, D> { + /// Create a new [`ArbiterDevice`]. + pub fn new(bus: &'a Arbiter, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } + } +} + +impl ErrorType for ArbiterDevice<'_, BUS, CS, D> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl SpiDevice for ArbiterDevice<'_, BUS, CS, D> +where + Word: Copy + 'static, + BUS: AsyncSpiBus, + 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).await, + Operation::Write(buf) => bus.write(buf).await, + Operation::Transfer(read, write) => bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await, + Operation::DelayNs(ns) => match bus.flush().await { + 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().await; + 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(()) + } +} + +/// [`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(()) + } +} -- cgit v1.2.3