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 if !signal.can_use_gpio_matrix() {
428 assert!(
429 !use_gpio_matrix,
430 "{:?} cannot be routed through the GPIO matrix",
431 signal
432 );
433 // At this point we have set up the AF. The signal does not have a `func_in_sel_cfg`
434 // register, and we must not try to write to it.
435 return;
436 }
437
438 let input = match self {
439 Signal::Pin(pin) => pin.number(),
440 Signal::Level(Level::Low) => property!("gpio.constant_0_input"),
441 Signal::Level(Level::High) => property!("gpio.constant_1_input"),
442 };
443
444 // No need for a critical section, this is a write and not a modify operation.
445 let offset = property!("gpio.func_in_sel_offset");
446 GPIO::regs()
447 .func_in_sel_cfg(signal as usize - 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 AlternateFunction::GPIO
469 } else {
470 pin.output_signals(private::Internal)
471 .iter()
472 .find(|(_af, s)| *s == signal)
473 .map(|(af, _)| *af)
474 .unwrap_or(AlternateFunction::GPIO)
475 };
476 pin.disable_usb_pads();
477 pin.set_alternate_function(af);
478
479 let use_gpio_matrix = af == AlternateFunction::GPIO;
480
481 assert!(
482 signal.can_use_gpio_matrix() || !use_gpio_matrix,
483 "{:?} cannot be routed through the GPIO matrix",
484 signal
485 );
486
487 GPIO::regs()
488 .func_out_sel_cfg(pin.number() as usize)
489 .write(|w| unsafe {
490 if use_gpio_matrix {
491 // Ignored if the signal is not routed through the GPIO matrix - alternate
492 // function selects peripheral signal directly.
493 w.out_sel().bits(signal as _);
494 w.inv_sel().bit(is_inverted);
495 }
496 w.oen_sel().bit(!peripheral_control_output_enable);
497 w.oen_inv_sel().bit(invert_output_enable)
498 });
499 }
500
501 fn disconnect_from_peripheral_output(&self) {
502 let Some(number) = self.gpio_number() else {
503 return;
504 };
505 GPIO::regs()
506 .func_out_sel_cfg(number as usize)
507 .modify(|_, w| unsafe { w.out_sel().bits(gpio::OutputSignal::GPIO as _) });
508 }
509}
510
511bitflags::bitflags! {
512 #[derive(Clone, Copy)]
513 struct InputFlags: u8 {
514 const ForceGpioMatrix = 1 << 0;
515 const Frozen = 1 << 1;
516 const InvertInput = 1 << 2;
517 }
518}
519
520/// An input signal between a peripheral and a GPIO pin.
521///
522/// If the `InputSignal` was obtained from a pin driver such as
523/// [`Input`](crate::gpio::Input::split), the GPIO driver will be responsible
524/// for configuring the pin with the correct settings, peripheral drivers will
525/// not be able to modify the pin settings.
526///
527/// Multiple input signals can be connected to one pin.
528#[instability::unstable]
529pub struct InputSignal<'d> {
530 pin: Signal<'d>,
531 flags: InputFlags,
532}
533
534impl From<Level> for InputSignal<'_> {
535 fn from(level: Level) -> Self {
536 InputSignal::new_level(level)
537 }
538}
539
540impl From<NoPin> for InputSignal<'_> {
541 fn from(_pin: NoPin) -> Self {
542 InputSignal::new_level(Level::Low)
543 }
544}
545
546impl<'d, P> From<P> for InputSignal<'d>
547where
548 P: Pin + 'd,
549{
550 fn from(input: P) -> Self {
551 InputSignal::new(input.degrade())
552 }
553}
554
555impl<'d> From<Flex<'d>> for InputSignal<'d> {
556 fn from(pin: Flex<'d>) -> Self {
557 pin.peripheral_input()
558 }
559}
560
561#[instability::unstable]
562impl<'d> From<Input<'d>> for InputSignal<'d> {
563 fn from(pin: Input<'d>) -> Self {
564 pin.pin.into()
565 }
566}
567
568impl Sealed for InputSignal<'_> {}
569
570impl Clone for InputSignal<'_> {
571 fn clone(&self) -> Self {
572 Self {
573 pin: unsafe { self.pin.clone_unchecked() },
574 flags: self.flags,
575 }
576 }
577}
578
579impl<'d> InputSignal<'d> {
580 fn new_inner(inner: Signal<'d>) -> Self {
581 Self {
582 pin: inner,
583 flags: InputFlags::empty(),
584 }
585 }
586
587 pub(crate) fn new(pin: AnyPin<'d>) -> Self {
588 Self::new_inner(Signal::Pin(pin))
589 }
590
591 pub(crate) fn new_level(level: Level) -> Self {
592 Self::new_inner(Signal::Level(level))
593 }
594
595 /// Freezes the pin configuration.
596 ///
597 /// This will prevent peripheral drivers using this signal from modifying
598 /// the pin settings.
599 pub fn freeze(mut self) -> Self {
600 self.flags.insert(InputFlags::Frozen);
601 self
602 }
603
604 /// Unfreezes the pin configuration.
605 ///
606 /// This will enable peripheral drivers to modify the pin settings
607 /// again.
608 ///
609 /// # Safety
610 ///
611 /// This function is unsafe because it allows peripherals to modify the pin
612 /// configuration again. This can lead to undefined behavior if the pin
613 /// is being configured by multiple peripherals at the same time. It can
614 /// also lead to surprising behavior if the pin is passed to multiple
615 /// peripherals that expect conflicting settings.
616 pub unsafe fn unfreeze(&mut self) {
617 self.flags.remove(InputFlags::Frozen);
618 }
619
620 /// Returns the GPIO number of the underlying pin.
621 ///
622 /// Returns `None` if the signal is a constant level.
623 pub fn gpio_number(&self) -> Option<u8> {
624 self.pin.gpio_number()
625 }
626
627 /// Returns `true` if the input signal is high.
628 ///
629 /// Note that this does not take [`Self::with_input_inverter`] into account.
630 pub fn is_input_high(&self) -> bool {
631 self.pin.is_input_high()
632 }
633
634 /// Returns the current signal level.
635 ///
636 /// Note that this does not take [`Self::with_input_inverter`] into account.
637 pub fn level(&self) -> Level {
638 self.is_input_high().into()
639 }
640
641 /// Returns `true` if the input signal is configured to be inverted.
642 ///
643 /// Note that the hardware is not configured until the signal is actually
644 /// connected to a peripheral.
645 pub fn is_input_inverted(&self) -> bool {
646 self.flags.contains(InputFlags::InvertInput)
647 }
648
649 /// Consumes the signal and returns a new one that inverts the peripheral's
650 /// input signal.
651 pub fn with_input_inverter(mut self, invert: bool) -> Self {
652 self.flags.set(InputFlags::InvertInput, invert);
653 self
654 }
655
656 /// Consumes the signal and returns a new one that forces the GPIO matrix
657 /// to be used.
658 pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
659 self.flags.set(InputFlags::ForceGpioMatrix, force);
660 self
661 }
662
663 /// Returns `true` if the input signal must be routed through the GPIO
664 /// matrix.
665 pub fn is_gpio_matrix_forced(&self) -> bool {
666 self.flags.contains(InputFlags::ForceGpioMatrix)
667 }
668
669 delegate::delegate! {
670 #[instability::unstable]
671 #[doc(hidden)]
672 to match &self.pin {
673 Signal::Pin(signal) => signal,
674 Signal::Level(_) => NoOp,
675 } {
676 pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
677 }
678 }
679
680 delegate::delegate! {
681 #[instability::unstable]
682 #[doc(hidden)]
683 to match &self.pin {
684 Signal::Pin(_) if self.flags.contains(InputFlags::Frozen) => NoOp,
685 Signal::Pin(signal) => signal,
686 Signal::Level(_) => NoOp,
687 } {
688 pub fn apply_input_config(&self, _config: &gpio::InputConfig);
689 pub fn set_input_enable(&self, on: bool);
690 }
691 }
692}
693
694bitflags::bitflags! {
695 #[derive(Clone, Copy)]
696 struct OutputFlags: u8 {
697 const ForceGpioMatrix = 1 << 0;
698 const Frozen = 1 << 1;
699 const InvertInput = 1 << 2;
700 const InvertOutput = 1 << 3;
701 }
702}
703
704/// An (input and) output signal between a peripheral and a GPIO pin.
705///
706/// If the `OutputSignal` was obtained from a pin driver such as
707/// [`Output`](crate::gpio::Output::split), the GPIO driver will be responsible
708/// for configuring the pin with the correct settings, peripheral drivers will
709/// not be able to modify the pin settings.
710///
711/// Note that connecting this to a peripheral input will enable the input stage
712/// of the GPIO pin.
713///
714/// Multiple pins can be connected to one output signal.
715#[instability::unstable]
716pub struct OutputSignal<'d> {
717 pin: Signal<'d>,
718 flags: OutputFlags,
719}
720
721impl Sealed for OutputSignal<'_> {}
722
723impl From<Level> for OutputSignal<'_> {
724 fn from(level: Level) -> Self {
725 OutputSignal::new_level(level)
726 }
727}
728
729impl From<NoPin> for OutputSignal<'_> {
730 fn from(_pin: NoPin) -> Self {
731 OutputSignal::new_level(Level::Low)
732 }
733}
734
735impl<'d, P> From<P> for OutputSignal<'d>
736where
737 P: OutputPin + 'd,
738{
739 fn from(output: P) -> Self {
740 OutputSignal::new(output.degrade())
741 }
742}
743
744impl<'d> From<Flex<'d>> for OutputSignal<'d> {
745 fn from(pin: Flex<'d>) -> Self {
746 pin.into_peripheral_output()
747 }
748}
749
750#[instability::unstable]
751impl<'d> From<Output<'d>> for OutputSignal<'d> {
752 fn from(pin: Output<'d>) -> Self {
753 pin.pin.into()
754 }
755}
756
757impl<'d> OutputSignal<'d> {
758 fn new_inner(inner: Signal<'d>) -> Self {
759 Self {
760 pin: inner,
761 flags: OutputFlags::empty(),
762 }
763 }
764
765 pub(crate) fn new(pin: AnyPin<'d>) -> Self {
766 Self::new_inner(Signal::Pin(pin))
767 }
768
769 pub(crate) fn new_level(level: Level) -> Self {
770 Self::new_inner(Signal::Level(level))
771 }
772
773 /// Freezes the pin configuration.
774 ///
775 /// This will prevent peripheral drivers using this signal from
776 /// modifying the pin settings.
777 pub fn freeze(mut self) -> Self {
778 self.flags.insert(OutputFlags::Frozen);
779 self
780 }
781
782 /// Unfreezes the pin configuration.
783 ///
784 /// This will enable peripheral drivers to modify the pin settings
785 /// again.
786 ///
787 /// # Safety
788 ///
789 /// This function is unsafe because it allows peripherals to modify the pin
790 /// configuration again. This can lead to undefined behavior if the pin
791 /// is being configured by multiple peripherals at the same time.
792 /// It can also lead to surprising behavior if the pin is passed to multiple
793 /// peripherals that expect conflicting settings.
794 pub unsafe fn unfreeze(&mut self) {
795 self.flags.remove(OutputFlags::Frozen);
796 }
797
798 /// Returns the GPIO number of the underlying pin.
799 ///
800 /// Returns `None` if the signal is a constant level.
801 pub fn gpio_number(&self) -> Option<u8> {
802 self.pin.gpio_number()
803 }
804
805 /// Returns `true` if the input signal is configured to be inverted.
806 ///
807 /// Note that the hardware is not configured until the signal is actually
808 /// connected to a peripheral.
809 pub fn is_input_inverted(&self) -> bool {
810 self.flags.contains(OutputFlags::InvertInput)
811 }
812
813 /// Returns `true` if the output signal is configured to be inverted.
814 ///
815 /// Note that the hardware is not configured until the signal is actually
816 /// connected to a peripheral.
817 pub fn is_output_inverted(&self) -> bool {
818 self.flags.contains(OutputFlags::InvertOutput)
819 }
820
821 /// Consumes the signal and returns a new one that inverts the peripheral's
822 /// output signal.
823 pub fn with_output_inverter(mut self, invert: bool) -> Self {
824 self.flags.set(OutputFlags::InvertOutput, invert);
825 self
826 }
827
828 /// Consumes the signal and returns a new one that inverts the peripheral's
829 /// input signal.
830 pub fn with_input_inverter(mut self, invert: bool) -> Self {
831 self.flags.set(OutputFlags::InvertInput, invert);
832 self
833 }
834
835 /// Consumes the signal and returns a new one that forces the GPIO matrix
836 /// to be used.
837 pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
838 self.flags.set(OutputFlags::ForceGpioMatrix, force);
839 self
840 }
841
842 /// Returns `true` if the input signal must be routed through the GPIO
843 /// matrix.
844 pub fn is_gpio_matrix_forced(&self) -> bool {
845 self.flags.contains(OutputFlags::ForceGpioMatrix)
846 }
847
848 /// Returns `true` if the input signal is high.
849 ///
850 /// Note that this does not take [`Self::with_input_inverter`] into account.
851 pub fn is_input_high(&self) -> bool {
852 self.pin.is_input_high()
853 }
854
855 /// Returns `true` if the output signal is set high.
856 ///
857 /// Note that this does not take [`Self::with_output_inverter`] into
858 /// account.
859 pub fn is_set_high(&self) -> bool {
860 self.pin.is_set_high()
861 }
862
863 #[doc(hidden)]
864 #[instability::unstable]
865 pub(crate) fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
866 signal.connect_to(&self);
867 self.pin.connect_with_guard(signal)
868 }
869
870 delegate::delegate! {
871 #[instability::unstable]
872 #[doc(hidden)]
873 to match &self.pin {
874 Signal::Pin(signal) => signal,
875 Signal::Level(_) => NoOp,
876 } {
877 pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
878 pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
879 }
880 }
881
882 delegate::delegate! {
883 #[instability::unstable]
884 #[doc(hidden)]
885 to match &self.pin {
886 Signal::Pin(_) if self.flags.contains(OutputFlags::Frozen) => NoOp,
887 Signal::Pin(pin) => pin,
888 Signal::Level(_) => NoOp,
889 } {
890 pub fn apply_input_config(&self, _config: &gpio::InputConfig);
891 pub fn apply_output_config(&self, _config: &gpio::OutputConfig);
892 pub fn set_input_enable(&self, on: bool);
893 pub fn set_output_enable(&self, on: bool);
894 pub fn set_output_high(&self, on: bool);
895 }
896 }
897}
898
899struct NoOp;
900
901impl NoOp {
902 fn set_input_enable(&self, _on: bool) {}
903 fn set_output_enable(&self, _on: bool) {}
904 fn set_output_high(&self, _on: bool) {}
905 fn apply_input_config(&self, _config: &gpio::InputConfig) {}
906 fn apply_output_config(&self, _config: &gpio::OutputConfig) {}
907
908 fn input_signals(
909 &self,
910 _: private::Internal,
911 ) -> &'static [(AlternateFunction, gpio::InputSignal)] {
912 &[]
913 }
914
915 fn output_signals(
916 &self,
917 _: private::Internal,
918 ) -> &'static [(AlternateFunction, gpio::OutputSignal)] {
919 &[]
920 }
921}
922
923#[procmacros::doc_replace]
924/// ```rust,compile_fail
925/// // Regression test for <https://github.com/esp-rs/esp-hal/issues/3313>
926/// // This test case is expected to generate the following error:
927/// // error[E0277]: the trait bound `Output<'_>: PeripheralInput<'_>` is not satisfied
928/// // --> src\gpio\interconnect.rs:977:5
929/// // |
930/// // 31 | function_expects_input(
931/// // | ---------------------- required by a bound introduced by this call
932/// // 32 | / Output::new(peripherals.GPIO0,
933/// // 33 | | Level::Low,
934/// // 34 | | Default::default()),
935/// // | |_______________________^ the trait `InputPin` is not implemented for `Output<'_>`
936/// // FIXME: due to <https://github.com/rust-lang/rust/issues/139924> this test may be ineffective.
937/// // It can be manually verified by changing it to `no_run` for a `run-doc-tests` run.
938/// # {before_snippet}
939/// use esp_hal::gpio::{Output, Level, interconnect::PeripheralInput};
940///
941/// fn function_expects_input<'d>(_: impl PeripheralInput<'d>) {}
942///
943/// function_expects_input(
944/// Output::new(peripherals.GPIO0,
945/// Level::Low,
946/// Default::default()),
947/// );
948///
949/// # {after_snippet}
950/// ```
951fn _compile_tests() {}