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}