#![cfg_attr(docsrs, procmacros::doc_replace(
"etm_availability" => {
cfg(etm_driver_supported) => "The GPIO pins also provide tasks and events via the ETM interconnect system. For more information, see the [etm] module."
}
))]
crate::unstable_module! {
pub mod interconnect;
#[cfg(etm_driver_supported)]
pub mod etm;
#[cfg(lp_io_driver_supported)]
pub mod lp_io;
#[cfg(dedicated_gpio_driver_supported)]
pub mod dedicated;
}
use interconnect::PeripheralOutput;
mod asynch;
mod embedded_hal_impls;
pub(crate) mod interrupt;
mod low_level;
use interrupt::*;
pub use low_level::GpioBank;
use low_level::{gpio_intr_enable, is_int_enabled, set_int_enable};
mod placeholder;
#[cfg(sleep_driver_supported)]
pub(crate) mod wakeup;
use core::fmt::Display;
use esp_sync::RawMutex;
pub use placeholder::NoPin;
#[cfg(sleep_driver_supported)]
#[instability::unstable]
pub use wakeup::WakeupConfig;
use crate::{
asynch::AtomicWaker,
interrupt::{InterruptHandler, Priority},
peripherals::{GPIO, IO_MUX},
private::{self, Sealed},
};
define_io_mux_signals!();
pub(crate) static GPIO_LOCK: RawMutex = RawMutex::new();
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PinGuard {
pin: u8,
}
impl crate::private::Sealed for PinGuard {}
impl PinGuard {
fn new(pin: AnyPin<'_>) -> Self {
Self { pin: pin.number() }
}
pub(crate) const fn new_unconnected() -> Self {
Self { pin: u8::MAX }
}
#[allow(unused)]
pub(crate) fn pin_number(&self) -> Option<u8> {
if self.pin == u8::MAX {
None
} else {
Some(self.pin)
}
}
}
impl Drop for PinGuard {
fn drop(&mut self) {
if self.pin != u8::MAX {
let pin = unsafe { AnyPin::steal(self.pin) };
pin.disconnect_from_peripheral_output();
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum Event {
RisingEdge = 1,
FallingEdge = 2,
AnyEdge = 3,
LowLevel = 4,
HighLevel = 5,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Level {
Low,
High,
}
impl Sealed for Level {}
impl core::ops::Not for Level {
type Output = Self;
fn not(self) -> Self {
match self {
Self::Low => Self::High,
Self::High => Self::Low,
}
}
}
impl Level {
pub(crate) const fn const_from(val: bool) -> Self {
match val {
true => Self::High,
false => Self::Low,
}
}
pub(crate) const fn const_into(self) -> bool {
match self {
Level::Low => false,
Level::High => true,
}
}
}
impl From<bool> for Level {
fn from(val: bool) -> Self {
Self::const_from(val)
}
}
impl From<Level> for bool {
fn from(level: Level) -> bool {
level.const_into()
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
#[non_exhaustive]
pub enum WakeConfigError {
NoLowPowerPath,
}
impl Display for WakeConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
WakeConfigError::NoLowPowerPath => {
write!(f, "The pad is not a low-power pad")
}
}
}
}
impl core::error::Error for WakeConfigError {}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
None,
Up,
Down,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveStrength {
_5mA = 0,
_10mA = 1,
_20mA = 2,
_40mA = 3,
}
#[doc(hidden)]
#[doc = crate::trm_markdown_link!("iomuxgpio")]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AlternateFunction {
_0 = 0,
_1 = 1,
_2 = 2,
_3 = 3,
_4 = 4,
_5 = 5,
}
impl AlternateFunction {
pub(crate) const GPIO: Self = match Self::const_try_from(property!("gpio.gpio_function")) {
Ok(func) => func,
Err(_) => ::core::panic!("Invalid GPIO function"),
};
const fn const_try_from(value: usize) -> Result<Self, ()> {
match value {
0 => Ok(Self::_0),
1 => Ok(Self::_1),
2 => Ok(Self::_2),
3 => Ok(Self::_3),
4 => Ok(Self::_4),
5 => Ok(Self::_5),
_ => Err(()),
}
}
}
impl TryFrom<usize> for AlternateFunction {
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::const_try_from(value)
}
}
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub trait LpPin: Pin {
fn lp_number(&self) -> u8;
}
pub trait Pin: Sealed {
fn number(&self) -> u8;
#[procmacros::doc_replace]
fn degrade<'d>(self) -> AnyPin<'d>
where
Self: Sized + 'd,
{
unsafe { AnyPin::steal(self.number()) }
}
#[doc(hidden)]
fn output_signals(&self, _: private::Internal) -> &'static [(AlternateFunction, OutputSignal)];
#[doc(hidden)]
fn input_signals(&self, _: private::Internal) -> &'static [(AlternateFunction, InputSignal)];
}
pub trait InputPin: Pin {
#[doc(hidden)]
fn waker(&self) -> &'static AtomicWaker;
}
pub trait OutputPin: Pin {}
#[instability::unstable]
pub trait AnalogPin: Pin {
#[doc(hidden)]
fn set_analog(&self, _: private::Internal);
}
#[cfg(touch_driver_supported)]
#[instability::unstable]
pub trait TouchPin: Pin {
#[doc(hidden)]
fn set_touch(&self, _: private::Internal);
#[doc(hidden)]
fn touch_measurement(&self, _: private::Internal) -> u16;
#[doc(hidden)]
fn touch_nr(&self, _: private::Internal) -> u8;
#[doc(hidden)]
fn set_threshold(&self, threshold: u16, _: private::Internal);
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AnyPin<'lt> {
pub(crate) pin: u8,
pub(crate) _lifetime: core::marker::PhantomData<&'lt mut ()>,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct Io<'d> {
_io_mux: IO_MUX<'d>,
}
impl<'d> Io<'d> {
#[instability::unstable]
pub fn new(_io_mux: IO_MUX<'d>) -> Self {
Io { _io_mux }
}
#[doc = cfg_select!{
any(single_core, esp32s3) => "Sets the interrupt priority and enables GPIO interrupts.",
_ => "Sets the interrupt priority and enables GPIO interrupts on all cores.",
}]
#[instability::unstable]
pub fn set_interrupt_priority(&self, prio: Priority) {
low_level::set_interrupt_priority(prio);
}
#[doc = cfg_select!{
any(single_core, esp32s3) => "Registers an interrupt handler for all GPIO pins.",
_ => "Registers an interrupt handler for all GPIO pins. Enables interrupts on all cores.",
}]
#[doc = ""]
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
USER_INTERRUPT_HANDLER.store(handler.handler().callback());
low_level::enable_interrupt(InterruptHandler::new(
user_gpio_interrupt_handler,
handler.priority(),
));
}
}
impl crate::private::Sealed for Io<'_> {}
#[instability::unstable]
impl crate::interrupt::InterruptConfigurable for Io<'_> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.set_interrupt_handler(handler);
}
}
#[instability::unstable]
pub unsafe fn handle_gpio_interrupt() {
unsafe { interrupt::handle_gpio_interrupt_impl() }
}
#[instability::unstable]
pub unsafe fn wake_pin(pin: u8) {
unsafe { interrupt::wake_pin_impl(pin) }
}
for_each_analog_function! {
(($_ch:ident, ADCn_CHm, $_n:literal, $_m:literal), $gpio:ident) => {
#[instability::unstable]
impl $crate::gpio::AnalogPin for crate::peripherals::$gpio<'_> {
fn set_analog(&self, _: private::Internal) {
cfg_select! {
xtensa => { self.set_analog_impl(); }
riscv => {
io_mux_reg(self.number()).modify(|_, w| unsafe {
w.mcu_sel().bits(AlternateFunction::GPIO as u8);
w.fun_ie().clear_bit();
w.fun_wpu().clear_bit();
w.fun_wpd().clear_bit()
});
GPIO::regs()
.enable_w1tc()
.write(|w| unsafe { w.bits(1 << self.number()) });
}
}
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveMode {
PushPull,
#[cfg_attr(
feature = "unstable",
doc = "\n\nEnable the input related functionality by using [Output::into_flex] and enabling input via [Flex::set_input_enable]"
)]
OpenDrain,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)]
#[non_exhaustive]
pub struct OutputConfig {
drive_mode: DriveMode,
drive_strength: DriveStrength,
pull: Pull,
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
drive_mode: DriveMode::PushPull,
drive_strength: DriveStrength::_20mA,
pull: Pull::None,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Output<'d> {
pin: Flex<'d>,
}
impl private::Sealed for Output<'_> {}
impl private::Sealed for &mut Output<'_> {}
impl<'d> Output<'d> {
#[procmacros::doc_replace]
#[inline]
pub fn new(pin: impl OutputPin + 'd, initial_level: Level, config: OutputConfig) -> Self {
let mut this = Self {
pin: Flex::new(pin),
};
this.set_level(initial_level);
this.apply_config(&config);
this.pin.pin.set_output_enable(true);
this
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal<'d> {
self.pin.into_peripheral_output()
}
#[procmacros::doc_replace]
#[inline]
pub fn apply_config(&mut self, config: &OutputConfig) {
self.pin.apply_output_config(config)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_high(&mut self) {
self.set_level(Level::High)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_low(&mut self) {
self.set_level(Level::Low)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_level(&mut self, level: Level) {
self.pin.set_level(level)
}
#[procmacros::doc_replace]
#[inline]
pub fn is_set_high(&self) -> bool {
self.output_level() == Level::High
}
#[procmacros::doc_replace]
#[inline]
pub fn is_set_low(&self) -> bool {
self.output_level() == Level::Low
}
#[procmacros::doc_replace]
#[inline]
pub fn output_level(&self) -> Level {
self.pin.output_level()
}
#[procmacros::doc_replace]
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle();
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn set_pad_hold(&mut self, enable: bool) {
self.pin.set_pad_hold(enable);
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn is_pad_held(&self) -> bool {
self.pin.is_pad_held()
}
#[inline]
#[instability::unstable]
pub fn into_flex(self) -> Flex<'d> {
self.pin
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
#[cfg(gpio_has_input_sync)]
pub enum InputSync {
#[default]
Disabled = 0,
NegativeEdge = 1,
PositiveEdge = 2,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)]
#[non_exhaustive]
pub struct InputConfig {
pull: Pull,
#[cfg(gpio_has_input_sync)]
#[builder_lite(unstable)]
input_sync_stage1: InputSync,
#[cfg(gpio_has_input_sync)]
#[builder_lite(unstable)]
input_sync_stage2: InputSync,
}
impl Default for InputConfig {
fn default() -> Self {
Self {
pull: Pull::None,
#[cfg(gpio_has_input_sync)]
input_sync_stage1: Default::default(),
#[cfg(gpio_has_input_sync)]
input_sync_stage2: Default::default(),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Input<'d> {
pin: Flex<'d>,
}
impl private::Sealed for Input<'_> {}
impl private::Sealed for &mut Input<'_> {}
impl<'d> Input<'d> {
#[procmacros::doc_replace]
#[inline]
pub fn new(pin: impl InputPin + 'd, config: InputConfig) -> Self {
let mut pin = Flex::new(pin);
pin.set_output_enable(false);
pin.set_input_enable(true);
pin.apply_input_config(&config);
Self { pin }
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
self.pin.peripheral_input()
}
#[procmacros::doc_replace]
#[inline]
pub fn is_high(&self) -> bool {
self.level() == Level::High
}
#[procmacros::doc_replace]
#[inline]
pub fn is_low(&self) -> bool {
self.level() == Level::Low
}
#[procmacros::doc_replace]
#[inline]
pub fn level(&self) -> Level {
self.pin.level()
}
#[procmacros::doc_replace]
pub fn apply_config(&mut self, config: &InputConfig) {
self.pin.apply_input_config(config)
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn listen(&mut self, event: Event) {
self.pin.listen(event);
}
#[inline]
#[instability::unstable]
pub fn unlisten(&mut self) {
self.pin.unlisten();
}
#[inline]
#[instability::unstable]
pub fn clear_interrupt(&mut self) {
self.pin.clear_interrupt();
}
#[inline]
#[instability::unstable]
pub fn is_interrupt_set(&self) -> bool {
self.pin.is_interrupt_set()
}
#[cfg(sleep_driver_supported)]
#[instability::unstable]
#[inline]
pub fn apply_wakeup_config(&mut self, config: &WakeupConfig) -> Result<(), WakeConfigError> {
self.pin.apply_wakeup_config(config)
}
#[cfg(sleep_driver_supported)]
#[instability::unstable]
#[inline]
pub fn caused_wakeup(&self) -> bool {
self.pin.caused_wakeup()
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn set_pad_hold(&mut self, enable: bool) {
self.pin.set_pad_hold(enable);
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn is_pad_held(&self) -> bool {
self.pin.is_pad_held()
}
#[inline]
#[instability::unstable]
pub fn into_flex(self) -> Flex<'d> {
self.pin
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct Flex<'d> {
pin: AnyPin<'d>,
}
impl private::Sealed for Flex<'_> {}
impl private::Sealed for &mut Flex<'_> {}
impl<'d> Flex<'d> {
#[inline]
#[instability::unstable]
pub fn new(pin: impl Pin + 'd) -> Self {
let pin = pin.degrade();
pin.init_gpio();
Self { pin }
}
#[inline]
#[instability::unstable]
pub fn apply_input_config(&mut self, config: &InputConfig) {
self.pin.apply_input_config(config);
}
#[inline]
#[instability::unstable]
pub fn set_input_enable(&mut self, enable_input: bool) {
self.pin.set_input_enable(enable_input);
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn set_pad_hold(&mut self, enable: bool) {
self.pin.set_pad_hold(enable);
}
#[inline]
#[instability::unstable]
#[cfg(lp_io_driver_supported)]
pub fn is_pad_held(&self) -> bool {
self.pin.is_pad_held()
}
#[inline]
#[instability::unstable]
pub fn is_high(&self) -> bool {
self.level() == Level::High
}
#[inline]
#[instability::unstable]
pub fn is_low(&self) -> bool {
self.level() == Level::Low
}
#[inline]
#[instability::unstable]
pub fn level(&self) -> Level {
self.pin.is_input_high().into()
}
#[inline]
#[instability::unstable]
pub fn listen(&mut self, event: Event) {
self.pin.listen(event);
}
#[inline]
#[instability::unstable]
pub fn unlisten(&mut self) {
GPIO_LOCK.lock(|| {
set_int_enable(self.pin.number(), Some(0), 0, false);
});
}
fn unlisten_and_clear(&mut self) {
GPIO_LOCK.lock(|| {
set_int_enable(self.pin.number(), Some(0), 0, false);
self.clear_interrupt();
});
}
#[inline]
#[instability::unstable]
pub fn is_listening(&self) -> bool {
is_int_enabled(self.pin.number())
}
#[inline]
#[instability::unstable]
pub fn clear_interrupt(&mut self) {
self.pin
.bank()
.write_interrupt_status_clear(self.pin.mask());
}
#[inline]
#[instability::unstable]
pub fn is_interrupt_set(&self) -> bool {
self.pin.bank().read_interrupt_status() & self.pin.mask() != 0
}
#[cfg(sleep_driver_supported)]
#[inline]
#[instability::unstable]
pub fn apply_wakeup_config(&mut self, config: &WakeupConfig) -> Result<(), WakeConfigError> {
wakeup::apply_config(&self.pin, config)
}
#[cfg(sleep_driver_supported)]
#[inline]
#[instability::unstable]
pub fn caused_wakeup(&self) -> bool {
wakeup::caused_wakeup(&self.pin)
}
#[inline]
#[instability::unstable]
pub fn apply_output_config(&mut self, config: &OutputConfig) {
self.pin.apply_output_config(config);
}
#[inline]
#[instability::unstable]
pub fn set_output_enable(&mut self, enable_output: bool) {
self.pin.set_output_enable(enable_output);
}
#[inline]
#[instability::unstable]
pub fn set_high(&mut self) {
self.set_level(Level::High)
}
#[inline]
#[instability::unstable]
pub fn set_low(&mut self) {
self.set_level(Level::Low)
}
#[inline]
#[instability::unstable]
pub fn set_level(&mut self, level: Level) {
self.pin.set_output_high(level.into());
}
#[inline]
#[instability::unstable]
pub fn is_set_high(&self) -> bool {
self.output_level() == Level::High
}
#[inline]
#[instability::unstable]
pub fn is_set_low(&self) -> bool {
self.output_level() == Level::Low
}
#[inline]
#[instability::unstable]
pub fn output_level(&self) -> Level {
self.pin.is_set_high().into()
}
#[inline]
#[instability::unstable]
pub fn toggle(&mut self) {
let level = self.output_level();
self.set_level(!level);
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
self.pin.set_input_enable(true);
unsafe {
self.pin.clone_unchecked().split_no_init().0.freeze()
}
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn split(
self,
) -> (
interconnect::InputSignal<'d>,
interconnect::OutputSignal<'d>,
) {
let input = self.peripheral_input();
let output = self.into_peripheral_output();
(input, output)
}
#[inline]
#[instability::unstable]
pub unsafe fn split_into_drivers(self) -> (Input<'d>, Output<'d>) {
self.pin.set_input_enable(true);
let input = Input {
pin: Flex {
pin: unsafe { self.pin.clone_unchecked() },
},
};
let output = Output { pin: self };
(input, output)
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal<'d> {
unsafe {
self.pin.split_no_init().1.freeze()
}
}
}
impl private::Sealed for AnyPin<'_> {}
impl<'lt> AnyPin<'lt> {
fn bank(&self) -> GpioBank {
low_level::bank(self.number())
}
pub(crate) fn disable_usb_pads(&self) {
#[cfg(soc_has_usb_device)]
{
fn disable_usb_pads(_gpionum: u8) {
crate::peripherals::USB_DEVICE::regs()
.conf0()
.modify(|_, w| {
w.usb_pad_enable().clear_bit();
w.dm_pullup().clear_bit();
w.dm_pulldown().clear_bit();
w.dp_pullup().clear_bit();
w.dp_pulldown().clear_bit()
});
}
#[cfg(esp32p4)]
fn disable_usb_fs_pads(_gpionum: u8) {
crate::peripherals::USB_WRAP::regs()
.otg_conf()
.modify(|_, w| {
w.pad_pull_override().set_bit();
w.dm_pullup().clear_bit();
w.dm_pulldown().clear_bit();
w.dp_pullup().clear_bit();
w.dp_pulldown().clear_bit()
});
}
#[cfg(soc_has_usb_fs)]
macro_rules! disable_usb_fs_pads {
($gpio:ident) => {
if self.number() == crate::peripherals::$gpio::NUMBER {
cfg_select! {
esp32p4 => {
disable_usb_fs_pads(crate::peripherals::$gpio::NUMBER);
}
_ => {
disable_usb_pads(crate::peripherals::$gpio::NUMBER);
}
}
}
};
}
for_each_analog_function! {
(USJ_DM, $gpio:ident) => {
if self.number() == crate::peripherals::$gpio::NUMBER {
disable_usb_pads(crate::peripherals::$gpio::NUMBER);
}
};
(USJ_DP, $gpio:ident) => {
if self.number() == crate::peripherals::$gpio::NUMBER {
disable_usb_pads(crate::peripherals::$gpio::NUMBER);
}
};
(USB_FS_DM, $gpio:ident) => { disable_usb_fs_pads!($gpio) };
(USB_FS_DP, $gpio:ident) => { disable_usb_fs_pads!($gpio) };
}
}
}
#[cfg(lp_io_driver_supported)]
pub(crate) fn set_pad_hold(&self, enable: bool) {
GPIO_LOCK.lock(|| {
if let Some(lp) = lp_io::lp_number(self.number()) {
lp_io::low_level::pad_hold(lp, enable);
} else {
lp_io::low_level::digital_pad_hold(self.number(), enable);
}
})
}
#[cfg(lp_io_driver_supported)]
pub(crate) fn is_pad_held(&self) -> bool {
if let Some(lp) = lp_io::lp_number(self.number()) {
lp_io::low_level::is_pad_held(lp)
} else {
lp_io::low_level::is_digital_pad_held(self.number())
}
}
#[inline]
pub(crate) fn init_gpio(&self) {
self.set_output_enable(false);
self.disable_usb_pads();
#[cfg(lp_io_driver_supported)]
self.set_pad_hold(false);
#[cfg(lp_io_driver_supported)]
for_each_lp_function! {
(($_signal:ident, LP_GPIOn, $lp_pin:literal), $gpio:ident, $af:ident, $_lp_in:tt $_lp_out:tt) => {
if self.number() == crate::peripherals::$gpio::NUMBER {
lp_io::low_level::set_config($lp_pin, false, false, lp_io::LpFunction::$af);
}
};
}
GPIO::regs()
.func_out_sel_cfg(self.number() as usize)
.write(|w| unsafe { w.out_sel().bits(OutputSignal::GPIO as _) });
io_mux_reg(self.number()).modify(|_, w| unsafe {
w.mcu_sel().bits(AlternateFunction::GPIO as u8);
w.fun_ie().clear_bit();
w.slp_sel().clear_bit()
});
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub unsafe fn split(
self,
) -> (
interconnect::InputSignal<'lt>,
interconnect::OutputSignal<'lt>,
) {
assert!(self.is_output());
self.init_gpio();
self.set_input_enable(true);
let (input, output) = unsafe { self.split_no_init() };
let output = output.with_gpio_matrix_forced(true);
(input, output)
}
#[inline]
#[instability::unstable]
pub unsafe fn into_input_signal(self) -> interconnect::InputSignal<'lt> {
self.init_gpio();
self.set_input_enable(true);
let (input, _) = unsafe { self.split_no_init() };
input
}
#[inline]
#[instability::unstable]
pub fn into_output_signal(self) -> interconnect::OutputSignal<'lt> {
assert!(self.is_output());
self.init_gpio();
let (_, output) = unsafe { self.split_no_init() };
output
}
unsafe fn split_no_init(
self,
) -> (
interconnect::InputSignal<'lt>,
interconnect::OutputSignal<'lt>,
) {
let input = interconnect::InputSignal::new(unsafe { self.clone_unchecked() });
let output = interconnect::OutputSignal::new(self);
let input = input.with_gpio_matrix_forced(true);
(input, output)
}
#[inline]
pub(crate) fn set_alternate_function(&self, alternate: AlternateFunction) {
io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) });
}
#[inline]
pub(crate) fn set_output_enable(&self, enable: bool) {
assert!(self.is_output() || !enable);
self.bank().write_out_en(self.mask(), enable);
}
#[inline]
pub(crate) fn set_input_enable(&self, on: bool) {
io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on));
}
#[inline]
pub(crate) fn apply_input_config(&self, config: &InputConfig) {
let pull_up = config.pull == Pull::Up;
let pull_down = config.pull == Pull::Down;
low_level::prepare_pin_pull(self, pull_up, pull_down);
io_mux_reg(self.number()).modify(|_, w| {
w.fun_wpd().bit(pull_down);
w.fun_wpu().bit(pull_up)
});
#[cfg(gpio_has_input_sync)]
self.with_gpio_lock(|| {
GPIO::regs()
.pin(self.number() as usize)
.modify(|_, w| unsafe {
w.sync1_bypass().bits(config.input_sync_stage1 as u8);
w.sync2_bypass().bits(config.input_sync_stage2 as u8)
});
});
}
fn clear_interrupt(&self) {
self.bank().write_interrupt_status_clear(self.mask());
}
fn with_gpio_lock<F, R>(&self, f: F) -> R
where
F: FnOnce() -> R,
{
if is_int_enabled(self.number()) {
GPIO_LOCK.lock(f)
} else {
f()
}
}
fn listen(&self, event: Event) {
self.with_gpio_lock(|| {
self.clear_interrupt();
set_int_enable(
self.number(),
Some(gpio_intr_enable(true)),
event as u8,
true,
);
});
#[cfg(sleep_driver_supported)]
wakeup::enable();
}
#[inline]
fn apply_output_config(&self, config: &OutputConfig) {
let pull_up = config.pull == Pull::Up;
let pull_down = config.pull == Pull::Down;
low_level::prepare_pin_pull(self, pull_up, pull_down);
io_mux_reg(self.number()).modify(|_, w| {
unsafe { w.fun_drv().bits(config.drive_strength as u8) };
w.fun_wpu().bit(pull_up);
w.fun_wpd().bit(pull_down);
w
});
self.with_gpio_lock(|| {
GPIO::regs().pin(self.number() as usize).modify(|_, w| {
w.pad_driver()
.bit(config.drive_mode == DriveMode::OpenDrain)
});
});
}
#[inline]
fn mask(&self) -> u32 {
1 << (self.number() % 32)
}
#[inline]
pub(crate) fn is_input_high(&self) -> bool {
self.bank().read_input() & self.mask() != 0
}
#[inline]
pub(crate) fn set_output_high(&self, high: bool) {
self.bank().write_output(self.mask(), high);
}
#[inline]
pub(crate) fn is_set_high(&self) -> bool {
self.bank().read_output() & self.mask() != 0
}
}
impl Pin for AnyPin<'_> {
#[inline(always)]
fn number(&self) -> u8 {
self.pin
}
fn output_signals(
&self,
private: private::Internal,
) -> &'static [(AlternateFunction, OutputSignal)] {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ($input:tt [$($is_output:ident)?]) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_output);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return Pin::output_signals(&inner, private);
}
)?)*
other => panic!("Pin {} is not an OutputPin", other)
}
};
}
}
fn input_signals(
&self,
private: private::Internal,
) -> &'static [(AlternateFunction, InputSignal)] {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ([$($is_input:ident)?] $output:tt) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_input);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return Pin::input_signals(&inner, private);
}
)?)*
other => panic!("Pin {} is not an InputPin", other)
}
};
}
}
}
impl InputPin for AnyPin<'_> {
fn waker(&self) -> &'static AtomicWaker {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ([$($is_input:ident)?] $output:tt) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_input);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return InputPin::waker(&inner);
}
)?)*
other => panic!("Pin {} is not an InputPin", other)
}
};
}
}
}
impl OutputPin for AnyPin<'_> {}
for_each_gpio! {
($n:literal, $gpio:ident $($_rest:tt)*) => {
impl<'lt> TryFrom<AnyPin<'lt>> for crate::peripherals::$gpio<'lt> {
type Error = AnyPin<'lt>;
fn try_from(any_pin: AnyPin<'lt>) -> Result<Self, Self::Error> {
if any_pin.number() == $n {
Ok(unsafe { Self::steal() })
} else {
Err(any_pin)
}
}
}
};
}
impl AnyPin<'_> {
#[procmacros::doc_replace]
#[inline]
pub fn downcast<P: Pin>(self) -> Result<P, Self>
where
Self: TryInto<P, Error = Self>,
{
self.try_into()
}
#[procmacros::doc_replace]
pub unsafe fn steal(pin: u8) -> Self {
for_each_gpio! {
(all $( ($n:literal $($any:tt)*) ),*) => { const PINS: &[u8] = &[ $($n),* ]; };
};
assert!(PINS.contains(&pin), "Pin {} does not exist", pin);
Self {
pin,
_lifetime: core::marker::PhantomData,
}
}
#[procmacros::doc_replace]
pub unsafe fn clone_unchecked(&self) -> Self {
Self {
pin: self.pin,
_lifetime: core::marker::PhantomData,
}
}
#[procmacros::doc_replace]
pub fn reborrow(&mut self) -> AnyPin<'_> {
unsafe { self.clone_unchecked() }
}
pub(crate) fn is_output(&self) -> bool {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ($input:tt [$($is_output:ident)?]) ) ),* ) => {
return match self.number() {
$($(
$n => {
crate::ignore!($is_output);
true
}
)?)*
other => false,
};
};
}
}
}
for_each_gpio! {
($n:literal, $gpio:ident $af_ins:tt $af_outs:tt ([Input] $output:tt)) => {
impl InputPin for crate::peripherals::$gpio<'_> {
#[doc(hidden)]
#[inline]
fn waker(&self) -> &'static $crate::asynch::AtomicWaker {
static WAKER: $crate::asynch::AtomicWaker = $crate::asynch::AtomicWaker::new();
&WAKER
}
}
};
}
for_each_gpio! {
($n:literal, $gpio:ident $af_ins:tt $af_outs:tt ($input:tt [Output])) => {
impl OutputPin for crate::peripherals::$gpio<'_> {}
};
}
for_each_gpio! {
($n:literal, $gpio:ident ($( $af_input_num:ident => $af_input_signal:ident )*) ($( $af_output_num:ident => $af_output_signal:ident )*) $attrs:tt) => {
impl<'d> crate::peripherals::$gpio<'d> {
#[allow(unused)]
pub(crate) const NUMBER: u8 = $n;
#[procmacros::doc_replace]
#[instability::unstable]
pub unsafe fn split(self) -> (interconnect::InputSignal<'d>, interconnect::OutputSignal<'d>) {
unsafe { self.degrade().split() }
}
}
impl Pin for crate::peripherals::$gpio<'_> {
#[inline(always)]
fn number(&self) -> u8 {
$n
}
fn output_signals(&self, _: crate::private::Internal) -> &'static [(AlternateFunction, OutputSignal)] {
&[$(
(AlternateFunction::$af_output_num, OutputSignal::$af_output_signal),
)*]
}
fn input_signals(&self, _: crate::private::Internal) -> &'static [(AlternateFunction, InputSignal)] {
&[$(
(AlternateFunction::$af_input_num, InputSignal::$af_input_signal),
)*]
}
}
impl<'lt> From<crate::peripherals::$gpio<'lt>> for AnyPin<'lt> {
fn from(pin: crate::peripherals::$gpio<'lt>) -> Self {
Pin::degrade(pin)
}
}
};
}
define_io_mux_reg!();
#[cfg(docsrs)]
mod io_matrix_impls {
use crate::gpio::{
self,
AnyPin,
interconnect::{
InputSignal,
OutputSignal,
PeripheralInput,
PeripheralOutput,
PeripheralSignal,
},
};
impl<'d> PeripheralSignal<'d> for AnyPin<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
let pin = unsafe { self.clone_unchecked() };
InputSignal::new(pin).connect_input_to_peripheral(signal);
}
}
impl<'d> PeripheralInput<'d> for AnyPin<'d> {}
impl<'d> PeripheralOutput<'d> for AnyPin<'d> {
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
let pin = unsafe { self.clone_unchecked() };
OutputSignal::new(pin).connect_peripheral_to_output(signal);
}
fn disconnect_from_peripheral_output(&self) {
let pin = unsafe { self.clone_unchecked() };
OutputSignal::new(pin).disconnect_from_peripheral_output();
}
}
for_each_gpio! {
($n:literal, $gpio:ident $($_rest:tt)*) => {
impl<'d> PeripheralSignal<'d> for crate::peripherals::$gpio<'d> {
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal) {
let pin = unsafe { AnyPin::steal($n) };
pin.connect_input_to_peripheral(signal);
}
}
impl<'d> PeripheralInput<'d> for crate::peripherals::$gpio<'d> {}
};
}
for_each_gpio! {
($n:literal, $gpio:ident $_af_ins:tt $_af_outs:tt ($input:tt [Output])) => {
impl<'d> PeripheralOutput<'d> for crate::peripherals::$gpio<'d>
{
fn connect_peripheral_to_output(&self, signal: gpio::OutputSignal) {
let pin = unsafe { AnyPin::steal($n) };
pin.connect_peripheral_to_output(signal);
}
fn disconnect_from_peripheral_output(&self) {
let pin = unsafe { AnyPin::steal($n) };
pin.disconnect_from_peripheral_output();
}
}
};
}
}
#[cfg(not(docsrs))]
mod io_matrix_impls {
use crate::gpio::{
self,
AnyPin,
InputPin,
OutputPin,
Pin,
interconnect::{
InputSignal,
OutputSignal,
PeripheralInput,
PeripheralOutput,
PeripheralSignal,
},
};
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();
}
}
}