Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blocking version of rtic_sync::arbiter::{i2c,spi}::ArbiterDevice #955

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rtic-sync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
158 changes: 152 additions & 6 deletions rtic-sync/src/arbiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -226,7 +227,7 @@ pub mod spi {
impl<'a, Word, BUS, CS, D> SpiDevice<Word> for ArbiterDevice<'a, BUS, CS, D>
where
Word: Copy + 'static,
BUS: SpiBus<Word>,
BUS: AsyncSpiBus<Word>,
CS: OutputPin,
D: DelayNs,
{
Expand Down Expand Up @@ -271,6 +272,89 @@ pub mod spi {
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(())
}
}
}

/// I2C bus sharing using [`Arbiter`]
Expand Down Expand Up @@ -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> {
Expand All @@ -345,9 +429,9 @@ pub mod i2c {
type Error = BUS::Error;
}

impl<'a, BUS, A> I2c<A> for ArbiterDevice<'a, BUS>
impl<'a, BUS, A> AsyncI2c<A> for ArbiterDevice<'a, BUS>
where
BUS: I2c<A>,
BUS: AsyncI2c<A>,
A: AddressMode,
{
async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {
Expand Down Expand Up @@ -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<BUS>,
}

impl<'a, BUS> BlockingArbiterDevice<'a, BUS> {
/// Create a new [`BlockingArbiterDevice`] for I2C.
pub fn new(bus: &'a Arbiter<BUS>) -> 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<A> for BlockingArbiterDevice<'a, BUS>
where
BUS: BlockingI2c<A>,
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)]
Expand Down