stm32f4xx_hal/gpio/
erased.rs

1use super::*;
2
3pub use AnyPin as ErasedPin;
4
5/// Fully erased pin
6///
7/// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section).
8pub struct AnyPin<MODE> {
9    // Bits 0-3: Pin, Bits 4-7: Port
10    pin_port: u8,
11    _mode: PhantomData<MODE>,
12}
13
14impl<MODE> fmt::Debug for AnyPin<MODE> {
15    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
16        formatter.write_fmt(format_args!(
17            "P({}{})<{}>",
18            self.port_id(),
19            self.pin_id(),
20            crate::stripped_type_name::<MODE>()
21        ))
22    }
23}
24
25#[cfg(feature = "defmt")]
26impl<MODE> defmt::Format for AnyPin<MODE> {
27    fn format(&self, f: defmt::Formatter) {
28        defmt::write!(
29            f,
30            "P({}{})<{}>",
31            self.port_id(),
32            self.pin_id(),
33            crate::stripped_type_name::<MODE>()
34        );
35    }
36}
37
38impl<MODE> PinExt for AnyPin<MODE> {
39    type Mode = MODE;
40
41    #[inline(always)]
42    fn pin_id(&self) -> u8 {
43        self.pin_port & 0x0f
44    }
45    #[inline(always)]
46    fn port_id(&self) -> u8 {
47        self.pin_port >> 4
48    }
49}
50
51impl<MODE> AnyPin<MODE> {
52    pub(crate) fn from_pin_port(pin_port: u8) -> Self {
53        Self {
54            pin_port,
55            _mode: PhantomData,
56        }
57    }
58    pub(crate) fn into_pin_port(self) -> u8 {
59        self.pin_port
60    }
61    pub(crate) fn new(port: u8, pin: u8) -> Self {
62        Self {
63            pin_port: port << 4 | pin,
64            _mode: PhantomData,
65        }
66    }
67
68    /// Convert type erased pin to `Pin` with fixed type
69    pub fn restore<const P: char, const N: u8>(self) -> Pin<P, N, MODE> {
70        assert_eq!(self.port_id(), P as u8 - b'A');
71        assert_eq!(self.pin_id(), N);
72        Pin::new()
73    }
74
75    #[inline]
76    pub(crate) fn block(&self) -> &crate::pac::gpioa::RegisterBlock {
77        // This function uses pointer arithmetic instead of branching to be more efficient
78
79        // The logic relies on the following assumptions:
80        // - GPIOA register is available on all chips
81        // - all gpio register blocks have the same layout
82        // - consecutive gpio register blocks have the same offset between them, namely 0x0400
83        // - ErasedPin::new was called with a valid port
84
85        // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910
86        const GPIO_REGISTER_OFFSET: usize = 0x0400;
87
88        let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize;
89        let block_ptr =
90            (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock;
91
92        unsafe { &*block_ptr }
93    }
94}
95
96impl<MODE> AnyPin<Output<MODE>> {
97    /// Drives the pin high
98    #[inline(always)]
99    pub fn set_high(&mut self) {
100        self.block().bsrr().write(|w| w.bs(self.pin_id()).set_bit());
101    }
102
103    /// Drives the pin low
104    #[inline(always)]
105    pub fn set_low(&mut self) {
106        self.block().bsrr().write(|w| w.br(self.pin_id()).set_bit());
107    }
108
109    /// Is the pin in drive high or low mode?
110    #[inline(always)]
111    pub fn get_state(&self) -> PinState {
112        if self.is_set_low() {
113            PinState::Low
114        } else {
115            PinState::High
116        }
117    }
118
119    /// Drives the pin high or low depending on the provided value
120    #[inline(always)]
121    pub fn set_state(&mut self, state: PinState) {
122        match state {
123            PinState::Low => self.set_low(),
124            PinState::High => self.set_high(),
125        }
126    }
127
128    /// Is the pin in drive high mode?
129    #[inline(always)]
130    pub fn is_set_high(&self) -> bool {
131        !self.is_set_low()
132    }
133
134    /// Is the pin in drive low mode?
135    #[inline(always)]
136    pub fn is_set_low(&self) -> bool {
137        self.block().odr().read().odr(self.pin_id()).bit_is_clear()
138    }
139
140    /// Toggle pin output
141    #[inline(always)]
142    pub fn toggle(&mut self) {
143        if self.is_set_low() {
144            self.set_high()
145        } else {
146            self.set_low()
147        }
148    }
149}
150
151impl<MODE> AnyPin<MODE>
152where
153    MODE: marker::Readable,
154{
155    /// Is the input pin high?
156    #[inline(always)]
157    pub fn is_high(&self) -> bool {
158        !self.is_low()
159    }
160
161    /// Is the input pin low?
162    #[inline(always)]
163    pub fn is_low(&self) -> bool {
164        self.block().idr().read().idr(self.pin_id()).bit_is_clear()
165    }
166}