Skip to main content

imxrt_hal/chip/drivers/
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).unwrap();
27//! output.set();
28//! output.clear();
29//! output.toggle();
30//! ```
31//! # TODO
32//!
33//! - Fast GPIOs
34
35use crate::{iomuxc, ral};
36
37pub use crate::PinPortIncompatibleError;
38
39/// Any GPIO instance.
40type AnyInstance = crate::AnyInstance<ral::gpio::RegisterBlock>;
41
42/// GPIO ports.
43pub struct Port {
44    gpio: AnyInstance,
45}
46
47impl Port {
48    /// Create a GPIO port that can allocate and convert GPIOs.
49    pub fn new<const N: u8>(gpio: ral::gpio::Instance<N>) -> Self {
50        let gpio: AnyInstance = crate::into_any(gpio);
51        Self { gpio }
52    }
53
54    fn instance(&self) -> u8 {
55        ral::gpio::number(&*self.gpio).unwrap()
56    }
57
58    fn duplicate_instance(&self) -> AnyInstance {
59        // SAFETY: We're creating an alias to the same register block.
60        // Output and Input only perform atomic register accesses.
61        unsafe { AnyInstance::new(&*self.gpio) }
62    }
63
64    /// Allocate an output GPIO.
65    ///
66    /// Returns an error if the pin is not compatible with this GPIO port
67    /// (i.e., the pin's GPIO module number does not match the port's instance).
68    /// The pin is returned inside the error so you can recover it.
69    pub fn output<P, const N: u8>(
70        &mut self,
71        mut pin: P,
72    ) -> Result<Output, PinPortIncompatibleError<P>>
73    where
74        P: iomuxc::gpio::Pin<N>,
75    {
76        if N != self.instance() {
77            return Err(PinPortIncompatibleError(pin));
78        }
79        iomuxc::gpio::prepare(&mut pin);
80        Ok(Output::new(self.duplicate_instance(), P::OFFSET))
81    }
82
83    /// Allocate an input GPIO.
84    ///
85    /// Returns an error if the pin is not compatible with this GPIO port
86    /// (i.e., the pin's GPIO module number does not match the port's instance).
87    /// The pin is returned inside the error so you can recover it.
88    pub fn input<P, const N: u8>(
89        &mut self,
90        mut pin: P,
91    ) -> Result<Input, PinPortIncompatibleError<P>>
92    where
93        P: iomuxc::gpio::Pin<N>,
94    {
95        if N != self.instance() {
96            return Err(PinPortIncompatibleError(pin));
97        }
98        iomuxc::gpio::prepare(&mut pin);
99        Ok(Input::new(self.duplicate_instance(), P::OFFSET))
100    }
101
102    /// Enable or disable GPIO input interrupts.
103    ///
104    /// Specify `None` to disable interrupts. Or, provide a trigger
105    /// to configure the interrupt. Remember that clearing a trigger
106    /// happens on the `Input` object, using [`Input::clear_triggered`].
107    /// Do not supply `None` in order to clear the trigger.
108    ///
109    /// If this pin isn't associated with the given GPIO port, this
110    /// does nothing and returns an error.
111    pub fn set_interrupt(
112        &mut self,
113        input: &Input,
114        trigger: Option<Trigger>,
115    ) -> Result<(), PinPortIncompatibleError<()>> {
116        if !crate::is_same_instance(&self.gpio, &input.gpio) {
117            return Err(PinPortIncompatibleError(()));
118        }
119
120        self.set_interrupt_enable(input, false);
121        if let Some(trigger) = trigger {
122            self.set_interrupt_trigger(input, trigger);
123            self.set_interrupt_enable(input, true);
124        }
125
126        Ok(())
127    }
128
129    /// Set the GPIO input interrupt trigger for the provided input pin.
130    fn set_interrupt_trigger(&mut self, input: &Input, trigger: Trigger) {
131        if Trigger::EitherEdge == trigger {
132            ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
133                edge_sel | input.mask()
134            });
135        } else {
136            ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
137                edge_sel & !input.mask()
138            });
139            let icr = trigger as u32;
140            let icr_modify =
141                |reg| reg & !(0b11 << input.icr_offset()) | (icr << input.icr_offset());
142            if input.offset < 16 {
143                ral::modify_reg!(ral::gpio, self.gpio, ICR1, icr_modify);
144            } else {
145                ral::modify_reg!(ral::gpio, self.gpio, ICR2, icr_modify);
146            }
147        }
148    }
149
150    /// Enable (`true`) or disable (`false`) interrupt generation.
151    fn set_interrupt_enable(&mut self, input: &Input, enable: bool) {
152        if enable {
153            ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr | input.mask());
154        } else {
155            ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr & !input.mask());
156        }
157    }
158}
159
160/// An output GPIO.
161pub struct Output {
162    // Logical ownership:
163    // - DR: read only
164    // - PSR: read only
165    // - DR_SET, DR_CLEAR, DR_TOGGLE: write 1 to set value in DR
166    gpio: AnyInstance,
167    offset: u32,
168}
169
170impl Output {
171    fn new(gpio: AnyInstance, offset: u32) -> Self {
172        let output = Self { gpio, offset };
173        ral::modify_reg!(ral::gpio, output.gpio, GDIR, |gdir| gdir | output.mask());
174        output
175    }
176
177    const fn mask(&self) -> u32 {
178        1 << self.offset
179    }
180
181    /// Set the GPIO high.
182    pub fn set(&self) {
183        // Atomic write, OK to take immutable reference.
184        ral::write_reg!(ral::gpio, self.gpio, DR_SET, self.mask());
185    }
186
187    /// Set the GPIO low.
188    pub fn clear(&self) {
189        // Atomic write, OK to take immutable reference.
190        ral::write_reg!(ral::gpio, self.gpio, DR_CLEAR, self.mask());
191    }
192
193    /// Alternate the GPIO pin output.
194    ///
195    /// `toggle` is implemented in hardware, so it will be more efficient
196    /// than implementing in software.
197    pub fn toggle(&self) {
198        // Atomic write, OK to take immutable reference.
199        ral::write_reg!(ral::gpio, self.gpio, DR_TOGGLE, self.mask());
200    }
201
202    /// Returns `true` if the GPIO is set.
203    pub fn is_set(&self) -> bool {
204        ral::read_reg!(ral::gpio, self.gpio, DR) & self.mask() != 0
205    }
206
207    /// Returns `true` if the value of the pad is high.
208    ///
209    /// Can differ from [`is_set()`](Self::is_set), especially in an open drain config.
210    pub fn is_pad_high(&self) -> bool {
211        ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
212    }
213
214    /// Allocate an output GPIO without a pin.
215    ///
216    /// Prefer using [`Port::output`](Port::output) to create a GPIO output with a
217    /// pin resource. That method ensures that pin resources are managed throughout
218    /// your program, and that the pin is configured to operate as a GPIO output.
219    ///
220    /// You may use this method to allocate duplicate `Output` object for the same
221    /// physical GPIO output. This is considered safe, since the `Output` API is
222    /// reentrant.
223    ///
224    /// If you use this constructor, you're responsible for configuring the IOMUX
225    /// multiplexer register.
226    pub fn without_pin(port: &mut Port, offset: u32) -> Self {
227        Self::new(port.duplicate_instance(), offset)
228    }
229}
230
231/// An input GPIO.
232pub struct Input {
233    // Logical ownership:
234    // - PSR: read only
235    // - ISR: read, W1C
236    gpio: AnyInstance,
237    offset: u32,
238}
239
240/// Input interrupt triggers.
241#[cfg_attr(feature = "defmt", derive(defmt::Format))]
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243#[repr(u32)]
244pub enum Trigger {
245    /// Interrupt when GPIO is low
246    Low = 0,
247    /// Interrupt when GPIO is high
248    High = 1,
249    /// Interrupt after GPIO rising edge
250    RisingEdge = 2,
251    /// Interrupt after GPIO falling edge
252    FallingEdge = 3,
253    /// Interrupt after either a rising or falling edge
254    EitherEdge = 4,
255}
256
257impl Input {
258    fn new(gpio: AnyInstance, offset: u32) -> Self {
259        let input = Self { gpio, offset };
260        ral::modify_reg!(ral::gpio, input.gpio, GDIR, |gdir| gdir & !input.mask());
261        input
262    }
263
264    const fn mask(&self) -> u32 {
265        1 << self.offset
266    }
267
268    const fn icr_offset(&self) -> u32 {
269        (self.offset % 16) * 2
270    }
271
272    /// Returns `true` if the GPIO is set high.
273    pub fn is_set(&self) -> bool {
274        ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
275    }
276
277    /// Returns `true` if the GPIO interrupt has triggered.
278    pub fn is_triggered(&self) -> bool {
279        ral::read_reg!(ral::gpio, self.gpio, ISR) & self.mask() != 0
280    }
281
282    /// Clear the interrupt triggered flag.
283    pub fn clear_triggered(&self) {
284        // Atomic write; OK to take immutable reference.
285        ral::write_reg!(ral::gpio, self.gpio, ISR, self.mask());
286    }
287
288    /// Indicates if interrupts are enabled for this input.
289    pub fn is_interrupt_enabled(&self) -> bool {
290        ral::read_reg!(ral::gpio, self.gpio, IMR) & self.mask() != 0
291    }
292
293    /// Allocate an input GPIO without a pin.
294    ///
295    /// Prefer using [`Port::input`](Port::input) to create a GPIO input with a
296    /// pin resource. That method ensures that pin resources are managed throughout
297    /// your program, and that the pin is configured to operate as a GPIO input.
298    ///
299    /// You may use this method to allocate duplicate `Input` object for the same
300    /// physical GPIO input. This is considered safe, since the `Input` API is
301    /// reentrant. Any non-reentrant methods are attached to [`Port`], which cannot
302    /// be constructed without an `unsafe` constructor of the register block.
303    ///
304    /// If you use this constructor, you're responsible for configuring the IOMUX
305    /// multiplexer register.
306    pub fn without_pin(port: &mut Port, offset: u32) -> Self {
307        Self::new(port.duplicate_instance(), offset)
308    }
309}
310
311impl eh02::digital::v2::OutputPin for Output {
312    type Error = core::convert::Infallible;
313
314    fn set_high(&mut self) -> Result<(), Self::Error> {
315        self.set();
316        Ok(())
317    }
318    fn set_low(&mut self) -> Result<(), Self::Error> {
319        self.clear();
320        Ok(())
321    }
322}
323
324impl eh1::digital::ErrorType for Output {
325    type Error = core::convert::Infallible;
326}
327
328impl eh1::digital::OutputPin for Output {
329    fn set_high(&mut self) -> Result<(), Self::Error> {
330        Output::set(self);
331        Ok(())
332    }
333    fn set_low(&mut self) -> Result<(), Self::Error> {
334        Output::clear(self);
335        Ok(())
336    }
337}
338
339impl eh1::digital::StatefulOutputPin for Output {
340    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
341        Ok(Output::is_set(self))
342    }
343
344    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
345        Ok(!Output::is_set(self))
346    }
347
348    fn toggle(&mut self) -> Result<(), Self::Error> {
349        Output::toggle(self);
350        Ok(())
351    }
352}
353
354// For open drain or simply reading back the actual state
355// of the pin.
356impl eh1::digital::InputPin for Output {
357    fn is_high(&mut self) -> Result<bool, Self::Error> {
358        Ok(Output::is_pad_high(self))
359    }
360
361    fn is_low(&mut self) -> Result<bool, Self::Error> {
362        Ok(!Output::is_pad_high(self))
363    }
364}
365
366impl eh1::digital::ErrorType for Input {
367    type Error = core::convert::Infallible;
368}
369
370impl eh1::digital::InputPin for Input {
371    fn is_high(&mut self) -> Result<bool, Self::Error> {
372        Ok(Input::is_set(self))
373    }
374
375    fn is_low(&mut self) -> Result<bool, Self::Error> {
376        Ok(!Input::is_set(self))
377    }
378}