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