Skip to content

Commit 99b652f

Browse files
committed
bus/i2c: add RefCell, CriticalSection and Mutex shared bus implementations.
1 parent 7b42f24 commit 99b652f

File tree

9 files changed

+212
-8
lines changed

9 files changed

+212
-8
lines changed

.github/workflows/clippy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ jobs:
1515
# Use a pinned version to avoid spontaneous breakages (new clippy lints are added often)
1616
toolchain: nightly-2022-11-22
1717
components: clippy
18-
- run: cargo clippy -- --deny=warnings
18+
- run: cargo clippy --features=embedded-hal-bus/std -- --deny=warnings

.github/workflows/test.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
# All generated code should be running on stable now
1716
rust:
1817
- stable
1918
- 1.59.0 # MSRV
2019
- nightly
21-
22-
# The default target we're compiling on and for
2320
target:
2421
- x86_64-unknown-linux-gnu
2522
- thumbv6m-none-eabi
2623
- thumbv7m-none-eabi
24+
include:
25+
- target: x86_64-unknown-linux-gnu
26+
features: embedded-hal-bus/std
2727

2828
steps:
2929
- uses: actions/checkout@v3
@@ -35,7 +35,7 @@ jobs:
3535
- run: sed -i '/nightly-only/d' Cargo.toml
3636
if: matrix.rust != 'nightly'
3737

38-
- run: cargo check --target=${{ matrix.target }}
38+
- run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }}
3939

40-
- run: cargo test --target=${{ matrix.target }}
40+
- run: cargo test --target=${{ matrix.target }} --features=${{ matrix.features }}
4141
if: contains(matrix.target, 'linux')

embedded-hal-bus/CHANGELOG.md

+2-1
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-
...
10+
### Added
11+
- i2c: add bus sharing implementations.
1112

1213
## [v0.1.0-alpha.1] - 2022-09-28
1314

embedded-hal-bus/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@ readme = "README.md"
1313
repository = "https://github.com/rust-embedded/embedded-hal"
1414
version = "0.1.0-alpha.1"
1515

