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