Skip to main content

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