stm32l4xx_hal/gpio/
erased.rs

1use super::*;
2
3pub type EPin<MODE> = ErasedPin<MODE>;
4
5/// Fully erased pin
6///
7/// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section).
8pub struct ErasedPin<MODE> {
9    // Bits 0-3: Pin, Bits 4-7: Port
10    pin_port: u8,
11    _mode: PhantomData<MODE>,
12}
13
14impl<MODE> PinExt for ErasedPin<MODE> {
15    type Mode = MODE;
16
17    #[inline(always)]
18    fn pin_id(&self) -> u8 {
19        self.pin_port & 0x0f
20    }
21    #[inline(always)]
22    fn port_id(&self) -> u8 {
23        self.pin_port >> 4
24    }
25}
26
27impl<MODE> ErasedPin<MODE> {
28    pub(crate) fn new(port: u8, pin: u8) -> Self {
29        Self {
30            pin_port: port << 4 | pin,
31            _mode: PhantomData,
32        }
33    }
34
35    #[inline]
36    fn block(&self) -> &crate::pac::gpioa::RegisterBlock {
37        // This function uses pointer arithmetic instead of branching to be more efficient
38
39        // The logic relies on the following assumptions:
40        // - GPIOA register is available on all chips
41        // - all gpio register blocks have the same layout
42        // - consecutive gpio register blocks have the same offset between them, namely 0x0400
43        // - ErasedPin::new was called with a valid port
44
45        // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910
46        const GPIO_REGISTER_OFFSET: usize = 0x0400;
47
48        let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize;
49        let block_ptr =
50            (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock;
51
52        unsafe { &*block_ptr }
53    }
54}
55
56impl<MODE> ErasedPin<Output<MODE>> {
57    #[inline(always)]
58    pub fn set_high(&mut self) {
59        // NOTE(unsafe) atomic write to a stateless register
60        unsafe { self.block().bsrr.write(|w| w.bits(1 << self.pin_id())) };
61    }
62
63    #[inline(always)]
64    pub fn set_low(&mut self) {
65        // NOTE(unsafe) atomic write to a stateless register
66        unsafe {
67            self.block()
68                .bsrr
69                .write(|w| w.bits(1 << (self.pin_id() + 16)))
70        };
71    }
72
73    #[inline(always)]
74    pub fn get_state(&self) -> PinState {
75        if self.is_set_low() {
76            PinState::Low
77        } else {
78            PinState::High
79        }
80    }
81
82    #[inline(always)]
83    pub fn set_state(&mut self, state: PinState) {
84        match state {
85            PinState::Low => self.set_low(),
86            PinState::High => self.set_high(),
87        }
88    }
89
90    #[inline(always)]
91    pub fn is_set_high(&self) -> bool {
92        !self.is_set_low()
93    }
94
95    #[inline(always)]
96    pub fn is_set_low(&self) -> bool {
97        self.block().odr.read().bits() & (1 << self.pin_id()) == 0
98    }
99
100    #[inline(always)]
101    pub fn toggle(&mut self) {
102        if self.is_set_low() {
103            self.set_high()
104        } else {
105            self.set_low()
106        }
107    }
108}
109
110impl<MODE> OutputPin for ErasedPin<Output<MODE>> {
111    type Error = core::convert::Infallible;
112
113    #[inline(always)]
114    fn set_high(&mut self) -> Result<(), Self::Error> {
115        self.set_high();
116        Ok(())
117    }
118
119    #[inline(always)]
120    fn set_low(&mut self) -> Result<(), Self::Error> {
121        self.set_low();
122        Ok(())
123    }
124}
125
126impl<MODE> StatefulOutputPin for ErasedPin<Output<MODE>> {
127    #[inline(always)]
128    fn is_set_high(&self) -> Result<bool, Self::Error> {
129        Ok(self.is_set_high())
130    }
131
132    #[inline(always)]
133    fn is_set_low(&self) -> Result<bool, Self::Error> {
134        Ok(self.is_set_low())
135    }
136}
137
138impl<MODE> ToggleableOutputPin for ErasedPin<Output<MODE>> {
139    type Error = Infallible;
140
141    #[inline(always)]
142    fn toggle(&mut self) -> Result<(), Self::Error> {
143        self.toggle();
144        Ok(())
145    }
146}
147
148impl ErasedPin<Output<OpenDrain>> {
149    #[inline(always)]
150    pub fn is_high(&self) -> bool {
151        !self.is_low()
152    }
153
154    #[inline(always)]
155    pub fn is_low(&self) -> bool {
156        self.block().idr.read().bits() & (1 << self.pin_id()) == 0
157    }
158}
159
160impl InputPin for ErasedPin<Output<OpenDrain>> {
161    type Error = core::convert::Infallible;
162
163    #[inline(always)]
164    fn is_high(&self) -> Result<bool, Self::Error> {
165        Ok(self.is_high())
166    }
167
168    #[inline(always)]
169    fn is_low(&self) -> Result<bool, Self::Error> {
170        Ok(self.is_low())
171    }
172}
173
174impl<MODE> ErasedPin<Input<MODE>> {
175    #[inline(always)]
176    pub fn is_high(&self) -> bool {
177        !self.is_low()
178    }
179
180    #[inline(always)]
181    pub fn is_low(&self) -> bool {
182        self.block().idr.read().bits() & (1 << self.pin_id()) == 0
183    }
184}
185
186impl<MODE> InputPin for ErasedPin<Input<MODE>> {
187    type Error = core::convert::Infallible;
188
189    #[inline(always)]
190    fn is_high(&self) -> Result<bool, Self::Error> {
191        Ok(self.is_high())
192    }
193
194    #[inline(always)]
195    fn is_low(&self) -> Result<bool, Self::Error> {
196        Ok(self.is_low())
197    }
198}