shared_bus/manager.rs
1/// "Manager" for a shared bus.
2///
3/// The manager owns the original bus peripheral (wrapped inside a mutex) and hands out proxies
4/// which can be used by device drivers for accessing the bus. Certain bus proxies can only be
5/// created with restrictions (see the individual methods for details).
6///
7/// Usually the type-aliases defined in this crate should be used instead of `BusManager` directly.
8/// Otherwise, the mutex type needs to be specified explicitly. Here is an overview of aliases
9/// (some are only available if a certain feature is enabled):
10///
11/// | Bus Manager | Mutex Type | Feature Name | Notes |
12/// | --- | --- | --- | --- |
13/// | [`BusManagerSimple`] | `shared_bus::NullMutex` | always available | For sharing within a single execution context. |
14/// | [`BusManagerStd`] | `std::sync::Mutex` | `std` | For platforms where `std` is available. |
15/// | [`BusManagerCortexM`] | `cortex_m::interrupt::Mutex` | `cortex-m` | For Cortex-M platforms; Uses a critcal section (i.e. turns off interrupts during bus transactions). |
16///
17/// [`BusManagerSimple`]: ./type.BusManagerSimple.html
18/// [`BusManagerStd`]: ./type.BusManagerStd.html
19/// [`BusManagerCortexM`]: ./type.BusManagerCortexM.html
20///
21/// # Constructing a `BusManager`
22/// There are two ways to instanciate a bus manager. Which one to use depends on the kind of
23/// sharing that is intended.
24///
25/// 1. When all bus users live in the same task/thread, a `BusManagerSimple` can be used:
26///
27/// ```
28/// # use embedded_hal::blocking::i2c;
29/// # use embedded_hal::blocking::i2c::Write as _;
30/// # struct MyDevice<T>(T);
31/// # impl<T: i2c::Write> MyDevice<T> {
32/// # pub fn new(t: T) -> Self { MyDevice(t) }
33/// # pub fn do_something_on_the_bus(&mut self) {
34/// # self.0.write(0xab, &[0x00]);
35/// # }
36/// # }
37/// #
38/// # fn _example(i2c: impl i2c::Write) {
39/// // For example:
40/// // let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 90.khz(), clocks, &mut rcc.apb1);
41///
42/// let bus = shared_bus::BusManagerSimple::new(i2c);
43///
44/// let mut proxy1 = bus.acquire_i2c();
45/// let mut my_device = MyDevice::new(bus.acquire_i2c());
46///
47/// proxy1.write(0x39, &[0xc0, 0xff, 0xee]);
48/// my_device.do_something_on_the_bus();
49/// # }
50/// ```
51///
52/// 2. When users are in different execution contexts, a proper mutex type is needed and the
53/// manager must be made `static` to ensure it lives long enough. For this, `shared-bus`
54/// provides a number of macros creating such a `static` instance:
55///
56/// ```
57/// # struct MyDevice<T>(T);
58/// # impl<T> MyDevice<T> {
59/// # pub fn new(t: T) -> Self { MyDevice(t) }
60/// # pub fn do_something_on_the_bus(&mut self) { }
61/// # }
62/// #
63/// # struct SomeI2cBus;
64/// # let i2c = SomeI2cBus;
65/// // For example:
66/// // let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 90.khz(), clocks, &mut rcc.apb1);
67///
68/// // The bus is a 'static reference -> it lives forever and references can be
69/// // shared with other threads.
70/// let bus: &'static _ = shared_bus::new_std!(SomeI2cBus = i2c).unwrap();
71///
72/// let mut proxy1 = bus.acquire_i2c();
73/// let mut my_device = MyDevice::new(bus.acquire_i2c());
74///
75/// // We can easily move a proxy to another thread:
76/// # let t =
77/// std::thread::spawn(move || {
78/// my_device.do_something_on_the_bus();
79/// });
80/// # t.join().unwrap();
81/// ```
82///
83/// For other platforms, similar macros exist (e.g. [`new_cortexm!()`]).
84///
85/// [`new_cortexm!()`]: ./macro.new_cortexm.html
86#[derive(Debug)]
87pub struct BusManager<M> {
88 mutex: M,
89}
90
91impl<M: crate::BusMutex> BusManager<M> {
92 /// Create a new bus manager for a bus.
93 ///
94 /// See the documentation for `BusManager` for more details.
95 pub fn new(bus: M::Bus) -> Self {
96 let mutex = M::create(bus);
97
98 BusManager { mutex }
99 }
100}
101
102impl<M: crate::BusMutex> BusManager<M> {
103 /// Acquire an [`I2cProxy`] for this bus.
104 ///
105 /// [`I2cProxy`]: ./struct.I2cProxy.html
106 ///
107 /// The returned proxy object can then be used for accessing the bus by e.g. a driver:
108 ///
109 /// ```
110 /// # use embedded_hal::blocking::i2c;
111 /// # use embedded_hal::blocking::i2c::Write as _;
112 /// # struct MyDevice<T>(T);
113 /// # impl<T: i2c::Write> MyDevice<T> {
114 /// # pub fn new(t: T) -> Self { MyDevice(t) }
115 /// # pub fn do_something_on_the_bus(&mut self) {
116 /// # self.0.write(0xab, &[0x00]);
117 /// # }
118 /// # }
119 /// #
120 /// # fn _example(i2c: impl i2c::Write) {
121 /// let bus = shared_bus::BusManagerSimple::new(i2c);
122 ///
123 /// let mut proxy1 = bus.acquire_i2c();
124 /// let mut my_device = MyDevice::new(bus.acquire_i2c());
125 ///
126 /// proxy1.write(0x39, &[0xc0, 0xff, 0xee]);
127 /// my_device.do_something_on_the_bus();
128 /// # }
129 /// ```
130 pub fn acquire_i2c<'a>(&'a self) -> crate::I2cProxy<'a, M> {
131 crate::I2cProxy { mutex: &self.mutex }
132 }
133
134 /// Acquire an [`AdcProxy`] for this hardware block.
135 ///
136 /// [`AdcProxy`]: ./struct.AdcProxy.html
137 ///
138 /// The returned proxy object can then be used for accessing the bus by e.g. a driver:
139 ///
140 /// ```ignore
141 /// // For example:
142 /// // let ch0 = gpioa.pa0.into_analog(&mut gpioa.crl);
143 /// // let ch1 = gpioa.pa1.into_analog(&mut gpioa.crl);
144 /// // let adc = Adc::adc1(p.ADC1, &mut rcc.apb2, clocks);
145 ///
146 /// let adc_bus: &'static _ = shared_bus::new_cortexm!(Adc<ADC1> = adc).unwrap();
147 /// let mut proxy1 = adc_bus.acquire_adc();
148 /// let mut proxy2 = adc_bus.acquire_adc();
149 ///
150 /// proxy1.read(ch0).unwrap();
151 /// proxy2.read(ch1).unwrap();
152 ///
153 /// ```
154
155 pub fn acquire_adc<'a>(&'a self) -> crate::AdcProxy<'a, M> {
156 crate::AdcProxy { mutex: &self.mutex }
157 }
158}
159
160impl<T> BusManager<crate::NullMutex<T>> {
161 /// Acquire an [`SpiProxy`] for this bus.
162 ///
163 /// **Note**: SPI Proxies can only be created from [`BusManagerSimple`] (= bus managers using
164 /// the [`NullMutex`]). See [`SpiProxy`] for more details why.
165 ///
166 /// [`BusManagerSimple`]: ./type.BusManagerSimple.html
167 /// [`NullMutex`]: ./struct.NullMutex.html
168 /// [`SpiProxy`]: ./struct.SpiProxy.html
169 ///
170 /// The returned proxy object can then be used for accessing the bus by e.g. a driver:
171 ///
172 /// ```
173 /// # use embedded_hal::blocking::spi;
174 /// # use embedded_hal::digital::v2;
175 /// # use embedded_hal::blocking::spi::Write as _;
176 /// # struct MyDevice<T>(T);
177 /// # impl<T: spi::Write<u8>> MyDevice<T> {
178 /// # pub fn new(t: T) -> Self { MyDevice(t) }
179 /// # pub fn do_something_on_the_bus(&mut self) {
180 /// # self.0.write(&[0x00]);
181 /// # }
182 /// # }
183 /// #
184 /// # fn _example(mut cs1: impl v2::OutputPin, spi: impl spi::Write<u8>) {
185 /// let bus = shared_bus::BusManagerSimple::new(spi);
186 ///
187 /// let mut proxy1 = bus.acquire_spi();
188 /// let mut my_device = MyDevice::new(bus.acquire_spi());
189 ///
190 /// // Chip-select needs to be managed manually
191 /// cs1.set_high();
192 /// proxy1.write(&[0xc0, 0xff, 0xee]);
193 /// cs1.set_low();
194 ///
195 /// my_device.do_something_on_the_bus();
196 /// # }
197 /// ```
198 pub fn acquire_spi<'a>(&'a self) -> crate::SpiProxy<'a, crate::NullMutex<T>> {
199 crate::SpiProxy {
200 mutex: &self.mutex,
201 _u: core::marker::PhantomData,
202 }
203 }
204}