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