max7800x_hal/
gpio.rs

1//! General Purpose Input/Output (GPIO)
2use core::marker::PhantomData;
3use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
4use paste::paste;
5
6/// Marker trait for GPIO pin modes.
7pub trait PinMode: crate::Sealed {}
8
9pub struct Input;
10pub struct InputOutput;
11pub struct Af1;
12pub struct Af2;
13
14impl crate::Sealed for Input {}
15impl crate::Sealed for InputOutput {}
16impl crate::Sealed for Af1 {}
17impl crate::Sealed for Af2 {}
18
19impl PinMode for Input {}
20impl PinMode for InputOutput {}
21impl PinMode for Af1 {}
22impl PinMode for Af2 {}
23
24/// Marker trait for GPIO pin power supply.
25pub trait PowerSupply: crate::Sealed {}
26
27pub struct Vddio;
28pub struct Vddioh;
29
30impl crate::Sealed for Vddio {}
31impl crate::Sealed for Vddioh {}
32
33impl PowerSupply for Vddio {}
34impl PowerSupply for Vddioh {}
35
36/// Marker trait for GPIO pin input pad modes.
37pub trait PadMode: crate::Sealed {}
38
39pub struct HighImpedance;
40pub struct PullUpWeak;
41pub struct PullUpStrong;
42pub struct PullDownWeak;
43pub struct PullDownStrong;
44
45impl crate::Sealed for HighImpedance {}
46impl crate::Sealed for PullUpWeak {}
47impl crate::Sealed for PullUpStrong {}
48impl crate::Sealed for PullDownWeak {}
49impl crate::Sealed for PullDownStrong {}
50
51impl PadMode for HighImpedance {}
52impl PadMode for PullUpWeak {}
53impl PadMode for PullUpStrong {}
54impl PadMode for PullDownWeak {}
55impl PadMode for PullDownStrong {}
56
57/// Marker trait for GPIO pin output drive strengths.
58pub trait DriveStrength: crate::Sealed {}
59
60pub struct Strength0;
61pub struct Strength1;
62pub struct Strength2;
63pub struct Strength3;
64
65impl crate::Sealed for Strength0 {}
66impl crate::Sealed for Strength1 {}
67impl crate::Sealed for Strength2 {}
68impl crate::Sealed for Strength3 {}
69
70impl DriveStrength for Strength0 {}
71impl DriveStrength for Strength1 {}
72impl DriveStrength for Strength2 {}
73impl DriveStrength for Strength3 {}
74
75/// Zero-sized abstraction type for a GPIO pin.
76///
77/// Traits from [`embedded_hal::digital`] are also implemented for each pin.
78///
79/// - `P` is the GPIO port number (e.g. `0` for `Gpio0`, `1` for `Gpio1`, etc.)
80/// - `N` is the GPIO pin number.
81/// - `MODE` is one of the pin modes (e.g. `Input`, `InputOutput`, `Af1`, `Af2`).
82pub struct Pin<
83    const P: u8,
84    const N: u8,
85    MODE: PinMode = Input,
86    SUPPLY: PowerSupply = Vddio,
87    PAD: PadMode = HighImpedance,
88    DRIVE: DriveStrength = Strength0,
89> {
90    _mode: PhantomData<MODE>,
91    _supply: PhantomData<SUPPLY>,
92    _pad: PhantomData<PAD>,
93    _drive: PhantomData<DRIVE>,
94}
95
96/// Default methods that should work across all pin modes.
97impl<const P: u8, const N: u8, MODE: PinMode> Pin<P, N, MODE> {
98    const fn new() -> Self {
99        Self {
100            _mode: PhantomData,
101            _supply: PhantomData,
102            _pad: PhantomData,
103            _drive: PhantomData,
104        }
105    }
106
107    #[doc(hidden)]
108    #[inline(always)]
109    fn _output_enable(&mut self) {
110        // Safety: Concurrent write access to the GPIO output enable atomic set register is safe
111        let gpio = unsafe { &*gpiox_ptr::<P>() };
112        gpio.outen_set().write(|w| unsafe { w.bits(1 << N) });
113    }
114
115    #[doc(hidden)]
116    #[inline(always)]
117    fn _output_disable(&mut self) {
118        // Safety: Concurrent write access to the GPIO output enable atomic clear register is safe
119        let gpio = unsafe { &*gpiox_ptr::<P>() };
120        gpio.outen_clr().write(|w| unsafe { w.bits(1 << N) });
121    }
122
123    #[doc(hidden)]
124    #[inline(always)]
125    fn _into_af1(&mut self) {
126        let gpio = unsafe { &*gpiox_ptr::<P>() };
127        // Set EN0 to 1
128        gpio.en0_set().write(|w| unsafe { w.bits(1 << N) });
129        // Set EN1 to 0
130        gpio.en1_clr().write(|w| unsafe { w.bits(1 << N) });
131        // Set EN0 to 0
132        gpio.en0_clr().write(|w| unsafe { w.bits(1 << N) });
133    }
134
135    #[doc(hidden)]
136    #[inline(always)]
137    fn _into_af2(&mut self) {
138        let gpio = unsafe { &*gpiox_ptr::<P>() };
139        // Set EN0 to 1
140        gpio.en0_set().write(|w| unsafe { w.bits(1 << N) });
141        // Set EN1 to 1
142        gpio.en1_set().write(|w| unsafe { w.bits(1 << N) });
143        // Set EN1 to 0
144        gpio.en1_clr().write(|w| unsafe { w.bits(1 << N) });
145    }
146
147    #[doc(hidden)]
148    #[inline(always)]
149    fn _is_high(&self) -> bool {
150        // Safety: Concurrent read access to the GPIO input register is safe
151        let gpio = unsafe { &*gpiox_ptr::<P>() };
152        gpio.in_().read().gpio_in().bits() & (1 << N) != 0
153    }
154
155    #[doc(hidden)]
156    #[inline(always)]
157    fn _is_low(&self) -> bool {
158        // Safety: Concurrent read access to the GPIO input register is safe
159        let gpio = unsafe { &*gpiox_ptr::<P>() };
160        gpio.in_().read().gpio_in().bits() & (1 << N) == 0
161    }
162
163    #[doc(hidden)]
164    #[inline(always)]
165    fn _set_high(&mut self) {
166        // Safety: Concurrent write access to the GPIO output atomic set register is safe
167        let gpio = unsafe { &*gpiox_ptr::<P>() };
168        gpio.out_set().write(|w| unsafe { w.bits(1 << N) });
169    }
170
171    #[doc(hidden)]
172    #[inline(always)]
173    fn _set_low(&mut self) {
174        // Safety: Concurrent write access to the GPIO output atomic clear register is safe
175        let gpio = unsafe { &*gpiox_ptr::<P>() };
176        gpio.out_clr().write(|w| unsafe { w.bits(1 << N) });
177    }
178
179    #[doc(hidden)]
180    #[inline(always)]
181    fn _is_set_high(&self) -> bool {
182        // Safety: Concurrent read access to the GPIO output register is safe
183        let gpio = unsafe { &*gpiox_ptr::<P>() };
184        gpio.out().read().bits() & (1 << N) != 0
185    }
186
187    #[doc(hidden)]
188    #[inline(always)]
189    fn _is_set_low(&self) -> bool {
190        // Safety: Concurrent read access to the GPIO output register is safe
191        let gpio = unsafe { &*gpiox_ptr::<P>() };
192        gpio.out().read().bits() & (1 << N) == 0
193    }
194
195    /// Returns [`true`] if the pin is high, [`false`] if the pin is low
196    #[inline(always)]
197    pub fn is_high(&self) -> bool {
198        self._is_high()
199    }
200
201    /// Returns [`true`] if the pin is low, [`false`] if the pin is high
202    #[inline(always)]
203    pub fn is_low(&self) -> bool {
204        self._is_low()
205    }
206}
207
208/// Methods for input pins.
209impl<const P: u8, const N: u8> Pin<P, N, Input> {
210    /// Configures the pin as an input/output pin.
211    #[inline(always)]
212    pub fn into_input_output(self) -> Pin<P, N, InputOutput> {
213        // Enable the output for the pin
214        let mut pin = Pin::<P, N, InputOutput>::new();
215        pin._output_enable();
216        pin
217    }
218
219    /// Configures the pin as an alternate function 1 pin.
220    #[inline(always)]
221    pub fn into_af1(self) -> Pin<P, N, Af1> {
222        let mut pin = Pin::<P, N, Af1>::new();
223        pin._into_af1();
224        pin
225    }
226
227    /// Configures the pin as an alternate function 2 pin.
228    #[inline(always)]
229    pub fn into_af2(self) -> Pin<P, N, Af2> {
230        let mut pin = Pin::<P, N, Af2>::new();
231        pin._into_af2();
232        pin
233    }
234}
235
236/// Methods for input/output pins.
237impl<const P: u8, const N: u8> Pin<P, N, InputOutput> {
238    /// Configures the pin as an input pin (disables output).
239    #[inline(always)]
240    pub fn into_input(self) -> Pin<P, N, Input> {
241        // Disable the output for the pin
242        let mut pin = Pin::<P, N, Input>::new();
243        pin._output_disable();
244        pin
245    }
246
247    /// Sets the pin high.
248    #[inline(always)]
249    pub fn set_high(&mut self) {
250        self._set_high();
251    }
252
253    /// Sets the pin low.
254    #[inline(always)]
255    pub fn set_low(&mut self) {
256        self._set_low();
257    }
258
259    /// Returns [`true`] if the pin is set to high, [`false`] if the pin is set to low.
260    #[inline(always)]
261    pub fn is_set_high(&self) -> bool {
262        self._is_set_high()
263    }
264
265    /// Returns [`true`] if the pin is set to low, [`false`] if the pin is set to high.
266    #[inline(always)]
267    pub fn is_set_low(&self) -> bool {
268        self._is_set_low()
269    }
270
271    /// Sets the pin power supply to VDDIO.
272    #[inline(always)]
273    pub fn set_power_vddio(&mut self) {
274        let gpio = unsafe { &*gpiox_ptr::<P>() };
275        gpio.vssel()
276            .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << N)) });
277    }
278
279    /// Sets the pin power supply to VDDIOH.
280    #[inline(always)]
281    pub fn set_power_vddioh(&mut self) {
282        let gpio = unsafe { &*gpiox_ptr::<P>() };
283        gpio.vssel()
284            .modify(|r, w| unsafe { w.bits(r.bits() | (1 << N)) });
285    }
286}
287
288/// embedded-hal ErrorType trait
289impl<const P: u8, const N: u8, MODE: PinMode> ErrorType for Pin<P, N, MODE> {
290    type Error = core::convert::Infallible;
291}
292
293/// embedded-hal InputPin trait
294impl<const P: u8, const N: u8, MODE: PinMode> InputPin for Pin<P, N, MODE> {
295    #[inline(always)]
296    fn is_high(&mut self) -> Result<bool, Self::Error> {
297        Ok(self._is_high())
298    }
299
300    #[inline(always)]
301    fn is_low(&mut self) -> Result<bool, Self::Error> {
302        Ok(self._is_low())
303    }
304}
305
306/// embedded-hal OutputPin trait
307impl<const P: u8, const N: u8> OutputPin for Pin<P, N, InputOutput> {
308    #[inline(always)]
309    fn set_high(&mut self) -> Result<(), Self::Error> {
310        self._set_high();
311        Ok(())
312    }
313
314    #[inline(always)]
315    fn set_low(&mut self) -> Result<(), Self::Error> {
316        self._set_low();
317        Ok(())
318    }
319}
320
321/// embedded-hal StatefulOutputPin trait
322impl<const P: u8, const N: u8> StatefulOutputPin for Pin<P, N, InputOutput> {
323    #[inline(always)]
324    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
325        Ok(self._is_set_high())
326    }
327
328    #[inline(always)]
329    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
330        Ok(self._is_set_low())
331    }
332}
333
334/// Macro that generates a GPIO module with an interface for splitting GPIO pins.
335///
336/// - `$MODULE_PAC`: The peripheral access crate (PAC) module for the GPIO (e.g., `Gpio1`).
337/// - `$MODULE_HAL`: The name of the module to be generated (e.g., `gpio1`).
338/// - `$GCR_TYPE`: The type of the GCR register for the GPIO (e.g., `gcr`).
339/// - `$PORT_NUM`: The port number (e.g., `1` for `Gpio1`).
340/// - `[$($PIN_NUM:literal),*]`: A list of pin numbers to include (e.g., `[0, 1, 2, 3]`).
341macro_rules! gpio {
342    ($MODULE_PAC:ident, $MODULE_HAL:ident, $GCR_TYPE:ident, $PORT_NUM:expr, [$($PIN_NUM:literal),*]) => {
343        paste!{
344            pub mod $MODULE_HAL {
345                /// Collection of GPIO pins from a single GPIO port.
346                pub struct Parts {
347                    $(
348                        pub [<p $PORT_NUM _ $PIN_NUM>]: [<P $PORT_NUM _ $PIN_NUM>],
349                    )+
350                }
351
352                /// # General Purpose Input/Output (GPIO) Peripheral
353                ///
354                /// This peripheral provides an interface for enabling and
355                /// splitting up GPIO pins.
356                ///
357                /// ## Example
358                /// ```
359                /// // Initialize a Gcr
360                /// let mut gcr = Gcr::new(peripherals.gcr, peripherals.lpgcr);
361                /// // Initialize the GPIO0 peripheral
362                /// let gpio0 = hal::gpio::Gpio0::new(p.gpio0, &mut gcr.reg);
363                /// // Split into pins
364                /// let pins0 = gpio0.split();
365                /// // Set up pins for UART communication
366                /// let rx_pin = pins0.p0_0.into_af1();
367                /// let tx_pin = pins0.p0_1.into_af1();
368                ///
369                /// // Initialize the GPIO2 peripheral
370                /// let gpio2 = hal::gpio::Gpio2::new(p.gpio2, &mut gcr.reg);
371                /// // Split into pins
372                /// let pins2 = gpio2.split();
373                /// // Set up pins for LED control/output
374                /// let led_red = pins2.p2_0.into_input_output();
375                /// let led_green = pins2.p2_1.into_input_output();
376                /// let led_blue = pins2.p2_2.into_input_output();
377                ///
378                /// // Acquired pins can then be passed to other peripherals in
379                /// // the HAL or embedded-hal driver crates.
380                /// ```
381                pub struct GpioPeripheral {
382                    _gpio: $crate::pac::$MODULE_PAC,
383                }
384
385                impl GpioPeripheral {
386                    /// Constructs and initializes a GPIO peripheral.
387                    pub fn new(gpio: $crate::pac::$MODULE_PAC, reg: &mut crate::gcr::GcrRegisters) -> Self {
388                        // Enable the GPIO peripheral clock
389                        use crate::gcr::ClockForPeripheral;
390                        unsafe { gpio.enable_clock(&mut reg.$GCR_TYPE); };
391                        Self {
392                            _gpio: gpio,
393                        }
394                    }
395                    /// Splits the GPIO peripheral into independent pins.
396                    pub fn split(self) -> Parts {
397                        Parts {
398                            $(
399                                [<p $PORT_NUM _ $PIN_NUM>]: [<P $PORT_NUM _ $PIN_NUM>]::new(),
400                            )+
401                        }
402                    }
403                }
404
405                // #[doc="Common type for "]
406                // #[doc=stringify!($GPIOX)]
407                // #[doc=" related pins"]
408                // pub type $PX_x<MODE> = super::PartiallyErasedPin<$port_id, MODE>;
409
410                // Creates a zero-sized type for each pin
411                $(
412                    #[doc=stringify!([<P $PORT_NUM _ $PIN_NUM>])]
413                    #[doc=" pin"]
414                    pub type [<P $PORT_NUM _ $PIN_NUM>] = super::Pin<$PORT_NUM, $PIN_NUM>;
415                )+
416            }
417
418            // Re-export the peripheral constructor for easy access
419            pub use $MODULE_HAL::GpioPeripheral as $MODULE_PAC;
420        }
421    };
422}
423
424gpio!(
425    Gpio0,
426    gpio0,
427    gcr,
428    0,
429    [
430        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
431        25, 26, 27, 28, 29, 30
432    ]
433);
434gpio!(Gpio1, gpio1, gcr, 1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
435gpio!(Gpio2, gpio2, lpgcr, 2, [0, 1, 2, 3, 4, 5, 6, 7]);
436
437/// Zero runtime cost function to get the address of a GPIO peripheral.
438#[inline(always)]
439const fn gpiox_ptr<const P: u8>() -> *const crate::pac::gpio0::RegisterBlock {
440    match P {
441        0 => crate::pac::Gpio0::ptr(),
442        1 => crate::pac::Gpio1::ptr(),
443        2 => crate::pac::Gpio2::ptr(),
444        _ => panic!("Invalid GPIO port number"),
445    }
446}