port_expander/
pin.rs

1use core::marker::PhantomData;
2use embedded_hal::digital::{self as hal_digital};
3
4/// Representation of a port-expander pin.
5///
6/// `Pin` is not constructed directly, this type is created by instanciating a port-expander and
7/// then getting access to all its pins using the `.split()` method.
8pub struct Pin<'a, MODE, MUTEX> {
9    pin_mask: u32,
10    port_driver: &'a MUTEX,
11    _m: PhantomData<MODE>,
12}
13
14impl<'a, MODE, MUTEX, PD> Pin<'a, MODE, MUTEX>
15where
16    PD: crate::PortDriver,
17    MUTEX: crate::PortMutex<Port = PD>,
18{
19    pub(crate) fn new(pin_number: u8, port_driver: &'a MUTEX) -> Self {
20        assert!(pin_number < 32);
21        Self {
22            pin_mask: 1 << pin_number,
23            port_driver,
24            _m: PhantomData,
25        }
26    }
27
28    pub fn pin_mask(&self) -> u32 {
29        self.pin_mask
30    }
31
32    pub(crate) fn port_driver(&self) -> &MUTEX {
33        self.port_driver
34    }
35
36    pub fn access_port_driver<F, R>(&self, f: F) -> R
37    where
38        F: FnOnce(&mut PD) -> R,
39    {
40        self.port_driver.lock(|pd| f(pd))
41    }
42}
43
44/// Error type for [`Pin`] which implements [`embedded_hal::digital::Error`].
45#[derive(Debug)]
46pub struct PinError<PDE> {
47    driver_error: PDE,
48}
49
50impl<PDE> PinError<PDE> {
51    /// The upstream port driver error that occurred
52    pub fn driver_error(&self) -> &PDE {
53        &self.driver_error
54    }
55}
56
57impl<PDE> hal_digital::Error for PinError<PDE>
58where
59    PDE: core::fmt::Debug,
60{
61    fn kind(&self) -> hal_digital::ErrorKind {
62        hal_digital::ErrorKind::Other
63    }
64}
65
66impl<PDE> From<PDE> for PinError<PDE> {
67    fn from(value: PDE) -> Self {
68        Self {
69            driver_error: value,
70        }
71    }
72}
73
74impl<'a, MODE, MUTEX, PD> hal_digital::ErrorType for Pin<'a, MODE, MUTEX>
75where
76    PD: crate::PortDriver,
77    PD::Error: core::fmt::Debug,
78    MUTEX: crate::PortMutex<Port = PD>,
79{
80    type Error = PinError<PD::Error>;
81}
82
83impl<'a, MODE, MUTEX, PD> Pin<'a, MODE, MUTEX>
84where
85    PD: crate::PortDriver + crate::PortDriverTotemPole,
86    MUTEX: crate::PortMutex<Port = PD>,
87{
88    /// Configure this pin as an input.
89    ///
90    /// The exact electrical details depend on the port-expander device which is used.
91    pub fn into_input(self) -> Result<Pin<'a, crate::mode::Input, MUTEX>, PinError<PD::Error>> {
92        self.port_driver
93            .lock(|drv| drv.set_direction(self.pin_mask, crate::Direction::Input, false))?;
94        Ok(Pin {
95            pin_mask: self.pin_mask,
96            port_driver: self.port_driver,
97            _m: PhantomData,
98        })
99    }
100
101    /// Configure this pin as an output with an initial LOW state.
102    ///
103    /// The LOW state is, as long as he port-expander chip allows this, entered without any
104    /// electrical glitch.
105    pub fn into_output(self) -> Result<Pin<'a, crate::mode::Output, MUTEX>, PinError<PD::Error>> {
106        self.port_driver
107            .lock(|drv| drv.set_direction(self.pin_mask, crate::Direction::Output, false))?;
108        Ok(Pin {
109            pin_mask: self.pin_mask,
110            port_driver: self.port_driver,
111            _m: PhantomData,
112        })
113    }
114
115    /// Configure this pin as an output with an initial HIGH state.
116    ///
117    /// The HIGH state is, as long as he port-expander chip allows this, entered without any
118    /// electrical glitch.
119    pub fn into_output_high(
120        self,
121    ) -> Result<Pin<'a, crate::mode::Output, MUTEX>, PinError<PD::Error>> {
122        self.port_driver
123            .lock(|drv| drv.set_direction(self.pin_mask, crate::Direction::Output, true))?;
124        Ok(Pin {
125            pin_mask: self.pin_mask,
126            port_driver: self.port_driver,
127            _m: PhantomData,
128        })
129    }
130}
131
132impl<'a, MODE, MUTEX, PD> Pin<'a, MODE, MUTEX>
133where
134    PD: crate::PortDriver + crate::PortDriverPolarity,
135    MUTEX: crate::PortMutex<Port = PD>,
136{
137    /// Turn on hardware polarity inversion for this pin.
138    pub fn into_inverted(self) -> Result<Self, PinError<PD::Error>> {
139        self.port_driver
140            .lock(|drv| drv.set_polarity(self.pin_mask, true))?;
141        Ok(self)
142    }
143
144    /// Set hardware polarity inversion for this pin.
145    pub fn set_inverted(&mut self, inverted: bool) -> Result<(), PinError<PD::Error>> {
146        self.port_driver
147            .lock(|drv| drv.set_polarity(self.pin_mask, inverted))?;
148        Ok(())
149    }
150}
151
152impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX>
153where
154    PD: crate::PortDriver,
155    MUTEX: crate::PortMutex<Port = PD>,
156{
157    /// Read the pin's input state and return `true` if it is HIGH.
158    pub fn is_high(&self) -> Result<bool, PinError<PD::Error>> {
159        self.port_driver
160            .lock(|drv| Ok(drv.get(self.pin_mask, 0)? == self.pin_mask))
161    }
162
163    /// Read the pin's input state and return `true` if it is LOW.
164    pub fn is_low(&self) -> Result<bool, PinError<PD::Error>> {
165        self.port_driver
166            .lock(|drv| Ok(drv.get(0, self.pin_mask)? == self.pin_mask))
167    }
168}
169
170impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX>
171where
172    PD: crate::PortDriver + crate::PortDriverPullUp,
173    MUTEX: crate::PortMutex<Port = PD>,
174{
175    /// Enable/Disable pull-up resistors for this pin.
176    ///
177    /// If `enable` is `true`, the pull-up resistor is enabled, otherwise the pin is configured as floating input.
178    pub fn enable_pull_up(&mut self, enable: bool) -> Result<(), PinError<PD::Error>> {
179        self.port_driver
180            .lock(|drv| drv.set_pull_up(self.pin_mask, enable))?;
181        Ok(())
182    }
183}
184
185impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX>
186where
187    PD: crate::PortDriver + crate::PortDriverPullDown,
188    MUTEX: crate::PortMutex<Port = PD>,
189{
190    /// Enable/Disable pull-down resistors for this pin.
191    ///
192    /// If `enable` is `true`, the pull-down resistor is enabled, otherwise the pin is configured as floating input.
193    pub fn enable_pull_down(&mut self, enable: bool) -> Result<(), PinError<PD::Error>> {
194        self.port_driver
195            .lock(|drv| drv.set_pull_down(self.pin_mask, enable))?;
196        Ok(())
197    }
198}
199
200impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> hal_digital::InputPin for Pin<'a, MODE, MUTEX>
201where
202    PD: crate::PortDriver,
203    <PD as crate::PortDriver>::Error: core::fmt::Debug,
204    MUTEX: crate::PortMutex<Port = PD>,
205{
206    fn is_high(&mut self) -> Result<bool, Self::Error> {
207        Pin::is_high(self)
208    }
209
210    fn is_low(&mut self) -> Result<bool, Self::Error> {
211        Pin::is_low(self)
212    }
213}
214
215impl<'a, MODE: crate::mode::HasOutput, MUTEX, PD> Pin<'a, MODE, MUTEX>
216where
217    PD: crate::PortDriver,
218    MUTEX: crate::PortMutex<Port = PD>,
219{
220    /// Set the pin's output state to HIGH.
221    ///
222    /// Note that this can have different electrical meanings depending on the port-expander chip.
223    pub fn set_high(&mut self) -> Result<(), PinError<PD::Error>> {
224        self.port_driver.lock(|drv| drv.set(self.pin_mask, 0))?;
225        Ok(())
226    }
227
228    /// Set the pin's output state to LOW.
229    ///
230    /// Note that this can have different electrical meanings depending on the port-expander chip.
231    pub fn set_low(&mut self) -> Result<(), PinError<PD::Error>> {
232        self.port_driver.lock(|drv| drv.set(0, self.pin_mask))?;
233        Ok(())
234    }
235
236    /// Return `true` if the pin's output state is HIGH.
237    ///
238    /// This method does **not** read the pin's electrical state.
239    pub fn is_set_high(&self) -> Result<bool, PinError<PD::Error>> {
240        self.port_driver
241            .lock(|drv| Ok(drv.is_set(self.pin_mask, 0)? == self.pin_mask))
242    }
243
244    /// Return `true` if the pin's output state is LOW.
245    ///
246    /// This method does **not** read the pin's electrical state.
247    pub fn is_set_low(&self) -> Result<bool, PinError<PD::Error>> {
248        self.port_driver
249            .lock(|drv| Ok(drv.is_set(0, self.pin_mask)? == self.pin_mask))
250    }
251
252    /// Toggle the pin's output state.
253    pub fn toggle(&mut self) -> Result<(), PinError<PD::Error>> {
254        self.port_driver.lock(|drv| drv.toggle(self.pin_mask))?;
255        Ok(())
256    }
257}
258
259impl<'a, MODE: crate::mode::HasOutput, MUTEX, PD> hal_digital::OutputPin for Pin<'a, MODE, MUTEX>
260where
261    PD: crate::PortDriver,
262    <PD as crate::PortDriver>::Error: core::fmt::Debug,
263    MUTEX: crate::PortMutex<Port = PD>,
264{
265    fn set_low(&mut self) -> Result<(), Self::Error> {
266        Pin::set_low(self)
267    }
268
269    fn set_high(&mut self) -> Result<(), Self::Error> {
270        Pin::set_high(self)
271    }
272}
273
274impl<'a, MODE: crate::mode::HasOutput, MUTEX, PD> hal_digital::StatefulOutputPin
275    for Pin<'a, MODE, MUTEX>
276where
277    PD: crate::PortDriver,
278    <PD as crate::PortDriver>::Error: core::fmt::Debug,
279    MUTEX: crate::PortMutex<Port = PD>,
280{
281    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
282        Pin::is_set_high(self)
283    }
284
285    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
286        Pin::is_set_low(self)
287    }
288
289    fn toggle(&mut self) -> Result<(), Self::Error> {
290        Pin::toggle(self)
291    }
292}