imxrt_hal/common/
gpio.rs

1//! General purpose I/O.
2//!
3//! Create a [`Port`](Port) over a RAL GPIO instance. Then, use the `Port` to
4//! allocate GPIO outputs and inputs.
5//!
6//! Use [`Output`](Output) to drive GPIO outputs. Use [`Input`](Input) to read
7//! GPIO pin states, and trigger interrupts when GPIO states change.
8//!
9//! # Interior mutability
10//!
11//! Methods on `Output` and `Input` take immutable references, `&self`. The hardware
12//! guarantees that these operations can occur without data races. Methods that
13//! require multiple operations on a register are implemented on the `Port`, and
14//! take the GPIO by reference.
15//!
16//! # Example
17//!
18//! ```no_run
19//! use imxrt_hal::gpio::Port;
20//! use imxrt_ral::gpio::GPIO2;
21//!
22//! let mut gpio2 = Port::new(unsafe { GPIO2::instance() });
23//! let gpio_b0_04 = // Handle to GPIO_B0_04 IOMUXC pin, provided by BSP or higher-level HAL...
24//!     # unsafe { imxrt_iomuxc::imxrt1060::gpio_b0::GPIO_B0_04::new() };
25//!
26//! let output = gpio2.output(gpio_b0_04);
27//! output.set();
28//! output.clear();
29//! output.toggle();
30//!
31//! let input = gpio2.input(output.release());
32//! assert!(input.is_set());
33//! ```
34//! # TODO
35//!
36//! - Fast GPIOs
37
38use crate::{iomuxc, ral};
39
40/// GPIO ports.
41pub struct Port<const N: u8> {
42    gpio: ral::gpio::Instance<N>,
43}
44
45impl<const N: u8> Port<N> {
46    /// Create a GPIO port that can allocate and convert GPIOs.
47    pub fn new(gpio: ral::gpio::Instance<N>) -> Self {
48        Self { gpio }
49    }
50
51    fn register_block(&self) -> &'static ral::gpio::RegisterBlock {
52        let register_block: &ral::gpio::RegisterBlock = &self.gpio;
53        // Safety: points to peripheral memory, which is static.
54        // Gpio implementation guarantees that memory which needs
55        // mutable access to shared GPIO registers passes through
56        // the Port type.
57        let register_block: &'static ral::gpio::RegisterBlock =
58            unsafe { core::mem::transmute(register_block) };
59        register_block
60    }
61
62    /// Allocate an output GPIO.
63    pub fn output<P>(&mut self, mut pin: P) -> Output<P>
64    where
65        P: iomuxc::gpio::Pin<N>,
66    {
67        iomuxc::gpio::prepare(&mut pin);
68        Output::new(pin, self.register_block(), P::OFFSET)
69    }
70
71    /// Allocate an input GPIO.
72    pub fn input<P>(&mut self, mut pin: P) -> Input<P>
73    where
74        P: iomuxc::gpio::Pin<N>,
75    {
76        iomuxc::gpio::prepare(&mut pin);
77        Input::new(pin, self.register_block(), P::OFFSET)
78    }
79
80    /// Enable or disable GPIO input interrupts.
81    ///
82    /// Specify `None` to disable interrupts. Or, provide a trigger
83    /// to configure the interrupt.
84    pub fn set_interrupt<P>(&mut self, pin: &Input<P>, trigger: Option<Trigger>) {
85        self.set_interrupt_enable(pin, false);
86        if let Some(trigger) = trigger {
87            self.set_interrupt_trigger(pin, trigger);
88            self.set_interrupt_enable(pin, true);
89        }
90    }
91
92    /// Set the GPIO input interrupt trigger for the provided input pin.
93    fn set_interrupt_trigger<P>(&mut self, pin: &Input<P>, trigger: Trigger) {
94        if Trigger::EitherEdge == trigger {
95            ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
96                edge_sel | pin.mask()
97            });
98        } else {
99            ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
100                edge_sel & !pin.mask()
101            });
102            let icr = trigger as u32;
103            let icr_modify = |reg| reg & !(0b11 << pin.icr_offset()) | (icr << pin.icr_offset());
104            if pin.offset < 16 {
105                ral::modify_reg!(ral::gpio, self.gpio, ICR1, icr_modify);
106            } else {
107                ral::modify_reg!(ral::gpio, self.gpio, ICR2, icr_modify);
108            }
109        }
110    }
111
112    /// Enable (`true`) or disable (`false`) interrupt generation.
113    fn set_interrupt_enable<P>(&mut self, pin: &Input<P>, enable: bool) {
114        if enable {
115            ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr | pin.mask());
116        } else {
117            ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr & !pin.mask());
118        }
119    }
120}
121
122/// An output GPIO.
123pub struct Output<P> {
124    pin: P,
125    // Logical ownership:
126    // - DR: read only
127    // - PSR: read only
128    // - DR_SET, DR_CLEAR, DR_TOGGLE: write 1 to set value in DR
129    gpio: &'static ral::gpio::RegisterBlock,
130    offset: u32,
131}
132
133// Safety: an output pin is safe to send across execution contexts,
134// because it points to static memory.
135unsafe impl<P: Send> Send for Output<P> {}
136
137impl<P> Output<P> {
138    fn new(pin: P, gpio: &'static ral::gpio::RegisterBlock, offset: u32) -> Self {
139        let output = Self { pin, gpio, offset };
140        ral::modify_reg!(ral::gpio, gpio, GDIR, |gdir| gdir | output.mask());
141        output
142    }
143
144    const fn mask(&self) -> u32 {
145        1 << self.offset
146    }
147
148    /// Set the GPIO high.
149    pub fn set(&self) {
150        // Atomic write, OK to take immutable reference.
151        ral::write_reg!(ral::gpio, self.gpio, DR_SET, self.mask());
152    }
153
154    /// Set the GPIO low.
155    pub fn clear(&self) {
156        // Atomic write, OK to take immutable reference.
157        ral::write_reg!(ral::gpio, self.gpio, DR_CLEAR, self.mask());
158    }
159
160    /// Alternate the GPIO pin output.
161    ///
162    /// `toggle` is implemented in hardware, so it will be more efficient
163    /// than implementing in software.
164    pub fn toggle(&self) {
165        // Atomic write, OK to take immutable reference.
166        ral::write_reg!(ral::gpio, self.gpio, DR_TOGGLE, self.mask());
167    }
168
169    /// Returns `true` if the GPIO is set.
170    pub fn is_set(&self) -> bool {
171        ral::read_reg!(ral::gpio, self.gpio, DR) & self.mask() != 0
172    }
173
174    /// Returns `true` if the value of the pad is high.
175    ///
176    /// Can differ from [`is_set()`](Self::is_set), especially in an open drain config.
177    pub fn is_pad_high(&self) -> bool {
178        ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
179    }
180
181    /// Release the underlying pin object.
182    pub fn release(self) -> P {
183        self.pin
184    }
185
186    /// Access the underlying pin.
187    pub fn pin(&self) -> &P {
188        &self.pin
189    }
190
191    /// Mutably access the underling pin.
192    pub fn pin_mut(&mut self) -> &mut P {
193        &mut self.pin
194    }
195}
196
197impl Output<()> {
198    /// Allocate an output GPIO without a pin.
199    ///
200    /// Prefer using [`Port::output`](Port::output) to create a GPIO output with a
201    /// pin resource. That method ensures that pin resources are managed throughout
202    /// your program, and that the pin is configured to operate as a GPIO output.
203    ///
204    /// You may use this method to allocate duplicate `Output` object for the same
205    /// physical GPIO output. This is considered safe, since the `Output` API is
206    /// reentrant.
207    ///
208    /// If you use this constructor, you're responsible for configuring the IOMUX
209    /// multiplexer register.
210    pub fn without_pin<const N: u8>(port: &mut Port<N>, offset: u32) -> Self {
211        Self::new((), port.register_block(), offset)
212    }
213}
214
215/// An input GPIO.
216pub struct Input<P> {
217    pin: P,
218    // Logical ownership:
219    // - PSR: read only
220    // - ISR: read, W1C
221    gpio: &'static ral::gpio::RegisterBlock,
222    offset: u32,
223}
224
225// Safety: see impl Send for Output.
226unsafe impl<P: Send> Send for Input<P> {}
227
228/// Input interrupt triggers.
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230#[repr(u32)]
231pub enum Trigger {
232    /// Interrupt when GPIO is low
233    Low = 0,
234    /// Interrupt when GPIO is high
235    High = 1,
236    /// Interrupt after GPIO rising edge
237    RisingEdge = 2,
238    /// Interrupt after GPIO falling edge
239    FallingEdge = 3,
240    /// Interrupt after either a rising or falling edge
241    EitherEdge = 4,
242}
243
244impl<P> Input<P> {
245    fn new(pin: P, gpio: &'static ral::gpio::RegisterBlock, offset: u32) -> Self {
246        let input = Self { pin, gpio, offset };
247        ral::modify_reg!(ral::gpio, gpio, GDIR, |gdir| gdir & !input.mask());
248        input
249    }
250
251    const fn mask(&self) -> u32 {
252        1 << self.offset
253    }
254
255    const fn icr_offset(&self) -> u32 {
256        (self.offset % 16) * 2
257    }
258
259    /// Returns `true` if the GPIO is set high.
260    pub fn is_set(&self) -> bool {
261        ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
262    }
263
264    /// Returns `true` if the GPIO interrupt has triggered.
265    pub fn is_triggered(&self) -> bool {
266        ral::read_reg!(ral::gpio, self.gpio, ISR) & self.mask() != 0
267    }
268
269    /// Clear the interrupt triggered flag.
270    pub fn clear_triggered(&self) {
271        // Atomic write; OK to take immutable reference.
272        ral::write_reg!(ral::gpio, self.gpio, ISR, self.mask());
273    }
274
275    /// Indicates if interrupts are enabled for this input.
276    pub fn is_interrupt_enabled(&self) -> bool {
277        ral::read_reg!(ral::gpio, self.gpio, IMR) & self.mask() != 0
278    }
279
280    /// Release the underlying pin object.
281    pub fn release(self) -> P {
282        self.pin
283    }
284
285    /// Access the underlying pin.
286    pub fn pin(&self) -> &P {
287        &self.pin
288    }
289
290    /// Mutably access the underling pin.
291    pub fn pin_mut(&mut self) -> &mut P {
292        &mut self.pin
293    }
294}
295
296impl Input<()> {
297    /// Allocate an input GPIO without a pin.
298    ///
299    /// Prefer using [`Port::input`](Port::input) to create a GPIO input with a
300    /// pin resource. That method ensures that pin resources are managed throughout
301    /// your program, and that the pin is configured to operate as a GPIO input.
302    ///
303    /// You may use this method to allocate duplicate `Input` object for the same
304    /// physical GPIO input. This is considered safe, since the `Input` API is
305    /// reentrant. Any non-reentrant methods are attached to [`Port`], which cannot
306    /// be constructed without an `unsafe` constructor of the register block.
307    ///
308    /// If you use this constructor, you're responsible for configuring the IOMUX
309    /// multiplexer register.
310    pub fn without_pin<const N: u8>(port: &mut Port<N>, offset: u32) -> Self {
311        Self::new((), port.register_block(), offset)
312    }
313}
314
315impl<P> eh02::digital::v2::OutputPin for Output<P> {
316    type Error = core::convert::Infallible;
317
318    fn set_high(&mut self) -> Result<(), Self::Error> {
319        self.set();
320        Ok(())
321    }
322    fn set_low(&mut self) -> Result<(), Self::Error> {
323        self.clear();
324        Ok(())
325    }
326}
327
328#[cfg(feature = "eh02-unproven")]
329impl<P> eh02::digital::v2::StatefulOutputPin for Output<P> {
330    fn is_set_high(&self) -> Result<bool, Self::Error> {
331        Ok(self.is_set())
332    }
333    fn is_set_low(&self) -> Result<bool, Self::Error> {
334        Ok(!self.is_set())
335    }
336}
337
338#[cfg(feature = "eh02-unproven")]
339impl<P> eh02::digital::v2::ToggleableOutputPin for Output<P> {
340    type Error = core::convert::Infallible;
341
342    fn toggle(&mut self) -> Result<(), Self::Error> {
343        Output::<P>::toggle(self);
344        Ok(())
345    }
346}
347
348#[cfg(feature = "eh02-unproven")]
349impl<P> eh02::digital::v2::InputPin for Input<P> {
350    type Error = core::convert::Infallible;
351
352    fn is_high(&self) -> Result<bool, Self::Error> {
353        Ok(self.is_set())
354    }
355    fn is_low(&self) -> Result<bool, Self::Error> {
356        Ok(!self.is_set())
357    }
358}
359
360impl<P> eh1::digital::ErrorType for Output<P> {
361    type Error = core::convert::Infallible;
362}
363
364impl<P> eh1::digital::OutputPin for Output<P> {
365    fn set_high(&mut self) -> Result<(), Self::Error> {
366        Output::set(self);
367        Ok(())
368    }
369    fn set_low(&mut self) -> Result<(), Self::Error> {
370        Output::clear(self);
371        Ok(())
372    }
373}
374
375impl<P> eh1::digital::StatefulOutputPin for Output<P> {
376    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
377        Ok(Output::is_set(self))
378    }
379
380    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
381        Ok(!Output::is_set(self))
382    }
383
384    fn toggle(&mut self) -> Result<(), Self::Error> {
385        Output::toggle(self);
386        Ok(())
387    }
388}
389
390// For open drain or simply reading back the actual state
391// of the pin.
392impl<P> eh1::digital::InputPin for Output<P> {
393    fn is_high(&mut self) -> Result<bool, Self::Error> {
394        Ok(Output::is_pad_high(self))
395    }
396
397    fn is_low(&mut self) -> Result<bool, Self::Error> {
398        Ok(!Output::is_pad_high(self))
399    }
400}
401
402impl<P> eh1::digital::ErrorType for Input<P> {
403    type Error = core::convert::Infallible;
404}
405
406impl<P> eh1::digital::InputPin for Input<P> {
407    fn is_high(&mut self) -> Result<bool, Self::Error> {
408        Ok(Input::is_set(self))
409    }
410
411    fn is_low(&mut self) -> Result<bool, Self::Error> {
412        Ok(!Input::is_set(self))
413    }
414}