e310x_hal/
gpio.rs

1//! General Purpose I/O
2
3use core::marker::PhantomData;
4
5use portable_atomic::{AtomicU32, Ordering};
6
7/// GpioExt trait extends the GPIO0 peripheral.
8pub trait GpioExt {
9    /// The parts to split the GPIO into.
10    type Parts;
11
12    /// Splits the GPIO block into independent pins and registers.
13    fn split(self) -> Self::Parts;
14}
15
16/// Unknown mode (type state)
17pub struct Unknown;
18
19/// Input mode (type state)
20pub struct Input<MODE> {
21    _mode: PhantomData<MODE>,
22}
23
24/// Floating input (type state)
25pub struct Floating;
26/// Pulled up input (type state)
27pub struct PullUp;
28
29/// Output mode (type state)
30pub struct Output<MODE> {
31    _mode: PhantomData<MODE>,
32}
33
34/// Regular output mode (type state)
35pub struct Regular<INVERT> {
36    _mode: PhantomData<INVERT>,
37}
38
39/// High current mode (type state)
40pub struct Drive<INVERT> {
41    _mode: PhantomData<INVERT>,
42}
43
44/// Alternate function 0 (type state)
45pub struct IOF0<INVERT> {
46    _mode: PhantomData<INVERT>,
47}
48
49/// Alternate function 1 (type state)
50pub struct IOF1<INVERT> {
51    _mode: PhantomData<INVERT>,
52}
53
54/// Non-inverted output mode (type state)
55pub struct NoInvert;
56
57/// Invert output mode (type state)
58pub struct Invert;
59
60trait PinIndex {
61    const INDEX: usize;
62}
63
64#[inline(always)]
65fn atomic_set_bit(r: &AtomicU32, index: usize, bit: bool) {
66    let mask = 1 << (index & 31);
67    match bit {
68        true => r.fetch_or(mask, Ordering::SeqCst),
69        false => r.fetch_and(!mask, Ordering::SeqCst),
70    };
71}
72
73trait PeripheralAccess {
74    fn peripheral() -> e310x::Gpio0;
75
76    fn input_value(index: usize) -> bool {
77        let p = Self::peripheral();
78        (p.input_val().read().bits() >> (index & 31) & 1) != 0
79    }
80
81    fn set_input_en(index: usize, bit: bool) {
82        let p = Self::peripheral();
83        let r: &AtomicU32 = unsafe { core::mem::transmute(p.input_en()) };
84        atomic_set_bit(r, index, bit);
85    }
86
87    fn set_output_en(index: usize, bit: bool) {
88        let p = Self::peripheral();
89        let r: &AtomicU32 = unsafe { core::mem::transmute(p.output_en()) };
90        atomic_set_bit(r, index, bit);
91    }
92
93    fn output_value(index: usize) -> bool {
94        let p = Self::peripheral();
95        ((p.output_val().read().bits() >> (index & 31)) & 1) != 0
96    }
97
98    fn set_output_value(index: usize, bit: bool) {
99        let p = Self::peripheral();
100        let r: &AtomicU32 = unsafe { core::mem::transmute(p.output_val()) };
101        atomic_set_bit(r, index, bit);
102    }
103
104    fn toggle_pin(index: usize) {
105        let p = Self::peripheral();
106        let r: &AtomicU32 = unsafe { core::mem::transmute(p.output_val()) };
107        let mask = 1 << (index & 31);
108        r.fetch_xor(mask, Ordering::SeqCst);
109    }
110
111    fn set_pullup(index: usize, bit: bool) {
112        let p = Self::peripheral();
113        let r: &AtomicU32 = unsafe { core::mem::transmute(p.pullup()) };
114        atomic_set_bit(r, index, bit);
115    }
116
117    fn set_drive(index: usize, bit: bool) {
118        let p = Self::peripheral();
119        let r: &AtomicU32 = unsafe { core::mem::transmute(p.drive()) };
120        atomic_set_bit(r, index, bit);
121    }
122
123    fn set_out_xor(index: usize, bit: bool) {
124        let p = Self::peripheral();
125        let r: &AtomicU32 = unsafe { core::mem::transmute(p.out_xor()) };
126        atomic_set_bit(r, index, bit);
127    }
128
129    fn set_iof_en(index: usize, bit: bool) {
130        let p = Self::peripheral();
131        let r: &AtomicU32 = unsafe { core::mem::transmute(p.iof_en()) };
132        atomic_set_bit(r, index, bit);
133    }
134
135    fn set_iof_sel(index: usize, bit: bool) {
136        let p = Self::peripheral();
137        let r: &AtomicU32 = unsafe { core::mem::transmute(p.iof_sel()) };
138        atomic_set_bit(r, index, bit);
139    }
140}
141
142macro_rules! gpio {
143    ($GPIOX:ident, $gpiox:ident, [
144        $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+
145    ]) => {
146        /// GPIO
147        pub mod $gpiox {
148            use core::marker::PhantomData;
149            use core::convert::Infallible;
150
151            use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin, ErrorType};
152            use e310x::$GPIOX;
153            use super::{Unknown, IOF0, IOF1, Drive, Floating, GpioExt, Input, Invert,
154                        NoInvert, Output, PullUp, Regular, PinIndex, PeripheralAccess};
155
156            /// GPIO parts for fine grained permission control.
157            pub struct Parts {
158                $(
159                    /// Pin
160                    pub $pxi: $PXi<$MODE>,
161                )+
162            }
163
164            impl PeripheralAccess for $GPIOX {
165                #[inline(always)]
166                fn peripheral() -> e310x::Gpio0 {
167                    unsafe { $GPIOX::steal() }
168                }
169            }
170
171            impl GpioExt for $GPIOX {
172                type Parts = Parts;
173
174                fn split(self) -> Parts {
175                    Parts {
176                        $(
177                            $pxi: $PXi { _mode: PhantomData },
178                        )+
179                    }
180                }
181            }
182
183            $(
184                /// Pin
185                pub struct $PXi<MODE> {
186                    _mode: PhantomData<MODE>,
187                }
188
189                impl<MODE> PinIndex for $PXi<MODE> {
190                    const INDEX: usize = $i;
191                }
192
193                impl<MODE> $PXi<MODE> {
194                    /// Configures the pin to serve as alternate function 0 (AF0)
195                    pub fn into_iof0(self) -> $PXi<IOF0<NoInvert>> {
196                        $GPIOX::set_out_xor(Self::INDEX, false);
197                        $GPIOX::set_iof_sel(Self::INDEX, false);
198                        $GPIOX::set_iof_en(Self::INDEX, true);
199                        $PXi { _mode: PhantomData }
200                    }
201
202                    /// Configures the pin to serve as alternate function 1 (AF1)
203                    pub fn into_iof1(self) -> $PXi<IOF1<NoInvert>> {
204                        $GPIOX::set_out_xor(Self::INDEX, false);
205                        $GPIOX::set_iof_sel(Self::INDEX, true);
206                        $GPIOX::set_iof_en(Self::INDEX, true);
207                        $PXi { _mode: PhantomData }
208                    }
209
210                    /// Configures the pin to serve as inverted alternate function 0 (AF0)
211                    pub fn into_inverted_iof0(self) -> $PXi<IOF0<Invert>> {
212                        $GPIOX::set_out_xor(Self::INDEX, true);
213                        $GPIOX::set_iof_sel(Self::INDEX, false);
214                        $GPIOX::set_iof_en(Self::INDEX, true);
215                        $PXi { _mode: PhantomData }
216                    }
217
218                    /// Configures the pin to serve as inverted alternate function 1 (AF1)
219                    pub fn into_inverted_iof1(self) -> $PXi<IOF1<Invert>> {
220                        $GPIOX::set_out_xor(Self::INDEX, true);
221                        $GPIOX::set_iof_sel(Self::INDEX, true);
222                        $GPIOX::set_iof_en(Self::INDEX, true);
223                        $PXi { _mode: PhantomData }
224                    }
225
226                    /// Configures the pin to serve as a floating input pin
227                    pub fn into_floating_input(self) -> $PXi<Input<Floating>> {
228                        $GPIOX::set_pullup(Self::INDEX, false);
229                        $GPIOX::set_input_en(Self::INDEX, true);
230                        $GPIOX::set_iof_en(Self::INDEX, false);
231                        $PXi { _mode: PhantomData }
232                    }
233
234                    /// Configures the pin to operate as a pulled down input pin
235                    pub fn into_pull_up_input(self) -> $PXi<Input<PullUp>> {
236                        $GPIOX::set_pullup(Self::INDEX, true);
237                        $GPIOX::set_input_en(Self::INDEX, true);
238                        $GPIOX::set_iof_en(Self::INDEX, false);
239                        $PXi { _mode: PhantomData }
240                    }
241
242                    /// Configures the pin to operate as an output pin
243                    pub fn into_output(self) -> $PXi<Output<Regular<NoInvert>>> {
244                        $GPIOX::set_drive(Self::INDEX, false);
245                        $GPIOX::set_out_xor(Self::INDEX, false);
246                        $GPIOX::set_output_en(Self::INDEX, true);
247                        $GPIOX::set_iof_en(Self::INDEX, false);
248                        $PXi { _mode: PhantomData }
249                    }
250
251                    /// Configures the pin to operate as an inverted output pin
252                    pub fn into_inverted_output(self) -> $PXi<Output<Regular<Invert>>> {
253                        $GPIOX::set_drive(Self::INDEX, false);
254                        $GPIOX::set_out_xor(Self::INDEX, true);
255                        $GPIOX::set_output_en(Self::INDEX, true);
256                        $GPIOX::set_iof_en(Self::INDEX, false);
257                        $PXi { _mode: PhantomData }
258                    }
259
260                    /// Configure the pin to operate as an output pin with high
261                    /// current drive
262                    pub fn into_output_drive(self) -> $PXi<Output<Drive<NoInvert>>> {
263                        $GPIOX::set_drive(Self::INDEX, true);
264                        $GPIOX::set_out_xor(Self::INDEX, false);
265                        $GPIOX::set_output_en(Self::INDEX, true);
266                        $GPIOX::set_iof_en(Self::INDEX, false);
267                        $PXi { _mode: PhantomData }
268                    }
269
270                    /// Configure the pin to operate as an inverted output pin with
271                    /// high current drive
272                    pub fn into_inverted_output_drive(self) -> $PXi<Output<Drive<Invert>>> {
273                        $GPIOX::set_drive(Self::INDEX, true);
274                        $GPIOX::set_out_xor(Self::INDEX, true);
275                        $GPIOX::set_output_en(Self::INDEX, true);
276                        $GPIOX::set_iof_en(Self::INDEX, false);
277                        $PXi { _mode: PhantomData }
278                    }
279                }
280
281                impl<MODE> ErrorType for $PXi<Input<MODE>> {
282                    type Error = Infallible;
283                }
284
285                impl<MODE> ErrorType for $PXi<Output<MODE>> {
286                    type Error = Infallible;
287                }
288
289                impl<MODE> InputPin for $PXi<Input<MODE>> {
290                    #[inline]
291                    fn is_high(&mut self) -> Result<bool, Self::Error> {
292                        Ok($GPIOX::input_value(Self::INDEX))
293                    }
294
295                    #[inline]
296                    fn is_low(&mut self) -> Result<bool, Self::Error> {
297                        Ok(!self.is_high()?)
298                    }
299                }
300
301                impl<MODE> OutputPin for $PXi<Output<MODE>> {
302                    #[inline]
303                    fn set_high(&mut self) -> Result<(), Infallible> {
304                        $GPIOX::set_output_value(Self::INDEX, true);
305                        Ok(())
306                    }
307
308                    #[inline]
309                    fn set_low(&mut self) -> Result<(), Infallible> {
310                        $GPIOX::set_output_value(Self::INDEX, false);
311                        Ok(())
312                    }
313                }
314
315                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
316                    #[inline]
317                    fn is_set_high(&mut self) -> Result<bool, Infallible> {
318                        Ok($GPIOX::output_value(Self::INDEX))
319                    }
320
321                    #[inline]
322                    fn is_set_low(&mut self) -> Result<bool, Infallible> {
323                        Ok(!self.is_set_high()?)
324                    }
325
326                    #[inline]
327                    fn toggle(&mut self) -> Result<(), Infallible> {
328                        $GPIOX::toggle_pin(Self::INDEX);
329                        Ok(())
330                    }
331                }
332            )+
333        }
334    }
335}
336
337// By default, all GPIOs are in the Unknown state for two reasons:
338// * bootloader may reconfigure some GPIOs
339// * we do not enforce any specific state in `split()`
340gpio!(Gpio0, gpio0, [
341    Pin0: (pin0, 0, Unknown),
342    Pin1: (pin1, 1, Unknown),
343    Pin2: (pin2, 2, Unknown),
344    Pin3: (pin3, 3, Unknown),
345    Pin4: (pin4, 4, Unknown),
346    Pin5: (pin5, 5, Unknown),
347    Pin6: (pin6, 6, Unknown),
348    Pin7: (pin7, 7, Unknown),
349    Pin8: (pin8, 8, Unknown),
350    Pin9: (pin9, 9, Unknown),
351    Pin10: (pin10, 10, Unknown),
352    Pin11: (pin11, 11, Unknown),
353    Pin12: (pin12, 12, Unknown),
354    Pin13: (pin13, 13, Unknown),
355    Pin14: (pin14, 14, Unknown),
356    Pin15: (pin15, 15, Unknown),
357    Pin16: (pin16, 16, Unknown),
358    Pin17: (pin17, 17, Unknown),
359    Pin18: (pin18, 18, Unknown),
360    Pin19: (pin19, 19, Unknown),
361    Pin20: (pin20, 20, Unknown),
362    Pin21: (pin21, 21, Unknown),
363    Pin22: (pin22, 22, Unknown),
364    Pin23: (pin23, 23, Unknown),
365    Pin24: (pin24, 24, Unknown),
366    Pin25: (pin25, 25, Unknown),
367    Pin26: (pin26, 26, Unknown),
368    Pin27: (pin27, 27, Unknown),
369    Pin28: (pin28, 28, Unknown),
370    Pin29: (pin29, 29, Unknown),
371    Pin30: (pin30, 30, Unknown),
372    Pin31: (pin31, 31, Unknown),
373]);