esp_hal/gpio/
interconnect.rs

1//! # Peripheral signal interconnect using the GPIO matrix.
2//!
3//! The GPIO matrix offers flexible connection options between GPIO pins and
4//! peripherals. This module offers capabilities not covered by GPIO pin types
5//! and drivers, like routing fixed logic levels to peripheral inputs, or
6//! inverting input and output signals.
7//!
8//! > Note that routing a signal through the GPIO matrix adds some latency to
9//! > the signal. This is not a problem for most peripherals, but it can be an
10//! > issue for high-speed peripherals like SPI or I2S. `esp-hal` tries to
11//! > bypass the GPIO matrix when possible (e.g. when the pin can be configured
12//! > as a suitable Alternate Function for the peripheral signal, and other
13//! > settings are compatible), but silently falls back to the GPIO matrix for
14//! > flexibility.
15#![doc = concat!("## Relation to the ", crate::trm_markdown_link!("iomuxgpio"))]
16//! The GPIO drivers implement IO MUX and pin functionality (input/output
17//! buffers, pull resistors, etc.). The GPIO matrix is represented by signals
18//! and the [`PeripheralInput`] and [`PeripheralOutput`] traits. There is some
19//! overlap between them: signal routing depends on what type is passed to a
20//! peripheral driver's pin setter functions.
21//!
22//! ## Signals
23//!
24//! GPIO signals are represented by the [`InputSignal`] and [`OutputSignal`]
25//! structs. Peripheral drivers accept [`PeripheralInput`] and
26//! [`PeripheralOutput`] implementations which are implemented for anything that
27//! can be converted into the signal types:
28//! - GPIO pins and drivers
29//! - A fixed logic [`Level`]
30//! - [`NoPin`]
31//!
32//! Note that some of these exist for convenience only. `Level` is meaningful as
33//! a peripheral input, but not as a peripheral output. `NoPin` is a placeholder
34//! for when a peripheral driver does not require a pin, but the API requires
35//! one. It is equivalent to [`Level::Low`].
36//!
37//! ### Splitting drivers into signals
38//!
39//! Each GPIO pin driver such as [`Input`], can be converted
40//! into input or output signals. [`Flex`], which can be either input or output,
41//! can be [`split`](Flex::split) into both signals at once. These signals can
42//! then be individually connected to a peripheral input or output signal. This
43//! allows for flexible routing of signals between peripherals and GPIO pins.
44//!
45//! Note that only configured GPIO drivers can be safely turned into signals.
46//! This conversion freezes the pin configuration, otherwise it would be
47//! possible for multiple peripheral drivers to configure the same GPIO pin at
48//! the same time, which is undefined behavior.
49//!
50//! ### Splitting pins into signals
51//!
52//! GPIO pin types such as [`GPIO0`] or [`AnyPin`] can be **unsafely**
53//! [split](AnyPin::split) into signals. In this case you need to carefully
54//! ensure that only a single driver configures the split pin, by selectively
55//! [freezing](`InputSignal::freeze`) the signals.
56//!
57//! For example, if you want to route GPIO3 to both a Pulse Counter
58//! input and a [UART](crate::uart::Uart) RX line, you will need to make sure
59//! one of the signals is frozen, otherwise the driver that is configured later
60//! will overwrite the other driver's configuration. Configuring the signals on
61//! multiple cores is undefined behaviour unless you ensure the configuration
62//! does not happen at the same time.
63//!
64//! ### Using pins and signals
65//!
66//! A GPIO pin can be configured either with a GPIO driver such as [`Input`], or
67//! by a peripheral driver using a pin assignment method such as
68//! [`Spi::with_mosi`]. The peripheral drivers' preferences can be overridden by
69//! passing a pin driver to the peripheral driver. When converting a driver to
70//! signals, the underlying signals will be initially
71//! [frozen](InputSignal::freeze) to support this use case.
72//!
73//! ## Inverting inputs and outputs
74//!
75//! The GPIO matrix allows for inverting the input and output signals. This can
76//! be configured via [`InputSignal::with_input_inverter`] and
77//! [`OutputSignal::with_input_inverter`]. The hardware is configured
78//! accordingly when the signal is connected to a peripheral input or output.
79//!
80//! ## Connection rules
81//!
82//! Peripheral signals and GPIOs can be connected with the following
83//! constraints:
84//!
85//! - A peripheral input signal must be driven by exactly one signal, which can be a GPIO input or a
86//!   constant level.
87//! - A peripheral output signal can be connected to any number of GPIOs. These GPIOs can be
88//!   configured differently. The peripheral drivers will only support a single connection (that is,
89//!   they disconnect previously configured signals on repeat calls to the same function), but you
90//!   can use `esp_hal::gpio::OutputSignal::connect_to` (note that the type is currently hidden from
91//!   the documentation) to connect multiple GPIOs to the same output signal.
92//! - A GPIO input signal can be connected to any number of peripheral inputs.
93//! - A GPIO output can be driven by only one peripheral output.
94//!
95//! [`GPIO0`]: crate::peripherals::GPIO0
96//! [`Spi::with_mosi`]: crate::spi::master::Spi::with_mosi
97
98#[cfg(feature = "unstable")]
99use crate::gpio::{Input, Output};
100use crate::{
101    gpio::{
102        self,
103        AlternateFunction,
104        AnyPin,
105        Flex,
106        InputPin,
107        Level,
108        NoPin,
109        OutputPin,
110        Pin,
111        PinGuard,
112    },
113    peripherals::GPIO,
114    private::{self, Sealed},
115};
116
117/// The base of all peripheral signals.
118///
119/// This trait represents a signal in the GPIO matrix. Signals are converted or
120/// split from GPIO pins and can be connected to peripheral inputs and outputs.
121///
122/// All signals can be peripheral inputs, but not all output-like types should
123/// be allowed to be passed as inputs. This trait bridges this gap by defining
124/// the logic, but not declaring the signal to be an actual Input signal.
125pub trait PeripheralSignal<'d>: Sealed {
126    /// Connects the peripheral input to an input signal source.
127    #[doc(hidden)] // Considered unstable
128    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal);
129}
130
131/// A signal that can be connected to a peripheral input.
132///
133/// Peripheral drivers are encouraged to accept types that implement this and
134/// [`PeripheralOutput`] as arguments instead of pin types.
135#[allow(
136    private_bounds,
137    reason = "InputSignal is unstable, but the trait needs to be public"
138)]
139pub trait PeripheralInput<'d>: Into<InputSignal<'d>> + PeripheralSignal<'d> {}
140
141/// A signal that can be connected to a peripheral input and/or output.
142///
143/// Peripheral drivers are encouraged to accept types that implement this and
144/// [`PeripheralInput`] as arguments instead of pin types.
145#[allow(
146    private_bounds,
147    reason = "OutputSignal is unstable, but the trait needs to be public"
148)]
149pub trait PeripheralOutput<'d>: Into<OutputSignal<'d>> + PeripheralSignal<'d> {
150    /// Connects the peripheral output to an output signal target.
151    #[doc(hidden)] // Considered unstable
152    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal);
153
154    /// Disconnects the peripheral output from an output signal target.
155    ///
156    /// This function clears the entry in the IO MUX that
157    /// associates this output pin with a previously connected
158    /// [signal](`gpio::OutputSignal`). Any other outputs connected to the
159    /// peripheral remain intact.
160    #[doc(hidden)] // Considered unstable
161    fn disconnect_from_peripheral_output(&self);
162}
163
164// Pins
165impl<'d, P> PeripheralSignal<'d> for P
166where
167    P: Pin + 'd,
168{
169    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
170        let pin = unsafe { AnyPin::steal(self.number()) };
171        InputSignal::new(pin).connect_input_to_peripheral(signal);
172    }
173}
174impl<'d, P> PeripheralInput<'d> for P where P: InputPin + 'd {}
175
176impl<'d, P> PeripheralOutput<'d> for P
177where
178    P: OutputPin + 'd,
179{
180    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
181        let pin = unsafe { AnyPin::steal(self.number()) };
182        OutputSignal::new(pin).connect_peripheral_to_output(signal);
183    }
184    fn disconnect_from_peripheral_output(&self) {
185        let pin = unsafe { AnyPin::steal(self.number()) };
186        OutputSignal::new(pin).disconnect_from_peripheral_output();
187    }
188}
189
190// Pin drivers
191#[instability::unstable]
192impl<'d> PeripheralSignal<'d> for Flex<'d> {
193    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
194        self.pin.connect_input_to_peripheral(signal);
195    }
196}
197#[instability::unstable]
198impl<'d> PeripheralInput<'d> for Flex<'d> {}
199#[instability::unstable]
200impl<'d> PeripheralOutput<'d> for Flex<'d> {
201    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
202        self.pin.connect_peripheral_to_output(signal);
203    }
204    fn disconnect_from_peripheral_output(&self) {
205        self.pin.disconnect_from_peripheral_output();
206    }
207}
208
209#[instability::unstable]
210impl<'d> PeripheralSignal<'d> for Input<'d> {
211    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
212        self.pin.connect_input_to_peripheral(signal);
213    }
214}
215#[instability::unstable]
216impl<'d> PeripheralInput<'d> for Input<'d> {}
217
218#[instability::unstable]
219impl<'d> PeripheralSignal<'d> for Output<'d> {
220    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
221        self.pin.connect_input_to_peripheral(signal);
222    }
223}
224#[instability::unstable]
225impl<'d> PeripheralOutput<'d> for Output<'d> {
226    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
227        self.pin.connect_peripheral_to_output(signal);
228    }
229    fn disconnect_from_peripheral_output(&self) {
230        self.pin.disconnect_from_peripheral_output();
231    }
232}
233
234// Placeholders
235impl PeripheralSignal<'_> for NoPin {
236    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
237        // Arbitrary choice but we need to overwrite a previous signal input
238        // association.
239        Level::Low.connect_input_to_peripheral(signal);
240    }
241}
242impl PeripheralInput<'_> for NoPin {}
243impl PeripheralOutput<'_> for NoPin {
244    fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
245        // A peripheral's outputs may be connected to any number of GPIOs.
246        // Connecting to, and disconnecting from a NoPin is therefore a
247        // no-op, as we are adding and removing nothing from that list of
248        // connections.
249    }
250    fn disconnect_from_peripheral_output(&self) {
251        // A peripheral's outputs may be connected to any number of GPIOs.
252        // Connecting to, and disconnecting from a NoPin is therefore a
253        // no-op, as we are adding and removing nothing from that list of
254        // connections.
255    }
256}
257
258impl PeripheralSignal<'_> for Level {
259    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
260        Signal::Level(*self).connect_to_peripheral_input(signal, false, true);
261    }
262}
263impl PeripheralInput<'_> for Level {}
264impl PeripheralOutput<'_> for Level {
265    fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
266        // There is no such thing as a constant-high level peripheral output,
267        // the implementation just exists for convenience.
268    }
269    fn disconnect_from_peripheral_output(&self) {
270        // There is no such thing as a constant-high level peripheral output,
271        // the implementation just exists for convenience.
272    }
273}
274
275// Split signals
276impl<'d> PeripheralSignal<'d> for InputSignal<'d> {
277    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
278        // Since there can only be one input signal connected to a peripheral
279        // at a time, this function will disconnect any previously
280        // connected input signals.
281        self.pin.connect_to_peripheral_input(
282            signal,
283            self.is_input_inverted(),
284            self.is_gpio_matrix_forced(),
285        );
286    }
287}
288impl<'d> PeripheralInput<'d> for InputSignal<'d> {}
289
290impl<'d> PeripheralSignal<'d> for OutputSignal<'d> {
291    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
292        self.pin.connect_to_peripheral_input(
293            signal,
294            self.is_input_inverted(),
295            self.is_gpio_matrix_forced(),
296        );
297    }
298}
299impl<'d> PeripheralOutput<'d> for OutputSignal<'d> {
300    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
301        self.pin.connect_peripheral_to_output(
302            signal,
303            self.is_output_inverted(),
304            self.is_gpio_matrix_forced(),
305            true,
306            false,
307        );
308    }
309    fn disconnect_from_peripheral_output(&self) {
310        self.pin.disconnect_from_peripheral_output();
311    }
312}
313
314impl gpio::InputSignal {
315    fn can_use_gpio_matrix(self) -> bool {
316        self as usize <= property!("gpio.input_signal_max")
317    }
318
319    /// Connects a peripheral input signal to a GPIO or a constant level.
320    ///
321    /// Note that connecting multiple GPIOs to a single peripheral input is not
322    /// possible and the previous connection will be replaced.
323    ///
324    /// Also note that a peripheral input must always be connected to something,
325    /// so if you want to disconnect it from GPIOs, you should connect it to a
326    /// constant level.
327    ///
328    /// This function allows connecting a peripheral input to either a
329    /// [`PeripheralInput`] or [`PeripheralOutput`] implementation.
330    #[inline]
331    #[instability::unstable]
332    pub fn connect_to<'a>(self, pin: &impl PeripheralSignal<'a>) {
333        pin.connect_input_to_peripheral(self);
334    }
335}
336
337impl gpio::OutputSignal {
338    fn can_use_gpio_matrix(self) -> bool {
339        self as usize <= property!("gpio.output_signal_max")
340    }
341
342    /// Connects a peripheral output signal to a GPIO.
343    ///
344    /// Note that connecting multiple output signals to a single GPIO is not
345    /// possible and the previous connection will be replaced.
346    ///
347    /// Also note that it is possible to connect a peripheral output signal to
348    /// multiple GPIOs, and old connections will not be cleared automatically.
349    #[inline]
350    #[instability::unstable]
351    pub fn connect_to<'d>(self, pin: &impl PeripheralOutput<'d>) {
352        pin.connect_peripheral_to_output(self);
353    }
354
355    /// Disconnects a peripheral output signal from a GPIO.
356    #[inline]
357    #[instability::unstable]
358    pub fn disconnect_from<'d>(self, pin: &impl PeripheralOutput<'d>) {
359        pin.disconnect_from_peripheral_output();
360    }
361}
362
363enum Signal<'d> {
364    Pin(AnyPin<'d>),
365    Level(Level),
366}
367impl Signal<'_> {
368    fn gpio_number(&self) -> Option<u8> {
369        match &self {
370            Signal::Pin(pin) => Some(pin.number()),
371            Signal::Level(_) => None,
372        }
373    }
374
375    unsafe fn clone_unchecked(&self) -> Self {
376        match self {
377            Signal::Pin(pin) => Signal::Pin(unsafe { pin.clone_unchecked() }),
378            Signal::Level(level) => Signal::Level(*level),
379        }
380    }
381
382    fn is_set_high(&self) -> bool {
383        match &self {
384            Signal::Pin(signal) => signal.is_set_high(),
385            Signal::Level(level) => *level == Level::High,
386        }
387    }
388
389    fn is_input_high(&self) -> bool {
390        match &self {
391            Signal::Pin(signal) => signal.is_input_high(),
392            Signal::Level(level) => *level == Level::High,
393        }
394    }
395
396    fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
397        match self {
398            Signal::Pin(pin) => PinGuard::new(pin, signal),
399            Signal::Level(_) => PinGuard::new_unconnected(signal),
400        }
401    }
402
403    fn connect_to_peripheral_input(
404        &self,
405        signal: gpio::InputSignal,
406        is_inverted: bool,
407        force_gpio: bool,
408    ) {
409        let use_gpio_matrix = match self {
410            Signal::Pin(pin) => {
411                let af = if is_inverted || force_gpio {
412                    AlternateFunction::GPIO
413                } else {
414                    pin.input_signals(private::Internal)
415                        .iter()
416                        .find(|(_af, s)| *s == signal)
417                        .map(|(af, _)| *af)
418                        .unwrap_or(AlternateFunction::GPIO)
419                };
420                pin.disable_usb_pads();
421                pin.set_alternate_function(af);
422                af == AlternateFunction::GPIO
423            }
424            Signal::Level(_) => true,
425        };
426
427        if !signal.can_use_gpio_matrix() {
428            assert!(
429                !use_gpio_matrix,
430                "{:?} cannot be routed through the GPIO matrix",
431                signal
432            );
433            // At this point we have set up the AF. The signal does not have a `func_in_sel_cfg`
434            // register, and we must not try to write to it.
435            return;
436        }
437
438        let input = match self {
439            Signal::Pin(pin) => pin.number(),
440            Signal::Level(Level::Low) => property!("gpio.constant_0_input"),
441            Signal::Level(Level::High) => property!("gpio.constant_1_input"),
442        };
443
444        // No need for a critical section, this is a write and not a modify operation.
445        let offset = property!("gpio.func_in_sel_offset");
446        GPIO::regs()
447            .func_in_sel_cfg(signal as usize - offset)
448            .write(|w| unsafe {
449                w.sel().bit(use_gpio_matrix);
450                w.in_inv_sel().bit(is_inverted);
451                // Connect to GPIO or constant level
452                w.in_sel().bits(input)
453            });
454    }
455
456    fn connect_peripheral_to_output(
457        &self,
458        signal: gpio::OutputSignal,
459        is_inverted: bool,
460        force_gpio: bool,
461        peripheral_control_output_enable: bool,
462        invert_output_enable: bool,
463    ) {
464        let Signal::Pin(pin) = self else {
465            return;
466        };
467        let af = if is_inverted || force_gpio {
468            AlternateFunction::GPIO
469        } else {
470            pin.output_signals(private::Internal)
471                .iter()
472                .find(|(_af, s)| *s == signal)
473                .map(|(af, _)| *af)
474                .unwrap_or(AlternateFunction::GPIO)
475        };
476        pin.disable_usb_pads();
477        pin.set_alternate_function(af);
478
479        let use_gpio_matrix = af == AlternateFunction::GPIO;
480
481        assert!(
482            signal.can_use_gpio_matrix() || !use_gpio_matrix,
483            "{:?} cannot be routed through the GPIO matrix",
484            signal
485        );
486
487        GPIO::regs()
488            .func_out_sel_cfg(pin.number() as usize)
489            .write(|w| unsafe {
490                if use_gpio_matrix {
491                    // Ignored if the signal is not routed through the GPIO matrix - alternate
492                    // function selects peripheral signal directly.
493                    w.out_sel().bits(signal as _);
494                    w.inv_sel().bit(is_inverted);
495                }
496                w.oen_sel().bit(!peripheral_control_output_enable);
497                w.oen_inv_sel().bit(invert_output_enable)
498            });
499    }
500
501    fn disconnect_from_peripheral_output(&self) {
502        let Some(number) = self.gpio_number() else {
503            return;
504        };
505        GPIO::regs()
506            .func_out_sel_cfg(number as usize)
507            .modify(|_, w| unsafe { w.out_sel().bits(gpio::OutputSignal::GPIO as _) });
508    }
509}
510
511bitflags::bitflags! {
512    #[derive(Clone, Copy)]
513    struct InputFlags: u8 {
514        const ForceGpioMatrix = 1 << 0;
515        const Frozen          = 1 << 1;
516        const InvertInput     = 1 << 2;
517    }
518}
519
520/// An input signal between a peripheral and a GPIO pin.
521///
522/// If the `InputSignal` was obtained from a pin driver such as
523/// [`Input`](crate::gpio::Input::split), the GPIO driver will be responsible
524/// for configuring the pin with the correct settings, peripheral drivers will
525/// not be able to modify the pin settings.
526///
527/// Multiple input signals can be connected to one pin.
528#[instability::unstable]
529pub struct InputSignal<'d> {
530    pin: Signal<'d>,
531    flags: InputFlags,
532}
533
534impl From<Level> for InputSignal<'_> {
535    fn from(level: Level) -> Self {
536        InputSignal::new_level(level)
537    }
538}
539
540impl From<NoPin> for InputSignal<'_> {
541    fn from(_pin: NoPin) -> Self {
542        InputSignal::new_level(Level::Low)
543    }
544}
545
546impl<'d, P> From<P> for InputSignal<'d>
547where
548    P: Pin + 'd,
549{
550    fn from(input: P) -> Self {
551        InputSignal::new(input.degrade())
552    }
553}
554
555impl<'d> From<Flex<'d>> for InputSignal<'d> {
556    fn from(pin: Flex<'d>) -> Self {
557        pin.peripheral_input()
558    }
559}
560
561#[instability::unstable]
562impl<'d> From<Input<'d>> for InputSignal<'d> {
563    fn from(pin: Input<'d>) -> Self {
564        pin.pin.into()
565    }
566}
567
568impl Sealed for InputSignal<'_> {}
569
570impl Clone for InputSignal<'_> {
571    fn clone(&self) -> Self {
572        Self {
573            pin: unsafe { self.pin.clone_unchecked() },
574            flags: self.flags,
575        }
576    }
577}
578
579impl<'d> InputSignal<'d> {
580    fn new_inner(inner: Signal<'d>) -> Self {
581        Self {
582            pin: inner,
583            flags: InputFlags::empty(),
584        }
585    }
586
587    pub(crate) fn new(pin: AnyPin<'d>) -> Self {
588        Self::new_inner(Signal::Pin(pin))
589    }
590
591    pub(crate) fn new_level(level: Level) -> Self {
592        Self::new_inner(Signal::Level(level))
593    }
594
595    /// Freezes the pin configuration.
596    ///
597    /// This will prevent peripheral drivers using this signal from modifying
598    /// the pin settings.
599    pub fn freeze(mut self) -> Self {
600        self.flags.insert(InputFlags::Frozen);
601        self
602    }
603
604    /// Unfreezes the pin configuration.
605    ///
606    /// This will enable peripheral drivers to modify the pin settings
607    /// again.
608    ///
609    /// # Safety
610    ///
611    /// This function is unsafe because it allows peripherals to modify the pin
612    /// configuration again. This can lead to undefined behavior if the pin
613    /// is being configured by multiple peripherals at the same time. It can
614    /// also lead to surprising behavior if the pin is passed to multiple
615    /// peripherals that expect conflicting settings.
616    pub unsafe fn unfreeze(&mut self) {
617        self.flags.remove(InputFlags::Frozen);
618    }
619
620    /// Returns the GPIO number of the underlying pin.
621    ///
622    /// Returns `None` if the signal is a constant level.
623    pub fn gpio_number(&self) -> Option<u8> {
624        self.pin.gpio_number()
625    }
626
627    /// Returns `true` if the input signal is high.
628    ///
629    /// Note that this does not take [`Self::with_input_inverter`] into account.
630    pub fn is_input_high(&self) -> bool {
631        self.pin.is_input_high()
632    }
633
634    /// Returns the current signal level.
635    ///
636    /// Note that this does not take [`Self::with_input_inverter`] into account.
637    pub fn level(&self) -> Level {
638        self.is_input_high().into()
639    }
640
641    /// Returns `true` if the input signal is configured to be inverted.
642    ///
643    /// Note that the hardware is not configured until the signal is actually
644    /// connected to a peripheral.
645    pub fn is_input_inverted(&self) -> bool {
646        self.flags.contains(InputFlags::InvertInput)
647    }
648
649    /// Consumes the signal and returns a new one that inverts the peripheral's
650    /// input signal.
651    pub fn with_input_inverter(mut self, invert: bool) -> Self {
652        self.flags.set(InputFlags::InvertInput, invert);
653        self
654    }
655
656    /// Consumes the signal and returns a new one that forces the GPIO matrix
657    /// to be used.
658    pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
659        self.flags.set(InputFlags::ForceGpioMatrix, force);
660        self
661    }
662
663    /// Returns `true` if the input signal must be routed through the GPIO
664    /// matrix.
665    pub fn is_gpio_matrix_forced(&self) -> bool {
666        self.flags.contains(InputFlags::ForceGpioMatrix)
667    }
668
669    delegate::delegate! {
670        #[instability::unstable]
671        #[doc(hidden)]
672        to match &self.pin {
673            Signal::Pin(signal) => signal,
674            Signal::Level(_) => NoOp,
675        } {
676            pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
677        }
678    }
679
680    delegate::delegate! {
681        #[instability::unstable]
682        #[doc(hidden)]
683        to match &self.pin {
684            Signal::Pin(_) if self.flags.contains(InputFlags::Frozen) => NoOp,
685            Signal::Pin(signal) => signal,
686            Signal::Level(_) => NoOp,
687        } {
688            pub fn apply_input_config(&self, _config: &gpio::InputConfig);
689            pub fn set_input_enable(&self, on: bool);
690        }
691    }
692}
693
694bitflags::bitflags! {
695    #[derive(Clone, Copy)]
696    struct OutputFlags: u8 {
697        const ForceGpioMatrix = 1 << 0;
698        const Frozen          = 1 << 1;
699        const InvertInput     = 1 << 2;
700        const InvertOutput    = 1 << 3;
701    }
702}
703
704/// An (input and) output signal between a peripheral and a GPIO pin.
705///
706/// If the `OutputSignal` was obtained from a pin driver such as
707/// [`Output`](crate::gpio::Output::split), the GPIO driver will be responsible
708/// for configuring the pin with the correct settings, peripheral drivers will
709/// not be able to modify the pin settings.
710///
711/// Note that connecting this to a peripheral input will enable the input stage
712/// of the GPIO pin.
713///
714/// Multiple pins can be connected to one output signal.
715#[instability::unstable]
716pub struct OutputSignal<'d> {
717    pin: Signal<'d>,
718    flags: OutputFlags,
719}
720
721impl Sealed for OutputSignal<'_> {}
722
723impl From<Level> for OutputSignal<'_> {
724    fn from(level: Level) -> Self {
725        OutputSignal::new_level(level)
726    }
727}
728
729impl From<NoPin> for OutputSignal<'_> {
730    fn from(_pin: NoPin) -> Self {
731        OutputSignal::new_level(Level::Low)
732    }
733}
734
735impl<'d, P> From<P> for OutputSignal<'d>
736where
737    P: OutputPin + 'd,
738{
739    fn from(output: P) -> Self {
740        OutputSignal::new(output.degrade())
741    }
742}
743
744impl<'d> From<Flex<'d>> for OutputSignal<'d> {
745    fn from(pin: Flex<'d>) -> Self {
746        pin.into_peripheral_output()
747    }
748}
749
750#[instability::unstable]
751impl<'d> From<Output<'d>> for OutputSignal<'d> {
752    fn from(pin: Output<'d>) -> Self {
753        pin.pin.into()
754    }
755}
756
757impl<'d> OutputSignal<'d> {
758    fn new_inner(inner: Signal<'d>) -> Self {
759        Self {
760            pin: inner,
761            flags: OutputFlags::empty(),
762        }
763    }
764
765    pub(crate) fn new(pin: AnyPin<'d>) -> Self {
766        Self::new_inner(Signal::Pin(pin))
767    }
768
769    pub(crate) fn new_level(level: Level) -> Self {
770        Self::new_inner(Signal::Level(level))
771    }
772
773    /// Freezes the pin configuration.
774    ///
775    /// This will prevent peripheral drivers using this signal from
776    /// modifying the pin settings.
777    pub fn freeze(mut self) -> Self {
778        self.flags.insert(OutputFlags::Frozen);
779        self
780    }
781
782    /// Unfreezes the pin configuration.
783    ///
784    /// This will enable peripheral drivers to modify the pin settings
785    /// again.
786    ///
787    /// # Safety
788    ///
789    /// This function is unsafe because it allows peripherals to modify the pin
790    /// configuration again. This can lead to undefined behavior if the pin
791    /// is being configured by multiple peripherals at the same time.
792    /// It can also lead to surprising behavior if the pin is passed to multiple
793    /// peripherals that expect conflicting settings.
794    pub unsafe fn unfreeze(&mut self) {
795        self.flags.remove(OutputFlags::Frozen);
796    }
797
798    /// Returns the GPIO number of the underlying pin.
799    ///
800    /// Returns `None` if the signal is a constant level.
801    pub fn gpio_number(&self) -> Option<u8> {
802        self.pin.gpio_number()
803    }
804
805    /// Returns `true` if the input signal is configured to be inverted.
806    ///
807    /// Note that the hardware is not configured until the signal is actually
808    /// connected to a peripheral.
809    pub fn is_input_inverted(&self) -> bool {
810        self.flags.contains(OutputFlags::InvertInput)
811    }
812
813    /// Returns `true` if the output signal is configured to be inverted.
814    ///
815    /// Note that the hardware is not configured until the signal is actually
816    /// connected to a peripheral.
817    pub fn is_output_inverted(&self) -> bool {
818        self.flags.contains(OutputFlags::InvertOutput)
819    }
820
821    /// Consumes the signal and returns a new one that inverts the peripheral's
822    /// output signal.
823    pub fn with_output_inverter(mut self, invert: bool) -> Self {
824        self.flags.set(OutputFlags::InvertOutput, invert);
825        self
826    }
827
828    /// Consumes the signal and returns a new one that inverts the peripheral's
829    /// input signal.
830    pub fn with_input_inverter(mut self, invert: bool) -> Self {
831        self.flags.set(OutputFlags::InvertInput, invert);
832        self
833    }
834
835    /// Consumes the signal and returns a new one that forces the GPIO matrix
836    /// to be used.
837    pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
838        self.flags.set(OutputFlags::ForceGpioMatrix, force);
839        self
840    }
841
842    /// Returns `true` if the input signal must be routed through the GPIO
843    /// matrix.
844    pub fn is_gpio_matrix_forced(&self) -> bool {
845        self.flags.contains(OutputFlags::ForceGpioMatrix)
846    }
847
848    /// Returns `true` if the input signal is high.
849    ///
850    /// Note that this does not take [`Self::with_input_inverter`] into account.
851    pub fn is_input_high(&self) -> bool {
852        self.pin.is_input_high()
853    }
854
855    /// Returns `true` if the output signal is set high.
856    ///
857    /// Note that this does not take [`Self::with_output_inverter`] into
858    /// account.
859    pub fn is_set_high(&self) -> bool {
860        self.pin.is_set_high()
861    }
862
863    #[doc(hidden)]
864    #[instability::unstable]
865    pub(crate) fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
866        signal.connect_to(&self);
867        self.pin.connect_with_guard(signal)
868    }
869
870    delegate::delegate! {
871        #[instability::unstable]
872        #[doc(hidden)]
873        to match &self.pin {
874            Signal::Pin(signal) => signal,
875            Signal::Level(_) => NoOp,
876        } {
877            pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
878            pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
879        }
880    }
881
882    delegate::delegate! {
883        #[instability::unstable]
884        #[doc(hidden)]
885        to match &self.pin {
886            Signal::Pin(_) if self.flags.contains(OutputFlags::Frozen) => NoOp,
887            Signal::Pin(pin) => pin,
888            Signal::Level(_) => NoOp,
889        } {
890            pub fn apply_input_config(&self, _config: &gpio::InputConfig);
891            pub fn apply_output_config(&self, _config: &gpio::OutputConfig);
892            pub fn set_input_enable(&self, on: bool);
893            pub fn set_output_enable(&self, on: bool);
894            pub fn set_output_high(&self, on: bool);
895        }
896    }
897}
898
899struct NoOp;
900
901impl NoOp {
902    fn set_input_enable(&self, _on: bool) {}
903    fn set_output_enable(&self, _on: bool) {}
904    fn set_output_high(&self, _on: bool) {}
905    fn apply_input_config(&self, _config: &gpio::InputConfig) {}
906    fn apply_output_config(&self, _config: &gpio::OutputConfig) {}
907
908    fn input_signals(
909        &self,
910        _: private::Internal,
911    ) -> &'static [(AlternateFunction, gpio::InputSignal)] {
912        &[]
913    }
914
915    fn output_signals(
916        &self,
917        _: private::Internal,
918    ) -> &'static [(AlternateFunction, gpio::OutputSignal)] {
919        &[]
920    }
921}
922
923#[procmacros::doc_replace]
924/// ```rust,compile_fail
925/// // Regression test for <https://github.com/esp-rs/esp-hal/issues/3313>
926/// // This test case is expected to generate the following error:
927/// // error[E0277]: the trait bound `Output<'_>: PeripheralInput<'_>` is not satisfied
928/// //   --> src\gpio\interconnect.rs:977:5
929/// //    |
930/// // 31 |   function_expects_input(
931/// //    |   ---------------------- required by a bound introduced by this call
932/// // 32 | /     Output::new(peripherals.GPIO0,
933/// // 33 | |     Level::Low,
934/// // 34 | |     Default::default()),
935/// //    | |_______________________^ the trait `InputPin` is not implemented for `Output<'_>`
936/// // FIXME: due to <https://github.com/rust-lang/rust/issues/139924> this test may be ineffective.
937/// //        It can be manually verified by changing it to `no_run` for a `run-doc-tests` run.
938/// # {before_snippet}
939/// use esp_hal::gpio::{Output, Level, interconnect::PeripheralInput};
940///
941/// fn function_expects_input<'d>(_: impl PeripheralInput<'d>) {}
942///
943/// function_expects_input(
944///     Output::new(peripherals.GPIO0,
945///     Level::Low,
946///     Default::default()),
947/// );
948///
949/// # {after_snippet}
950/// ```
951fn _compile_tests() {}