mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +01:00
rtic-sync: Add SPI bus sharing with arbiter
This commit is contained in:
parent
96e7704487
commit
ff5cad9cd2
4 changed files with 90 additions and 0 deletions
|
@ -9,6 +9,8 @@ For each category, _Added_, _Changed_, _Fixed_ add new entries at the top!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- `arbiter::spi::ArbiterDevice` for sharing SPI buses using `embedded-hal-async`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -21,6 +21,9 @@ heapless = "0.7"
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
rtic-common = { version = "1.0.0", path = "../rtic-common" }
|
rtic-common = { version = "1.0.0", path = "../rtic-common" }
|
||||||
portable-atomic = { version = "1", default-features = false }
|
portable-atomic = { version = "1", default-features = false }
|
||||||
|
embedded-hal = { version = "1.0.0-rc.1", optional = true }
|
||||||
|
embedded-hal-async = { version = "1.0.0-rc.1", optional = true }
|
||||||
|
embedded-hal-bus = { version = "0.1.0-rc.1", optional = true, features = ["async"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1", features = ["rt", "macros", "time"] }
|
tokio = { version = "1", features = ["rt", "macros", "time"] }
|
||||||
|
@ -29,3 +32,4 @@ tokio = { version = "1", features = ["rt", "macros", "time"] }
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
testing = ["critical-section/std", "rtic-common/testing"]
|
testing = ["critical-section/std", "rtic-common/testing"]
|
||||||
|
unstable = ["embedded-hal", "embedded-hal-async", "embedded-hal-bus"]
|
||||||
|
|
|
@ -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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -89,6 +89,7 @@ impl Package {
|
||||||
.chain(std::iter::once(None))
|
.chain(std::iter::once(None))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
Package::RticSync => vec![Some("unstable".to_string()), None],
|
||||||
_ => vec![None],
|
_ => vec![None],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue