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