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