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
86//!   be a GPIO input or a constant level.
87//! - A peripheral output signal can be connected to any number of GPIOs. These
88//!   GPIOs can be configured differently. The peripheral drivers will only
89//!   support a single connection (that is, they disconnect previously
90//!   configured signals on repeat calls to the same function), but you can use
91//!   `esp_hal::gpio::OutputSignal::connect_to` (note that the type is currently
92//!   hidden from the documentation) to connect multiple GPIOs to the same
93//!   output signal.
94//! - A GPIO input signal can be connected to any number of peripheral inputs.
95//! - A GPIO output can be driven by only one peripheral output.
96//!
97//! [`GPIO0`]: crate::peripherals::GPIO0
98//! [`Spi::with_mosi`]: crate::spi::master::Spi::with_mosi
99
100#[cfg(feature = "unstable")]
101use crate::gpio::{Input, Output};
102use crate::{
103    gpio::{
104        self,
105        AlternateFunction,
106        AnyPin,
107        FUNC_IN_SEL_OFFSET,
108        Flex,
109        GPIO_FUNCTION,
110        INPUT_SIGNAL_MAX,
111        InputPin,
112        InputSignalType,
113        Level,
114        NoPin,
115        OUTPUT_SIGNAL_MAX,
116        OutputPin,
117        OutputSignalType,
118        Pin,
119        PinGuard,
120    },
121    peripherals::GPIO,
122    private::{self, Sealed},
123};
124
125/// The base of all peripheral signals.
126///
127/// This trait represents a signal in the GPIO matrix. Signals are converted or
128/// split from GPIO pins and can be connected to peripheral inputs and outputs.
129///
130/// All signals can be peripheral inputs, but not all output-like types should
131/// be allowed to be passed as inputs. This trait bridges this gap by defining
132/// the logic, but not declaring the signal to be an actual Input signal.
133pub trait PeripheralSignal<'d>: Sealed {
134    /// Connects the peripheral input to an input signal source.
135    #[doc(hidden)] // Considered unstable
136    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal);
137}
138
139/// A signal that can be connected to a peripheral input.
140///
141/// Peripheral drivers are encouraged to accept types that implement this and
142/// [`PeripheralOutput`] as arguments instead of pin types.
143#[allow(
144    private_bounds,
145    reason = "InputSignal is unstable, but the trait needs to be public"
146)]
147pub trait PeripheralInput<'d>: Into<InputSignal<'d>> + PeripheralSignal<'d> {}
148
149/// A signal that can be connected to a peripheral input and/or output.
150///
151/// Peripheral drivers are encouraged to accept types that implement this and
152/// [`PeripheralInput`] as arguments instead of pin types.
153#[allow(
154    private_bounds,
155    reason = "OutputSignal is unstable, but the trait needs to be public"
156)]
157pub trait PeripheralOutput<'d>: Into<OutputSignal<'d>> + PeripheralSignal<'d> {
158    /// Connects the peripheral output to an output signal target.
159    #[doc(hidden)] // Considered unstable
160    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal);
161
162    /// Disconnects the peripheral output from an output signal target.
163    ///
164    /// This function clears the entry in the IO MUX that
165    /// associates this output pin with a previously connected
166    /// [signal](`gpio::OutputSignal`). Any other outputs connected to the
167    /// peripheral remain intact.
168    #[doc(hidden)] // Considered unstable
169    fn disconnect_from_peripheral_output(&self);
170}
171
172// Pins
173impl<'d, P> PeripheralSignal<'d> for P
174where
175    P: Pin + 'd,
176{
177    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
178        let pin = unsafe { AnyPin::steal(self.number()) };
179        InputSignal::new(pin).connect_input_to_peripheral(signal);
180    }
181}
182impl<'d, P> PeripheralInput<'d> for P where P: InputPin + 'd {}
183
184impl<'d, P> PeripheralOutput<'d> for P
185where
186    P: OutputPin + 'd,
187{
188    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
189        let pin = unsafe { AnyPin::steal(self.number()) };
190        OutputSignal::new(pin).connect_peripheral_to_output(signal);
191    }
192    fn disconnect_from_peripheral_output(&self) {
193        let pin = unsafe { AnyPin::steal(self.number()) };
194        OutputSignal::new(pin).disconnect_from_peripheral_output();
195    }
196}
197
198// Pin drivers
199#[instability::unstable]
200impl<'d> PeripheralSignal<'d> for Flex<'d> {
201    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
202        self.pin.connect_input_to_peripheral(signal);
203    }
204}
205#[instability::unstable]
206impl<'d> PeripheralInput<'d> for Flex<'d> {}
207#[instability::unstable]
208impl<'d> PeripheralOutput<'d> for Flex<'d> {
209    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
210        self.pin.connect_peripheral_to_output(signal);
211    }
212    fn disconnect_from_peripheral_output(&self) {
213        self.pin.disconnect_from_peripheral_output();
214    }
215}
216
217#[instability::unstable]
218impl<'d> PeripheralSignal<'d> for Input<'d> {
219    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
220        self.pin.connect_input_to_peripheral(signal);
221    }
222}
223#[instability::unstable]
224impl<'d> PeripheralInput<'d> for Input<'d> {}
225
226#[instability::unstable]
227impl<'d> PeripheralSignal<'d> for Output<'d> {
228    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
229        self.pin.connect_input_to_peripheral(signal);
230    }
231}
232#[instability::unstable]
233impl<'d> PeripheralOutput<'d> for Output<'d> {
234    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
235        self.pin.connect_peripheral_to_output(signal);
236    }
237    fn disconnect_from_peripheral_output(&self) {
238        self.pin.disconnect_from_peripheral_output();
239    }
240}
241
242// Placeholders
243impl PeripheralSignal<'_> for NoPin {
244    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
245        // Arbitrary choice but we need to overwrite a previous signal input
246        // association.
247        Level::Low.connect_input_to_peripheral(signal);
248    }
249}
250impl PeripheralInput<'_> for NoPin {}
251impl PeripheralOutput<'_> for NoPin {
252    fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
253        // A peripheral's outputs may be connected to any number of GPIOs.
254        // Connecting to, and disconnecting from a NoPin is therefore a
255        // no-op, as we are adding and removing nothing from that list of
256        // connections.
257    }
258    fn disconnect_from_peripheral_output(&self) {
259        // A peripheral's outputs may be connected to any number of GPIOs.
260        // Connecting to, and disconnecting from a NoPin is therefore a
261        // no-op, as we are adding and removing nothing from that list of
262        // connections.
263    }
264}
265
266impl PeripheralSignal<'_> for Level {
267    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
268        Signal::Level(*self).connect_to_peripheral_input(signal, false, true);
269    }
270}
271impl PeripheralInput<'_> for Level {}
272impl PeripheralOutput<'_> for Level {
273    fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
274        // There is no such thing as a constant-high level peripheral output,
275        // the implementation just exists for convenience.
276    }
277    fn disconnect_from_peripheral_output(&self) {
278        // There is no such thing as a constant-high level peripheral output,
279        // the implementation just exists for convenience.
280    }
281}
282
283// Split signals
284impl<'d> PeripheralSignal<'d> for InputSignal<'d> {
285    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
286        // Since there can only be one input signal connected to a peripheral
287        // at a time, this function will disconnect any previously
288        // connected input signals.
289        self.pin.connect_to_peripheral_input(
290            signal,
291            self.is_input_inverted(),
292            self.is_gpio_matrix_forced(),
293        );
294    }
295}
296impl<'d> PeripheralInput<'d> for InputSignal<'d> {}
297
298impl<'d> PeripheralSignal<'d> for OutputSignal<'d> {
299    fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
300        self.pin.connect_to_peripheral_input(
301            signal,
302            self.is_input_inverted(),
303            self.is_gpio_matrix_forced(),
304        );
305    }
306}
307impl<'d> PeripheralOutput<'d> for OutputSignal<'d> {
308    fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
309        self.pin.connect_peripheral_to_output(
310            signal,
311            self.is_output_inverted(),
312            self.is_gpio_matrix_forced(),
313            true,
314            false,
315        );
316    }
317    fn disconnect_from_peripheral_output(&self) {
318        self.pin.disconnect_from_peripheral_output();
319    }
320}
321
322impl gpio::InputSignal {
323    fn can_use_gpio_matrix(self) -> bool {
324        self as InputSignalType <= INPUT_SIGNAL_MAX
325    }
326
327    /// Connects a peripheral input signal to a GPIO or a constant level.
328    ///
329    /// Note that connecting multiple GPIOs to a single peripheral input is not
330    /// possible and the previous connection will be replaced.
331    ///
332    /// Also note that a peripheral input must always be connected to something,
333    /// so if you want to disconnect it from GPIOs, you should connect it to a
334    /// constant level.
335    ///
336    /// This function allows connecting a peripheral input to either a
337    /// [`PeripheralInput`] or [`PeripheralOutput`] implementation.
338    #[inline]
339    #[instability::unstable]
340    pub fn connect_to<'a>(self, pin: &impl PeripheralSignal<'a>) {
341        pin.connect_input_to_peripheral(self);
342    }
343}
344
345impl gpio::OutputSignal {
346    fn can_use_gpio_matrix(self) -> bool {
347        self as OutputSignalType <= OUTPUT_SIGNAL_MAX
348    }
349
350    /// Connects a peripheral output signal to a GPIO.
351    ///
352    /// Note that connecting multiple output signals to a single GPIO is not
353    /// possible and the previous connection will be replaced.
354    ///
355    /// Also note that it is possible to connect a peripheral output signal to
356    /// multiple GPIOs, and old connections will not be cleared automatically.
357    #[inline]
358    #[instability::unstable]
359    pub fn connect_to<'d>(self, pin: &impl PeripheralOutput<'d>) {
360        pin.connect_peripheral_to_output(self);
361    }
362
363    /// Disconnects a peripheral output signal from a GPIO.
364    #[inline]
365    #[instability::unstable]
366    pub fn disconnect_from<'d>(self, pin: &impl PeripheralOutput<'d>) {
367        pin.disconnect_from_peripheral_output();
368    }
369}
370
371enum Signal<'d> {
372    Pin(AnyPin<'d>),
373    Level(Level),
374}
375impl Signal<'_> {
376    fn gpio_number(&self) -> Option<u8> {
377        match &self {
378            Signal::Pin(pin) => Some(pin.number()),
379            Signal::Level(_) => None,
380        }
381    }
382
383    unsafe fn clone_unchecked(&self) -> Self {
384        match self {
385            Signal::Pin(pin) => Signal::Pin(unsafe { pin.clone_unchecked() }),
386            Signal::Level(level) => Signal::Level(*level),
387        }
388    }
389
390    fn is_set_high(&self) -> bool {
391        match &self {
392            Signal::Pin(signal) => signal.is_set_high(),
393            Signal::Level(level) => *level == Level::High,
394        }
395    }
396
397    fn is_input_high(&self) -> bool {
398        match &self {
399            Signal::Pin(signal) => signal.is_input_high(),
400            Signal::Level(level) => *level == Level::High,
401        }
402    }
403
404    fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
405        match self {
406            Signal::Pin(pin) => PinGuard::new(pin, signal),
407            Signal::Level(_) => PinGuard::new_unconnected(signal),
408        }
409    }
410
411    fn connect_to_peripheral_input(
412        &self,
413        signal: gpio::InputSignal,
414        is_inverted: bool,
415        force_gpio: bool,
416    ) {
417        let use_gpio_matrix = match self {
418            Signal::Pin(pin) => {
419                let af = if is_inverted || force_gpio {
420                    GPIO_FUNCTION
421                } else {
422                    pin.input_signals(private::Internal)
423                        .iter()
424                        .find(|(_af, s)| *s == signal)
425                        .map(|(af, _)| *af)
426                        .unwrap_or(GPIO_FUNCTION)
427                };
428                pin.set_alternate_function(af);
429                af == GPIO_FUNCTION
430            }
431            Signal::Level(_) => true,
432        };
433
434        let input = match self {
435            Signal::Pin(pin) => pin.number(),
436            Signal::Level(Level::Low) => gpio::ZERO_INPUT,
437            Signal::Level(Level::High) => gpio::ONE_INPUT,
438        };
439
440        assert!(
441            signal.can_use_gpio_matrix() || !use_gpio_matrix,
442            "{:?} cannot be routed through the GPIO matrix",
443            signal
444        );
445        // No need for a critical section, this is a write and not a modify operation.
446        GPIO::regs()
447            .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_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            GPIO_FUNCTION
469        } else {
470            pin.output_signals(private::Internal)
471                .iter()
472                .find(|(_af, s)| *s == signal)
473                .map(|(af, _)| *af)
474                .unwrap_or(GPIO_FUNCTION)
475        };
476        pin.set_alternate_function(af);
477
478        let use_gpio_matrix = af == GPIO_FUNCTION;
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    pub(crate) fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
865        signal.connect_to(&self);
866        self.pin.connect_with_guard(signal)
867    }
868
869    delegate::delegate! {
870        #[instability::unstable]
871        #[doc(hidden)]
872        to match &self.pin {
873            Signal::Pin(signal) => signal,
874            Signal::Level(_) => NoOp,
875        } {
876            pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
877            pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
878        }
879    }
880
881    delegate::delegate! {
882        #[instability::unstable]
883        #[doc(hidden)]
884        to match &self.pin {
885            Signal::Pin(_) if self.flags.contains(OutputFlags::Frozen) => NoOp,
886            Signal::Pin(pin) => pin,
887            Signal::Level(_) => NoOp,
888        } {
889            pub fn apply_input_config(&self, _config: &gpio::InputConfig);
890            pub fn apply_output_config(&self, _config: &gpio::OutputConfig);
891            pub fn set_input_enable(&self, on: bool);
892            pub fn set_output_enable(&self, on: bool);
893            pub fn set_output_high(&self, on: bool);
894        }
895    }
896}
897
898struct NoOp;
899
900impl NoOp {
901    fn set_input_enable(&self, _on: bool) {}
902    fn set_output_enable(&self, _on: bool) {}
903    fn set_output_high(&self, _on: bool) {}
904    fn apply_input_config(&self, _config: &gpio::InputConfig) {}
905    fn apply_output_config(&self, _config: &gpio::OutputConfig) {}
906
907    fn input_signals(
908        &self,
909        _: private::Internal,
910    ) -> &'static [(AlternateFunction, gpio::InputSignal)] {
911        &[]
912    }
913
914    fn output_signals(
915        &self,
916        _: private::Internal,
917    ) -> &'static [(AlternateFunction, gpio::OutputSignal)] {
918        &[]
919    }
920}
921
922/// ```rust,compile_fail
923/// // Regression test for <https://github.com/esp-rs/esp-hal/issues/3313>
924/// // This test case is expected to generate the following error:
925/// // error[E0277]: the trait bound `Output<'_>: PeripheralInput<'_>` is not satisfied
926/// //   --> src\gpio\interconnect.rs:977:5
927/// //    |
928/// // 31 |   function_expects_input(
929/// //    |   ---------------------- required by a bound introduced by this call
930/// // 32 | /     Output::new(peripherals.GPIO0,
931/// // 33 | |     Level::Low,
932/// // 34 | |     Default::default()),
933/// //    | |_______________________^ the trait `InputPin` is not implemented for `Output<'_>`
934/// // FIXME: due to <https://github.com/rust-lang/rust/issues/139924> this test may be ineffective.
935/// //        It can be manually verified by changing it to `no_run` for a `run-doc-tests` run.
936#[doc = crate::before_snippet!()]
937/// use esp_hal::gpio::{Output, Level, interconnect::PeripheralInput};
938///
939/// fn function_expects_input<'d>(_: impl PeripheralInput<'d>) {}
940///
941/// function_expects_input(
942///     Output::new(peripherals.GPIO0,
943///     Level::Low,
944///     Default::default()),
945/// );
946///
947/// # Ok(())
948/// # }
949/// ```
950fn _compile_tests() {}