16+
[features]
17+
std = []
18+
1619
[dependencies]
1720
embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" }
21+
critical-section = { version = "1.0" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use core::cell::RefCell;
2+
use critical_section::Mutex;
3+
use embedded_hal::i2c::{ErrorType, I2c};
4+
5+
/// `critical-section`-based shared bus [`I2c`] implementation.
6+
///
7+
/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for
8+
/// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels).
9+
/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely
10+
/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using
11+
/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections.
12+
pub struct CriticalSectionDevice<'a, T> {
13+
bus: &'a Mutex<RefCell<T>>,
14+
}
15+
16+
impl<'a, T> CriticalSectionDevice<'a, T> {
17+
/// Create a new Mutexdevice
18+
pub fn new(bus: &'a Mutex<RefCell<T>>) -> Self {
19+
Self { bus }
20+
}
21+
}
22+
23+
impl<'a, T> ErrorType for CriticalSectionDevice<'a, T>
24+
where
25+
T: I2c,
26+
{
27+
type Error = T::Error;
28+
}
29+
30+
impl<'a, T> I2c for CriticalSectionDevice<'a, T>
31+
where
32+
T: I2c,
33+
{
34+
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
35+
critical_section::with(|cs| {
36+
let bus = &mut *self.bus.borrow_ref_mut(cs);
37+
bus.read(address, read)
38+
})
39+
}
40+
41+
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
42+
critical_section::with(|cs| {
43+
let bus = &mut *self.bus.borrow_ref_mut(cs);
44+
bus.write(address, write)
45+
})
46+
}
47+
48+
fn write_read(
49+
&mut self,
50+
address: u8,
51+
write: &[u8],
52+
read: &mut [u8],
53+
) -> Result<(), Self::Error> {
54+
critical_section::with(|cs| {
55+
let bus = &mut *self.bus.borrow_ref_mut(cs);
56+
bus.write_read(address, write, read)
57+
})
58+
}
59+
60+
fn transaction(
61+
&mut self,
62+
address: u8,
63+
operations: &mut [embedded_hal::i2c::Operation<'_>],
64+
) -> Result<(), Self::Error> {
65+
critical_section::with(|cs| {
66+
let bus = &mut *self.bus.borrow_ref_mut(cs);
67+
bus.transaction(address, operations)
68+
})
69+
}
70+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//! `I2c` shared bus implementations.
2+
3+
mod refcell;
4+
pub use refcell::*;
5+
#[cfg(feature = "std")]
6+
mod mutex;
7+
#[cfg(feature = "std")]
8+
pub use mutex::*;
9+
mod critical_section;
10+
pub use self::critical_section::*;

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

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use embedded_hal::i2c::{ErrorType, I2c};
2+
use std::sync::Mutex;
3+
4+
/// `std` `Mutex`-based shared bus [`I2c`] implementation.
5+
///
6+
/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads,
7+
/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is
8+
/// it is only available in `std` targets.
9+
pub struct MutexDevice<'a, T> {
10+
bus: &'a Mutex<T>,
11+
}
12+
13+
impl<'a, T> MutexDevice<'a, T> {
14+
/// Create a new Mutexdevice
15+
pub fn new(bus: &'a Mutex<T>) -> Self {
16+
Self { bus }
17+
}
18+
}
19+
20+
impl<'a, T> ErrorType for MutexDevice<'a, T>
21+
where
22+
T: I2c,
23+
{
24+
type Error = T::Error;
25+
}
26+
27+
impl<'a, T> I2c for MutexDevice<'a, T>
28+
where
29+
T: I2c,
30+
{
31+
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
32+
let bus = &mut *self.bus.lock().unwrap();
33+
bus.read(address, read)
34+
}
35+
36+
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
37+
let bus = &mut *self.bus.lock().unwrap();
38+
bus.write(address, write)
39+
}
40+
41+
fn write_read(
42+
&mut self,
43+
address: u8,
44+
write: &[u8],
45+
read: &mut [u8],
46+
) -> Result<(), Self::Error> {
47+
let bus = &mut *self.bus.lock().unwrap();
48+
bus.write_read(address, write, read)
49+
}
50+
51+
fn transaction(
52+
&mut self,
53+
address: u8,
54+
operations: &mut [embedded_hal::i2c::Operation<'_>],
55+
) -> Result<(), Self::Error> {
56+
let bus = &mut *self.bus.lock().unwrap();
57+
bus.transaction(address, operations)
58+
}
59+
}

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

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use core::cell::RefCell;
2+
use embedded_hal::i2c::{ErrorType, I2c};
3+
4+
/// `RefCell`-based shared bus [`I2c`] implementation.
5+
///
6+
/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`,
7+
/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several
8+
/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead.
9+
pub struct RefCellDevice<'a, T> {
10+
bus: &'a RefCell<T>,
11+
}
12+
13+
impl<'a, T> RefCellDevice<'a, T> {
14+
/// Create a new RefCellDevice
15+
pub fn new(bus: &'a RefCell<T>) -> Self {
16+
Self { bus }
17+
}
18+
}
19+
20+
impl<'a, T> ErrorType for RefCellDevice<'a, T>
21+
where
22+
T: I2c,
23+
{
24+
type Error = T::Error;
25+
}
26+
27+
impl<'a, T> I2c for RefCellDevice<'a, T>
28+
where
29+
T: I2c,
30+
{
31+
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
32+
let bus = &mut *self.bus.borrow_mut();
33+
bus.read(address, read)
34+
}
35+
36+
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
37+
let bus = &mut *self.bus.borrow_mut();
38+
bus.write(address, write)
39+
}
40+
41+
fn write_read(
42+
&mut self,
43+
address: u8,
44+
write: &[u8],
45+
read: &mut [u8],
46+
) -> Result<(), Self::Error> {
47+
let bus = &mut *self.bus.borrow_mut();
48+
bus.write_read(address, write, read)
49+
}
50+
51+
fn transaction(
52+
&mut self,
53+
address: u8,
54+
operations: &mut [embedded_hal::i2c::Operation<'_>],
55+
) -> Result<(), Self::Error> {
56+
let bus = &mut *self.bus.borrow_mut();
57+
bus.transaction(address, operations)
58+
}
59+
}

embedded-hal-bus/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! For further details on these traits, please consult the [`embedded-hal` documentation](https://docs.rs/embedded-hal).
1313
1414
#![warn(missing_docs)]
15-
#![no_std]
15+
#![cfg_attr(not(feature = "std"), no_std)]
1616

17+
pub mod i2c;
1718
pub mod spi;

0 commit comments

Comments
 (0)