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 | |
25 | use embassy_sync::blocking_mutex::raw::RawMutex; |
26 | use embassy_sync::mutex::Mutex; |
27 | use embedded_hal_async::i2c; |
28 | |
29 | use crate::shared_bus::I2cDeviceError; |
30 | use crate::SetConfig; |
31 | |
32 | /// I2C device on a shared bus. |
33 | pub struct I2cDevice<'a, M: RawMutex, BUS> { |
34 | bus: &'a Mutex<M, BUS>, |
35 | } |
36 | |
37 | impl<'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 | |
44 | impl<'a, M: RawMutex, BUS> i2c::ErrorType for I2cDevice<'a, M, BUS> |
45 | where |
46 | BUS: i2c::ErrorType, |
47 | { |
48 | type Error = I2cDeviceError<BUS::Error>; |
49 | } |
50 | |
51 | impl<M, BUS> i2c::I2c for I2cDevice<'_, M, BUS> |
52 | where |
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. |
99 | pub struct I2cDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig> { |
100 | bus: &'a Mutex<M, BUS>, |
101 | config: BUS::Config, |
102 | } |
103 | |
104 | impl<'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 | |
116 | impl<'a, M, BUS> i2c::ErrorType for I2cDeviceWithConfig<'a, M, BUS> |
117 | where |
118 | BUS: i2c::ErrorType, |
119 | M: RawMutex, |
120 | BUS: SetConfig, |
121 | { |
122 | type Error = I2cDeviceError<BUS::Error>; |
123 | } |
124 | |
125 | impl<M, BUS> i2c::I2c for I2cDeviceWithConfig<'_, M, BUS> |
126 | where |
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 | |