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