diff options
author | 2023-10-09 16:55:55 +0200 | |
---|---|---|
committer | 2023-11-22 19:42:46 +0000 | |
commit | ff5cad9cd2a3d6c6618bf54e0b126ed9d68ef71a (patch) | |
tree | 4a8a8d04cd41a707aa875be8a9baca32d47a728b /rtic-sync/src | |
parent | 96e7704487a58ea57972f15986b0ca0a64f144c6 (diff) | |
download | rtic-ff5cad9cd2a3d6c6618bf54e0b126ed9d68ef71a.tar.gz rtic-ff5cad9cd2a3d6c6618bf54e0b126ed9d68ef71a.tar.zst rtic-ff5cad9cd2a3d6c6618bf54e0b126ed9d68ef71a.zip |
rtic-sync: Add SPI bus sharing with arbiter
Diffstat (limited to 'rtic-sync/src')
-rw-r--r-- | rtic-sync/src/arbiter.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/rtic-sync/src/arbiter.rs b/rtic-sync/src/arbiter.rs index 2d66a677..5683f93d 100644 --- a/rtic-sync/src/arbiter.rs +++ b/rtic-sync/src/arbiter.rs @@ -191,6 +191,89 @@ impl<'a, T> DerefMut for ExclusiveAccess<'a, T> { } } +#[cfg(feature = "unstable")] +/// SPI bus sharing using [`Arbiter`] +pub mod spi { + use super::Arbiter; + use embedded_hal::digital::OutputPin; + use embedded_hal_async::{ + delay::DelayUs, + spi::{ErrorType, Operation, SpiBus, 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<'a, BUS, CS, D> ErrorType for ArbiterDevice<'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 ArbiterDevice<'a, BUS, CS, D> + where + Word: Copy + 'static, + BUS: SpiBus<Word>, + CS: OutputPin, + D: DelayUs, + { + 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::DelayUs(us) => match bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).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(()) + } + } +} + #[cfg(test)] mod tests { use super::*; |