1//! Asynchronous shared I2C bus
2//!
3//! # Example (nrf52)
4//!
5//! ```rust,ignore
6//! use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
7//! use embassy_sync::mutex::Mutex;
8//! use embassy_sync::blocking_mutex::raw::NoopRawMutex;
9//!
10//! static I2C_BUS: StaticCell<Mutex<NoopRawMutex, Twim<TWISPI0>>> = StaticCell::new();
11//! let config = twim::Config::default();
12//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);
13//! let i2c_bus = Mutex::new(i2c);
14//! let i2c_bus = I2C_BUS.init(i2c_bus);
15//!
16//! // Device 1, using embedded-hal-async compatible driver for QMC5883L compass
17//! let i2c_dev1 = I2cDevice::new(i2c_bus);
18//! let compass = QMC5883L::new(i2c_dev1).await.unwrap();
19//!
20//! // Device 2, using embedded-hal-async compatible driver for Mpu6050 accelerometer
21//! let i2c_dev2 = I2cDevice::new(i2c_bus);
22//! let mpu = Mpu6050::new(i2c_dev2);
23//! ```
24
25use embassy_sync::blocking_mutex::raw::RawMutex;
26use embassy_sync::mutex::Mutex;
27use embedded_hal_async::i2c;
28
29use crate::shared_bus::I2cDeviceError;
30use crate::SetConfig;
31
32/// I2C device on a shared bus.
33pub struct I2cDevice<'a, M: RawMutex, BUS> {
34 bus: &'a Mutex<M, BUS>,
35}
36
37impl<'a, M: RawMutex, BUS> I2cDevice<'a, M, BUS> {
38 /// Create a new `I2cDevice`.
39 pub fn new(bus: &'a Mutex<M, BUS>) -> Self {
40 Self { bus }
41 }
42}
43
44impl<'a, M: RawMutex, BUS> i2c::ErrorType for I2cDevice<'a, M, BUS>
45where
46 BUS: i2c::ErrorType,
47{
48 type Error = I2cDeviceError<BUS::Error>;
49}
50
51impl<M, BUS> i2c::I2c for I2cDevice<'_, M, BUS>
52where
53 M: RawMutex + 'static,
54 BUS: i2c::I2c + 'static,
55{
56 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
57 let mut bus = self.bus.lock().await;
58 bus.read(address, read).await.map_err(I2cDeviceError::I2c)?;
59 Ok(())
60 }
61
62 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
63 let mut bus = self.bus.lock().await;
64 bus.write(address, write).await.map_err(I2cDeviceError::I2c)?;
65 Ok(())
66 }
67
68 async fn write_read(
69 &mut self,
70 address: u8,
71 write: &[u8],
72 read: &mut [u8],
73 ) -> Result<(), I2cDeviceError<BUS::Error>> {
74 let mut bus = self.bus.lock().await;
75 bus.write_read(address, write, read)
76 .await
77 .map_err(I2cDeviceError::I2c)?;
78 Ok(())
79 }
80
81 async fn transaction(
82 &mut self,
83 address: u8,
84 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
85 ) -> Result<(), I2cDeviceError<BUS::Error>> {
86 let mut bus = self.bus.lock().await;
87 bus.transaction(address, operations)
88 .await
89 .map_err(I2cDeviceError::I2c)?;
90 Ok(())
91 }
92}
93
94/// I2C device on a shared bus, with its own configuration.
95///
96/// This is like [`I2cDevice`], with an additional bus configuration that's applied
97/// to the bus before each use using [`SetConfig`]. This allows different
98/// devices on the same bus to use different communication settings.
99pub struct I2cDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig> {
100 bus: &'a Mutex<M, BUS>,
101 config: BUS::Config,
102}
103
104impl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> {
105 /// Create a new `I2cDeviceWithConfig`.
106 pub fn new(bus: &'a Mutex<M, BUS>, config: BUS::Config) -> Self {
107 Self { bus, config }
108 }
109
110 /// Change the device's config at runtime
111 pub fn set_config(&mut self, config: BUS::Config) {
112 self.config = config;
113 }
114}
115
116impl<'a, M, BUS> i2c::ErrorType for I2cDeviceWithConfig<'a, M, BUS>
117where
118 BUS: i2c::ErrorType,
119 M: RawMutex,
120 BUS: SetConfig,
121{
122 type Error = I2cDeviceError<BUS::Error>;
123}
124
125impl<M, BUS> i2c::I2c for I2cDeviceWithConfig<'_, M, BUS>
126where
127 M: RawMutex + 'static,
128 BUS: i2c::I2c + SetConfig + 'static,
129{
130 async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
131 let mut bus = self.bus.lock().await;
132 bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
133 bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
134 Ok(())
135 }
136
137 async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
138 let mut bus = self.bus.lock().await;
139 bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
140 bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
141 Ok(())
142 }
143
144 async fn write_read(
145 &mut self,
146 address: u8,
147 wr_buffer: &[u8],
148 rd_buffer: &mut [u8],
149 ) -> Result<(), I2cDeviceError<BUS::Error>> {
150 let mut bus = self.bus.lock().await;
151 bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
152 bus.write_read(address, wr_buffer, rd_buffer)
153 .await
154 .map_err(I2cDeviceError::I2c)?;
155 Ok(())
156 }
157
158 async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
159 let mut bus = self.bus.lock().await;
160 bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
161 bus.transaction(address, operations)
162 .await
163 .map_err(I2cDeviceError::I2c)?;
164 Ok(())
165 }
166}
167