Skip to content

Commit f91fcbc

Browse files
authored
Merge pull request #613 from adri326/rc-spi-device
Add a Rc<RefCell<Bus>>-based implementation of SpiDevice and I2C
2 parents 4e9f3ed + 21e82bb commit f91fcbc

File tree

7 files changed

+181
-2
lines changed

7 files changed

+181
-2
lines changed

embedded-hal-bus/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10-
(Add unreleased changes here)
10+
- Added the `alloc` feature.
11+
- Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`.
1112

1213
## [v0.2.0] - 2024-04-23
1314

embedded-hal-bus/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ repository = "https://github.com/rust-embedded/embedded-hal"
1515
version = "0.2.0"
1616

1717
[features]
18-
std = []
18+
std = ["alloc"]
1919
async = ["dep:embedded-hal-async"]
2020
defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"]
21+
# Enables additional utilities requiring a global allocator.
22+
alloc = []
2123

2224
[dependencies]
2325
embedded-hal = { version = "1.0.0", path = "../embedded-hal" }

embedded-hal-bus/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins
3434
`std::error::Error` for `DeviceError`.
3535
- **`async`**: enable `embedded-hal-async` support.
3636
- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs.
37+
- **`alloc`**: enable implementations using `alloc` (for instance, `spi::RcDevice`, which makes use of `alloc::rc::Rc`)
3738

3839
## Minimum Supported Rust Version (MSRV)
3940

embedded-hal-bus/src/i2c/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ mod critical_section;
1010
pub use self::critical_section::*;
1111
mod atomic;
1212
pub use atomic::*;
13+
14+
#[cfg(feature = "alloc")]
15+
mod rc;
16+
#[cfg(feature = "alloc")]
17+
pub use rc::*;

embedded-hal-bus/src/i2c/rc.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
extern crate alloc;
2+
use alloc::rc::Rc;
3+
4+
use core::cell::RefCell;
5+
use embedded_hal::i2c::{ErrorType, I2c};
6+
7+
/// `Rc<RefCell<T>>`-based shared bus [`I2c`] implementation.
8+
/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice).
9+
///
10+
/// Sharing is implemented with a [`RefCell`] and ownership is managed by [`Rc`].
11+
/// Like [`RefCellDevice`](super::RefCellDevice), `RcDevice` instances are not [`Send`],
12+
/// so they can only be shared within a single thread (interrupt priority level).
13+
///
14+
/// When this `RcDevice` is dropped, the reference count of the I2C bus will be decremented.
15+
/// Once that reference count hits zero, it will be cleaned up.
16+
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
17+
pub struct RcDevice<Bus> {
18+
bus: Rc<RefCell<Bus>>,
19+
}
20+
21+
impl<Bus> RcDevice<Bus> {
22+
/// Creates a new `RcDevice`.
23+
///
24+
/// This function does not increment the reference count for the bus:
25+
/// you will need to call `Rc::clone(&bus)` if you only have a `&Rc<RefCell<Bus>>`.
26+
#[inline]
27+
pub fn new(bus: Rc<RefCell<Bus>>) -> Self {
28+
Self { bus }
29+
}
30+
}
31+
32+
impl<Bus> ErrorType for RcDevice<Bus>
33+
where
34+
Bus: ErrorType,
35+
{
36+
type Error = Bus::Error;
37+
}
38+
39+
impl<Bus> I2c for RcDevice<Bus>
40+
where
41+
Bus: I2c,
42+
{
43+
#[inline]
44+
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
45+
let bus = &mut *self.bus.borrow_mut();
46+
bus.read(address, read)
47+
}
48+
49+
#[inline]
50+
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
51+
let bus = &mut *self.bus.borrow_mut();
52+
bus.write(address, write)
53+
}
54+
55+
#[inline]
56+
fn write_read(
57+
&mut self,
58+
address: u8,
59+
write: &[u8],
60+
read: &mut [u8],
61+
) -> Result<(), Self::Error> {
62+
let bus = &mut *self.bus.borrow_mut();
63+
bus.write_read(address, write, read)
64+
}
65+
66+
#[inline]
67+
fn transaction(
68+
&mut self,
69+
address: u8,
70+
operations: &mut [embedded_hal::i2c::Operation<'_>],
71+
) -> Result<(), Self::Error> {
72+
let bus = &mut *self.bus.borrow_mut();
73+
bus.transaction(address, operations)
74+
}
75+
}

embedded-hal-bus/src/spi/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ mod critical_section;
1616
mod shared;
1717
pub use atomic::*;
1818

19+
#[cfg(feature = "alloc")]
20+
mod rc;
21+
#[cfg(feature = "alloc")]
22+
pub use rc::*;
23+
1924
pub use self::critical_section::*;
2025

2126
#[cfg(feature = "defmt-03")]

embedded-hal-bus/src/spi/rc.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
extern crate alloc;
2+
use alloc::rc::Rc;
3+
4+
use core::cell::RefCell;
5+
use embedded_hal::delay::DelayNs;
6+
use embedded_hal::digital::OutputPin;
7+
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
8+
9+
use super::DeviceError;
10+
use crate::spi::shared::transaction;
11+
12+
/// Implementation of [`SpiDevice`] around a bus shared with `Rc<RefCell<T>>`.
13+
/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice), requiring allocation.
14+
///
15+
/// A single [`SpiBus`] is shared via [`RefCell`], and its ownership is handled by [`Rc`].
16+
/// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level).
17+
/// For this reason, this does not implement [`Send`].
18+
///
19+
/// When this structure is dropped, the reference count of the `Bus` instance will be decremented,
20+
/// and it will be cleaned up once the reference count reaches zero.
21+
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
22+
pub struct RcDevice<Bus, Cs, Delay> {
23+
bus: Rc<RefCell<Bus>>,
24+
cs: Cs,
25+
delay: Delay,
26+
}
27+
28+
impl<Bus, Cs, Delay> RcDevice<Bus, Cs, Delay> {
29+
/// Creates a new [`RcDevice`].
30+
///
31+
/// This sets the `cs` pin high, and returns an error if that fails.
32+
/// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches.
33+
///
34+
/// This function does not increment the reference count:
35+
/// you will need to call `Rc::clone(&bus)` if you only have a `&Rc<RefCell<Bus>>`.
36+
#[inline]
37+
pub fn new(bus: Rc<RefCell<Bus>>, mut cs: Cs, delay: Delay) -> Result<Self, Cs::Error>
38+
where
39+
Cs: OutputPin,
40+
{
41+
cs.set_high()?;
42+
43+
Ok(Self { bus, cs, delay })
44+
}
45+
}
46+
47+
impl<Bus, Cs> RcDevice<Bus, Cs, super::NoDelay> {
48+
/// Creates a new [`RcDevice`] without support for in-transaction delays.
49+
///
50+
/// **Warning**: It's advised to prefer [`RcDevice::new`],
51+
/// as the contract of [`SpiDevice`] requests support for in-transaction delays.
52+
///
53+
/// Refer to [`RefCellDevice::new_no_delay`](super::RefCellDevice::new_no_delay) for more information.
54+
#[inline]
55+
pub fn new_no_delay(bus: Rc<RefCell<Bus>>, mut cs: Cs) -> Result<Self, Cs::Error>
56+
where
57+
Cs: OutputPin,
58+
{
59+
cs.set_high()?;
60+
61+
Ok(Self {
62+
bus,
63+
cs,
64+
delay: super::NoDelay,
65+
})
66+
}
67+
}
68+
69+
impl<Bus, Cs, Delay> ErrorType for RcDevice<Bus, Cs, Delay>
70+
where
71+
Bus: ErrorType,
72+
Cs: OutputPin,
73+
{
74+
type Error = DeviceError<Bus::Error, Cs::Error>;
75+
}
76+
77+
impl<Word, Bus, Cs, Delay> SpiDevice<Word> for RcDevice<Bus, Cs, Delay>
78+
where
79+
Word: Copy + 'static,
80+
Bus: SpiBus<Word>,
81+
Cs: OutputPin,
82+
Delay: DelayNs,
83+
{
84+
#[inline]
85+
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
86+
let bus = &mut *self.bus.borrow_mut();
87+
88+
transaction(operations, bus, &mut self.delay, &mut self.cs)
89+
}
90+
}

0 commit comments

Comments
 (0)