#![doc = concat!("## Relation to the ", crate::trm_markdown_link!("iomuxgpio"))]
# RX line, you will need to make sure
one of the signals is frozen, otherwise the driver that is configured later
will overwrite the other driver's configuration. Configuring the signals on
multiple cores is undefined behaviour unless you ensure the configuration
does not happen at the same time."
)]
#![cfg_attr(spi_master_driver_supported, doc = "[`Spi::with_mosi`].")]
#![cfg_attr(not(spi_master_driver_supported), doc = "`Spi::with_mosi`.")]
#![cfg_attr(
spi_master_driver_supported,
doc = "[`Spi::with_mosi`]: crate::spi::master::Spi::with_mosi"
)]
#[cfg(feature = "unstable")]
use crate::gpio::{Input, Output};
use crate::{
gpio::{
self,
AlternateFunction,
AnyPin,
Flex,
InputPin,
Level,
NoPin,
OutputPin,
Pin,
PinGuard,
},
peripherals::GPIO,
private::{self, Sealed},
};
pub trait PeripheralSignal<'d>: Sealed {
#[doc(hidden)] fn connect_input_to_peripheral(&self, signal: gpio::InputSignal);
}
#[allow(
private_bounds,
reason = "InputSignal is unstable, but the trait needs to be public"
)]
pub trait PeripheralInput<'d>: Into<InputSignal<'d>> + PeripheralSignal<'d> {}
#[allow(
private_bounds,
reason = "OutputSignal is unstable, but the trait needs to be public"
)]
pub trait PeripheralOutput<'d>: Into<OutputSignal<'d>> + PeripheralSignal<'d> {
#[doc(hidden)] fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal);
#[doc(hidden)] fn disconnect_from_peripheral_output(&self);
}
impl<'d, P> PeripheralSignal<'d> for P
where
P: Pin + 'd,
{
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
let pin = unsafe { AnyPin::steal(self.number()) };
InputSignal::new(pin).connect_input_to_peripheral(signal);
}
}
impl<'d, P> PeripheralInput<'d> for P where P: InputPin + 'd {}
impl<'d, P> PeripheralOutput<'d> for P
where
P: OutputPin + 'd,
{
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
let pin = unsafe { AnyPin::steal(self.number()) };
OutputSignal::new(pin).connect_peripheral_to_output(signal);
}
fn disconnect_from_peripheral_output(&self) {
let pin = unsafe { AnyPin::steal(self.number()) };
OutputSignal::new(pin).disconnect_from_peripheral_output();
}
}
#[instability::unstable]
impl<'d> PeripheralSignal<'d> for Flex<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
self.pin.connect_input_to_peripheral(signal);
}
}
#[instability::unstable]
impl<'d> PeripheralInput<'d> for Flex<'d> {}
#[instability::unstable]
impl<'d> PeripheralOutput<'d> for Flex<'d> {
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
self.pin.connect_peripheral_to_output(signal);
}
fn disconnect_from_peripheral_output(&self) {
self.pin.disconnect_from_peripheral_output();
}
}
#[instability::unstable]
impl<'d> PeripheralSignal<'d> for Input<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
self.pin.connect_input_to_peripheral(signal);
}
}
#[instability::unstable]
impl<'d> PeripheralInput<'d> for Input<'d> {}
#[instability::unstable]
impl<'d> PeripheralSignal<'d> for Output<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
self.pin.connect_input_to_peripheral(signal);
}
}
#[instability::unstable]
impl<'d> PeripheralOutput<'d> for Output<'d> {
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
self.pin.connect_peripheral_to_output(signal);
}
fn disconnect_from_peripheral_output(&self) {
self.pin.disconnect_from_peripheral_output();
}
}
impl PeripheralSignal<'_> for NoPin {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
Level::Low.connect_input_to_peripheral(signal);
}
}
impl PeripheralInput<'_> for NoPin {}
impl PeripheralOutput<'_> for NoPin {
fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
}
fn disconnect_from_peripheral_output(&self) {
}
}
impl PeripheralSignal<'_> for Level {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
Signal::Level(*self).connect_to_peripheral_input(signal, false, true);
}
}
impl PeripheralInput<'_> for Level {}
impl PeripheralOutput<'_> for Level {
fn connect_peripheral_to_output(&self, _: gpio::OutputSignal) {
}
fn disconnect_from_peripheral_output(&self) {
}
}
impl<'d> PeripheralSignal<'d> for InputSignal<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
self.pin.connect_to_peripheral_input(
signal,
self.is_input_inverted(),
self.is_gpio_matrix_forced(),
);
}
}
impl<'d> PeripheralInput<'d> for InputSignal<'d> {}
impl<'d> PeripheralSignal<'d> for OutputSignal<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
self.pin.connect_to_peripheral_input(
signal,
self.is_input_inverted(),
self.is_gpio_matrix_forced(),
);
}
}
impl<'d> PeripheralOutput<'d> for OutputSignal<'d> {
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
self.pin.connect_peripheral_to_output(
signal,
self.is_output_inverted(),
self.is_gpio_matrix_forced(),
true,
false,
);
}
fn disconnect_from_peripheral_output(&self) {
self.pin.disconnect_from_peripheral_output();
}
}
impl gpio::InputSignal {
fn can_use_gpio_matrix(self) -> bool {
self as usize <= property!("gpio.input_signal_max")
}
#[inline]
#[instability::unstable]
pub fn connect_to<'a>(self, pin: &impl PeripheralSignal<'a>) {
pin.connect_input_to_peripheral(self);
}
}
impl gpio::OutputSignal {
fn can_use_gpio_matrix(self) -> bool {
self as usize <= property!("gpio.output_signal_max")
}
#[inline]
#[instability::unstable]
pub fn connect_to<'d>(self, pin: &impl PeripheralOutput<'d>) {
pin.connect_peripheral_to_output(self);
}
#[inline]
#[instability::unstable]
pub fn disconnect_from<'d>(self, pin: &impl PeripheralOutput<'d>) {
pin.disconnect_from_peripheral_output();
}
}
enum Signal<'d> {
Pin(AnyPin<'d>),
Level(Level),
}
impl Signal<'_> {
fn gpio_number(&self) -> Option<u8> {
match &self {
Signal::Pin(pin) => Some(pin.number()),
Signal::Level(_) => None,
}
}
unsafe fn clone_unchecked(&self) -> Self {
match self {
Signal::Pin(pin) => Signal::Pin(unsafe { pin.clone_unchecked() }),
Signal::Level(level) => Signal::Level(*level),
}
}
fn is_set_high(&self) -> bool {
match &self {
Signal::Pin(signal) => signal.is_set_high(),
Signal::Level(level) => *level == Level::High,
}
}
fn is_input_high(&self) -> bool {
match &self {
Signal::Pin(signal) => signal.is_input_high(),
Signal::Level(level) => *level == Level::High,
}
}
fn connect_to_peripheral_input(
&self,
signal: gpio::InputSignal,
is_inverted: bool,
force_gpio: bool,
) {
let use_gpio_matrix = match self {
Signal::Pin(pin) => {
let af = if is_inverted || force_gpio {
AlternateFunction::GPIO
} else {
pin.input_signals(private::Internal)
.iter()
.find(|(_af, s)| *s == signal)
.map(|(af, _)| *af)
.unwrap_or(AlternateFunction::GPIO)
};
pin.disable_usb_pads();
pin.set_alternate_function(af);
af == AlternateFunction::GPIO
}
Signal::Level(_) => true,
};
if !signal.can_use_gpio_matrix() {
assert!(
!use_gpio_matrix,
"{:?} cannot be routed through the GPIO matrix",
signal
);
return;
}
let input = match self {
Signal::Pin(pin) => pin.number(),
Signal::Level(Level::Low) => property!("gpio.constant_0_input"),
Signal::Level(Level::High) => property!("gpio.constant_1_input"),
};
let offset = property!("gpio.func_in_sel_offset");
GPIO::regs()
.func_in_sel_cfg(signal as usize - offset)
.write(|w| unsafe {
w.sel().bit(use_gpio_matrix);
w.in_inv_sel().bit(is_inverted);
w.in_sel().bits(input)
});
}
fn connect_peripheral_to_output(
&self,
signal: gpio::OutputSignal,
is_inverted: bool,
force_gpio: bool,
peripheral_control_output_enable: bool,
invert_output_enable: bool,
) {
let Signal::Pin(pin) = self else {
return;
};
let af = if is_inverted || force_gpio {
AlternateFunction::GPIO
} else {
pin.output_signals(private::Internal)
.iter()
.find(|(_af, s)| *s == signal)
.map(|(af, _)| *af)
.unwrap_or(AlternateFunction::GPIO)
};
pin.disable_usb_pads();
pin.set_alternate_function(af);
let use_gpio_matrix = af == AlternateFunction::GPIO;
assert!(
signal.can_use_gpio_matrix() || !use_gpio_matrix,
"{:?} cannot be routed through the GPIO matrix",
signal
);
GPIO::regs()
.func_out_sel_cfg(pin.number() as usize)
.write(|w| unsafe {
if use_gpio_matrix {
w.out_sel().bits(signal as _);
w.inv_sel().bit(is_inverted);
}
w.oen_sel().bit(!peripheral_control_output_enable);
w.oen_inv_sel().bit(invert_output_enable)
});
}
fn disconnect_from_peripheral_output(&self) {
let Some(number) = self.gpio_number() else {
return;
};
GPIO::regs()
.func_out_sel_cfg(number as usize)
.modify(|_, w| unsafe { w.out_sel().bits(gpio::OutputSignal::GPIO as _) });
}
}
bitflags::bitflags! {
#[derive(Clone, Copy)]
struct InputFlags: u8 {
const ForceGpioMatrix = 1 << 0;
const Frozen = 1 << 1;
const InvertInput = 1 << 2;
}
}
#[instability::unstable]
pub struct InputSignal<'d> {
pin: Signal<'d>,
flags: InputFlags,
}
impl From<Level> for InputSignal<'_> {
fn from(level: Level) -> Self {
InputSignal::new_level(level)
}
}
impl From<NoPin> for InputSignal<'_> {
fn from(_pin: NoPin) -> Self {
InputSignal::new_level(Level::Low)
}
}
impl<'d, P> From<P> for InputSignal<'d>
where
P: Pin + 'd,
{
fn from(input: P) -> Self {
InputSignal::new(input.degrade())
}
}
impl<'d> From<Flex<'d>> for InputSignal<'d> {
fn from(pin: Flex<'d>) -> Self {
pin.peripheral_input()
}
}
#[instability::unstable]
impl<'d> From<Input<'d>> for InputSignal<'d> {
fn from(pin: Input<'d>) -> Self {
pin.pin.into()
}
}
impl Sealed for InputSignal<'_> {}
impl Clone for InputSignal<'_> {
fn clone(&self) -> Self {
Self {
pin: unsafe { self.pin.clone_unchecked() },
flags: self.flags,
}
}
}
impl<'d> InputSignal<'d> {
fn new_inner(inner: Signal<'d>) -> Self {
Self {
pin: inner,
flags: InputFlags::empty(),
}
}
pub(crate) fn new(pin: AnyPin<'d>) -> Self {
Self::new_inner(Signal::Pin(pin))
}
pub(crate) fn new_level(level: Level) -> Self {
Self::new_inner(Signal::Level(level))
}
pub fn freeze(mut self) -> Self {
self.flags.insert(InputFlags::Frozen);
self
}
pub unsafe fn unfreeze(&mut self) {
self.flags.remove(InputFlags::Frozen);
}
pub fn gpio_number(&self) -> Option<u8> {
self.pin.gpio_number()
}
pub fn is_input_high(&self) -> bool {
self.pin.is_input_high()
}
pub fn level(&self) -> Level {
self.is_input_high().into()
}
pub fn is_input_inverted(&self) -> bool {
self.flags.contains(InputFlags::InvertInput)
}
pub fn with_input_inverter(mut self, invert: bool) -> Self {
self.flags.set(InputFlags::InvertInput, invert);
self
}
pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
self.flags.set(InputFlags::ForceGpioMatrix, force);
self
}
pub fn is_gpio_matrix_forced(&self) -> bool {
self.flags.contains(InputFlags::ForceGpioMatrix)
}
delegate::delegate! {
#[instability::unstable]
#[doc(hidden)]
to match &self.pin {
Signal::Pin(signal) => signal,
Signal::Level(_) => NoOp,
} {
pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
}
}
delegate::delegate! {
#[instability::unstable]
#[doc(hidden)]
to match &self.pin {
Signal::Pin(_) if self.flags.contains(InputFlags::Frozen) => NoOp,
Signal::Pin(signal) => signal,
Signal::Level(_) => NoOp,
} {
pub fn apply_input_config(&self, _config: &gpio::InputConfig);
pub fn set_input_enable(&self, on: bool);
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy)]
struct OutputFlags: u8 {
const ForceGpioMatrix = 1 << 0;
const Frozen = 1 << 1;
const InvertInput = 1 << 2;
const InvertOutput = 1 << 3;
}
}
#[instability::unstable]
pub struct OutputSignal<'d> {
pin: Signal<'d>,
flags: OutputFlags,
}
impl Sealed for OutputSignal<'_> {}
impl From<Level> for OutputSignal<'_> {
fn from(level: Level) -> Self {
OutputSignal::new_level(level)
}
}
impl From<NoPin> for OutputSignal<'_> {
fn from(_pin: NoPin) -> Self {
OutputSignal::new_level(Level::Low)
}
}
impl<'d, P> From<P> for OutputSignal<'d>
where
P: OutputPin + 'd,
{
fn from(output: P) -> Self {
OutputSignal::new(output.degrade())
}
}
impl<'d> From<Flex<'d>> for OutputSignal<'d> {
fn from(pin: Flex<'d>) -> Self {
pin.into_peripheral_output()
}
}
#[instability::unstable]
impl<'d> From<Output<'d>> for OutputSignal<'d> {
fn from(pin: Output<'d>) -> Self {
pin.pin.into()
}
}
impl<'d> OutputSignal<'d> {
fn new_inner(inner: Signal<'d>) -> Self {
Self {
pin: inner,
flags: OutputFlags::empty(),
}
}
pub(crate) fn new(pin: AnyPin<'d>) -> Self {
Self::new_inner(Signal::Pin(pin))
}
pub(crate) fn new_level(level: Level) -> Self {
Self::new_inner(Signal::Level(level))
}
pub fn freeze(mut self) -> Self {
self.flags.insert(OutputFlags::Frozen);
self
}
pub unsafe fn unfreeze(&mut self) {
self.flags.remove(OutputFlags::Frozen);
}
pub fn gpio_number(&self) -> Option<u8> {
self.pin.gpio_number()
}
pub fn is_input_inverted(&self) -> bool {
self.flags.contains(OutputFlags::InvertInput)
}
pub fn is_output_inverted(&self) -> bool {
self.flags.contains(OutputFlags::InvertOutput)
}
pub fn with_output_inverter(mut self, invert: bool) -> Self {
self.flags.set(OutputFlags::InvertOutput, invert);
self
}
pub fn with_input_inverter(mut self, invert: bool) -> Self {
self.flags.set(OutputFlags::InvertInput, invert);
self
}
pub fn with_gpio_matrix_forced(mut self, force: bool) -> Self {
self.flags.set(OutputFlags::ForceGpioMatrix, force);
self
}
pub fn is_gpio_matrix_forced(&self) -> bool {
self.flags.contains(OutputFlags::ForceGpioMatrix)
}
pub fn is_input_high(&self) -> bool {
self.pin.is_input_high()
}
pub fn is_set_high(&self) -> bool {
self.pin.is_set_high()
}
#[doc(hidden)]
#[instability::unstable]
#[cfg_attr(
not(any(
i2c_master_driver_supported,
spi_master_driver_supported,
uart_driver_supported
)),
expect(unused)
)]
pub(crate) fn connect_with_guard(self, signal: crate::gpio::OutputSignal) -> PinGuard {
signal.connect_to(&self);
match self.pin {
Signal::Pin(pin) => PinGuard::new(pin),
Signal::Level(_) => PinGuard::new_unconnected(),
}
}
delegate::delegate! {
#[instability::unstable]
#[doc(hidden)]
to match &self.pin {
Signal::Pin(signal) => signal,
Signal::Level(_) => NoOp,
} {
pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
}
}
delegate::delegate! {
#[instability::unstable]
#[doc(hidden)]
to match &self.pin {
Signal::Pin(_) if self.flags.contains(OutputFlags::Frozen) => NoOp,
Signal::Pin(pin) => pin,
Signal::Level(_) => NoOp,
} {
pub fn apply_input_config(&self, _config: &gpio::InputConfig);
pub fn apply_output_config(&self, _config: &gpio::OutputConfig);
pub fn set_input_enable(&self, on: bool);
pub fn set_output_enable(&self, on: bool);
pub fn set_output_high(&self, on: bool);
}
}
}
struct NoOp;
impl NoOp {
fn set_input_enable(&self, _on: bool) {}
fn set_output_enable(&self, _on: bool) {}
fn set_output_high(&self, _on: bool) {}
fn apply_input_config(&self, _config: &gpio::InputConfig) {}
fn apply_output_config(&self, _config: &gpio::OutputConfig) {}
fn input_signals(
&self,
_: private::Internal,
) -> &'static [(AlternateFunction, gpio::InputSignal)] {
&[]
}
fn output_signals(
&self,
_: private::Internal,
) -> &'static [(AlternateFunction, gpio::OutputSignal)] {
&[]
}
}
#[procmacros::doc_replace]
fn _compile_tests() {}