diff options
| author | Emil Fresk <emil.fresk@gmail.com> | 2024-06-23 10:33:02 +0200 |
|---|---|---|
| committer | Emil Fresk <emil.fresk@gmail.com> | 2025-06-15 08:10:41 +0000 |
| commit | 38c364473c83f7a6214046d157ea5fe92e758732 (patch) | |
| tree | 10499ce35907a1a774c0e6762a0ad33fd8ef5afe /rtic-sync/src/arbiter/spi.rs | |
| parent | 95616b3c59e0d57afc8fb569458a33973beeaf54 (diff) | |
Add blocking version of `rtic_sync::arbiter::{i2c,spi}::ArbiterDevice`
Diffstat (limited to 'rtic-sync/src/arbiter/spi.rs')
| -rw-r--r-- | rtic-sync/src/arbiter/spi.rs | 164 |
1 files changed, 164 insertions, 0 deletions
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<BUS>, + cs: CS, + delay: D, +} + +impl<'a, BUS, CS, D> ArbiterDevice<'a, BUS, CS, D> { + /// Create a new [`ArbiterDevice`]. + pub fn new(bus: &'a Arbiter<BUS>, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } + } +} + +impl<BUS, CS, D> ErrorType for ArbiterDevice<'_, BUS, CS, D> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError<BUS::Error, CS::Error>; +} + +impl<Word, BUS, CS, D> SpiDevice<Word> for ArbiterDevice<'_, BUS, CS, D> +where + Word: Copy + 'static, + BUS: AsyncSpiBus<Word>, + CS: OutputPin, + D: DelayNs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), DeviceError<BUS::Error, CS::Error>> { + 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<BUS>, + cs: CS, + delay: D, +} + +impl<'a, BUS, CS, D> BlockingArbiterDevice<'a, BUS, CS, D> { + /// Create a new [`BlockingArbiterDevice`]. + pub fn new(bus: &'a Arbiter<BUS>, 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<BUS::Error, CS::Error>; +} + +impl<'a, Word, BUS, CS, D> SpiDevice<Word> for BlockingArbiterDevice<'a, BUS, CS, D> +where + Word: Copy + 'static, + BUS: BlockingSpiBus<Word>, + CS: OutputPin, + D: DelayNs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), DeviceError<BUS::Error, CS::Error>> { + 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(()) + } +} |
