Extract inner modules of gpio module into their own files (#1397)
* Extract inner modules of `gpio` module into their own files * Update `CHANGELOG.md` * Add missing doc comment in `gpio::lp_io` module --------- Co-authored-by: Scott Mabin <scott@mabez.dev>
This commit is contained in:
parent
b3bc28efef
commit
fd9f7895f6
@ -620,7 +620,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
let imports = quote! {
|
||||
use #hal_crate::lp_core::LpCore;
|
||||
use #hal_crate::lp_core::LpCoreWakeupSource;
|
||||
use #hal_crate::gpio::lp_gpio::LowPowerPin;
|
||||
use #hal_crate::gpio::lp_io::LowPowerPin;
|
||||
use #hal_crate::gpio::*;
|
||||
use #hal_crate::uart::lp_uart::LpUart;
|
||||
use #hal_crate::i2c::lp_i2c::LpI2c;
|
||||
|
||||
@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `UsbSerialJtag` can be created in async or blocking mode. The blocking constructor takes an optional interrupt handler argument (#1377)
|
||||
- SYSTIMER and TIMG instances can now be created in async or blocking mode (#1348)
|
||||
- Runtime ISR binding for TWAI (#1384)
|
||||
- ESP32-C6: The `gpio::lp_gpio` module has been renamed to `gpio::lp_io` to match the peripheral name (#1397)
|
||||
- Runtime ISR binding for assist_debug (#1395)
|
||||
- Runtime ISR binding for software interrupts, software interrupts are split now, interrupt-executor takes the software interrupt to use, interrupt-executor is easier to use (#1398)
|
||||
|
||||
|
||||
322
esp-hal/src/gpio/etm.rs
Normal file
322
esp-hal/src/gpio/etm.rs
Normal file
@ -0,0 +1,322 @@
|
||||
//! # Event Task Matrix Function
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! GPIO supports ETM function, that is, the ETM task of GPIO can be
|
||||
//! triggered by the ETM event of any peripheral, or the ETM task of any
|
||||
//! peripheral can be triggered by the ETM event of GPIO.
|
||||
//!
|
||||
//! The GPIO ETM provides eight task channels. The ETM tasks that each task
|
||||
//! channel can receive are:
|
||||
//! - SET: GPIO goes high when triggered
|
||||
//! - CLEAR: GPIO goes low when triggered
|
||||
//! - TOGGLE: GPIO toggle level when triggered.
|
||||
//!
|
||||
//! GPIO has eight event channels, and the ETM events that each event
|
||||
//! channel can generate are:
|
||||
//! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO has
|
||||
//! a rising edge
|
||||
//! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO has
|
||||
//! a falling edge
|
||||
//! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO is
|
||||
//! reversed
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```no_run
|
||||
//! let led_task = gpio_ext.channel0_task.toggle(&mut led);
|
||||
//! let button_event = gpio_ext.channel0_event.falling_edge(button);
|
||||
//! ```
|
||||
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
/// All the GPIO ETM channels
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmChannels<'d> {
|
||||
_gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>,
|
||||
pub channel0_task: GpioEtmTaskChannel<0>,
|
||||
pub channel0_event: GpioEtmEventChannel<0>,
|
||||
pub channel1_task: GpioEtmTaskChannel<1>,
|
||||
pub channel1_event: GpioEtmEventChannel<1>,
|
||||
pub channel2_task: GpioEtmTaskChannel<2>,
|
||||
pub channel2_event: GpioEtmEventChannel<2>,
|
||||
pub channel3_task: GpioEtmTaskChannel<3>,
|
||||
pub channel3_event: GpioEtmEventChannel<3>,
|
||||
pub channel4_task: GpioEtmTaskChannel<4>,
|
||||
pub channel4_event: GpioEtmEventChannel<4>,
|
||||
pub channel5_task: GpioEtmTaskChannel<5>,
|
||||
pub channel5_event: GpioEtmEventChannel<5>,
|
||||
pub channel6_task: GpioEtmTaskChannel<6>,
|
||||
pub channel6_event: GpioEtmEventChannel<6>,
|
||||
pub channel7_task: GpioEtmTaskChannel<7>,
|
||||
pub channel7_event: GpioEtmEventChannel<7>,
|
||||
}
|
||||
|
||||
impl<'d> GpioEtmChannels<'d> {
|
||||
pub fn new(peripheral: impl Peripheral<P = crate::peripherals::GPIO_SD> + 'd) -> Self {
|
||||
crate::into_ref!(peripheral);
|
||||
|
||||
Self {
|
||||
_gpio_sd: peripheral,
|
||||
channel0_task: GpioEtmTaskChannel {},
|
||||
channel0_event: GpioEtmEventChannel {},
|
||||
channel1_task: GpioEtmTaskChannel {},
|
||||
channel1_event: GpioEtmEventChannel {},
|
||||
channel2_task: GpioEtmTaskChannel {},
|
||||
channel2_event: GpioEtmEventChannel {},
|
||||
channel3_task: GpioEtmTaskChannel {},
|
||||
channel3_event: GpioEtmEventChannel {},
|
||||
channel4_task: GpioEtmTaskChannel {},
|
||||
channel4_event: GpioEtmEventChannel {},
|
||||
channel5_task: GpioEtmTaskChannel {},
|
||||
channel5_event: GpioEtmEventChannel {},
|
||||
channel6_task: GpioEtmTaskChannel {},
|
||||
channel6_event: GpioEtmEventChannel {},
|
||||
channel7_task: GpioEtmTaskChannel {},
|
||||
channel7_event: GpioEtmEventChannel {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ETM controlled GPIO event
|
||||
pub struct GpioEtmEventChannel<const C: u8> {}
|
||||
|
||||
impl<const C: u8> GpioEtmEventChannel<C> {
|
||||
/// Trigger at rising edge
|
||||
pub fn rising_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelRising<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelRising { _pin: pin }
|
||||
}
|
||||
|
||||
/// Trigger at falling edge
|
||||
pub fn falling_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelFalling<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelFalling { _pin: pin }
|
||||
}
|
||||
|
||||
/// Trigger at any edge
|
||||
pub fn any_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelAny<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelAny { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for rising edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
1 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for falling edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
9 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for any edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
17 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// An ETM controlled GPIO task
|
||||
pub struct GpioEtmTaskChannel<const C: u8> {}
|
||||
|
||||
impl<const C: u8> GpioEtmTaskChannel<C> {
|
||||
// In theory we could have multiple pins assigned to the same task. Not sure how
|
||||
// useful that would be. If we want to support it, the easiest would be
|
||||
// to offer additional functions like `set2`, `set3` etc. where the
|
||||
// number is the pin-count
|
||||
|
||||
/// Task to set a high level
|
||||
pub fn set<'d, PIN>(self, pin: impl Peripheral<P = PIN> + 'd) -> GpioEtmTaskSet<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskSet { _pin: pin }
|
||||
}
|
||||
|
||||
/// Task to set a low level
|
||||
pub fn clear<'d, PIN>(self, pin: impl Peripheral<P = PIN> + 'd) -> GpioEtmTaskClear<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskClear { _pin: pin }
|
||||
}
|
||||
|
||||
/// Task to toggle the level
|
||||
pub fn toggle<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmTaskToggle<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskToggle { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for set operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskSet<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskSet<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
1 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for clear operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskClear<'d, PIN, const C: u8> {
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskClear<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
9 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for toggle operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> {
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
17 + C
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_task_channel(channel: u8, pin: u8) {
|
||||
let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
|
||||
let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
|
||||
let shift = 8 * (pin as usize % 4);
|
||||
// bit 0 = en, bit 1-3 = channel
|
||||
unsafe {
|
||||
ptr.write_volatile(
|
||||
ptr.read_volatile() & !(0xf << shift) | 1 << shift | (channel as u32) << (shift + 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_event_channel(channel: u8, pin: u8) {
|
||||
let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_en().clear_bit());
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_sel().variant(pin));
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_en().set_bit());
|
||||
}
|
||||
250
esp-hal/src/gpio/lp_io.rs
Normal file
250
esp-hal/src/gpio/lp_io.rs
Normal file
@ -0,0 +1,250 @@
|
||||
//! Low Power IO (LP_IO)
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The hardware provides a couple of GPIO pins with low power (LP)
|
||||
//! capabilities and analog functions. These pins can be controlled by
|
||||
//! either IO MUX or LP IO MUX.
|
||||
//!
|
||||
//! If controlled by LP IO MUX, these pins will bypass IO MUX and GPIO
|
||||
//! matrix for the use by ULP and peripherals in LP system.
|
||||
//!
|
||||
//! When configured as LP GPIOs, the pins can still be controlled by ULP or
|
||||
//! the peripherals in LP system during chip Deep-sleep, and wake up the
|
||||
//! chip from Deep-sleep.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
//! // configure GPIO 1 as LP output pin
|
||||
//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
|
||||
//! ```
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
use super::OpenDrain;
|
||||
use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
|
||||
|
||||
/// A GPIO pin configured for low power operation
|
||||
pub struct LowPowerPin<MODE, const PIN: u8> {
|
||||
pub(crate) private: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE, const PIN: u8> LowPowerPin<MODE, PIN> {
|
||||
#[doc(hidden)]
|
||||
pub fn output_enable(&self, enable: bool) {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
if enable {
|
||||
lp_io
|
||||
.out_enable_w1ts()
|
||||
.write(|w| w.enable_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
lp_io
|
||||
.out_enable_w1tc()
|
||||
.write(|w| w.enable_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
fn input_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
|
||||
}
|
||||
|
||||
fn pullup_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_wpu().bit(enable));
|
||||
}
|
||||
|
||||
fn pulldown_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_wpd().bit(enable));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_level(&mut self, level: bool) {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
if level {
|
||||
lp_io
|
||||
.out_data_w1ts()
|
||||
.write(|w| w.out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
lp_io
|
||||
.out_data_w1tc()
|
||||
.write(|w| w.out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn get_level(&self) -> bool {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
(lp_io.in_().read().data_next().bits() & 1 << PIN) != 0
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-up resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_up_input(self) -> LowPowerPin<Input<PullUp>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(true);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-down resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_down_input(self) -> LowPowerPin<Input<PullDown>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as a floating input pin.
|
||||
pub fn into_floating_input(self) -> LowPowerPin<Input<Floating>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as a push-pull output pin.
|
||||
pub fn into_push_pull_output(self) -> LowPowerPin<Output<PushPull>, PIN> {
|
||||
self.output_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an open-drain output pin.
|
||||
pub fn into_open_drain_output(self) -> LowPowerPin<OpenDrain, PIN> {
|
||||
use crate::peripherals::GPIO;
|
||||
|
||||
let gpio = unsafe { &*GPIO::PTR };
|
||||
|
||||
gpio.pin(PIN as usize)
|
||||
.modify(|_, w| w.pad_driver().bit(true));
|
||||
self.pulldown_enable(false);
|
||||
self.into_pull_up_input().into_push_pull_output();
|
||||
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_low_power_pin(pin: u8) {
|
||||
let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR };
|
||||
|
||||
lp_aon
|
||||
.gpio_mux()
|
||||
.modify(|r, w| w.sel().variant(r.sel().bits() | 1 << pin));
|
||||
|
||||
get_pin_reg(pin).modify(|_, w| w.mcu_sel().variant(0));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::lp_io::GPIO0 {
|
||||
// ideally we should change the SVD and make the GPIOx registers into an
|
||||
// array
|
||||
unsafe {
|
||||
let lp_io = &*crate::peripherals::LP_IO::PTR;
|
||||
let pin_ptr = (lp_io.gpio0().as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr as *const esp32c6::generic::Reg<esp32c6::lp_io::gpio0::GPIO0_SPEC>)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures a pin for use as a low power pin
|
||||
pub trait IntoLowPowerPin<const PIN: u8> {
|
||||
fn into_low_power(self) -> LowPowerPin<Unknown, { PIN }>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! lp_gpio {
|
||||
(
|
||||
$($gpionum:literal)+
|
||||
) => {
|
||||
paste::paste!{
|
||||
$(
|
||||
impl<MODE> $crate::gpio::lp_io::IntoLowPowerPin<$gpionum> for GpioPin<MODE, $gpionum> {
|
||||
fn into_low_power(self) -> $crate::gpio::lp_io::LowPowerPin<Unknown, $gpionum> {
|
||||
$crate::gpio::lp_io::init_low_power_pin($gpionum);
|
||||
$crate::gpio::lp_io::LowPowerPin {
|
||||
private: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $crate::gpio::RTCPin for GpioPin<MODE, $gpionum> {
|
||||
unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) {
|
||||
let lp_io = &*$crate::peripherals::LP_IO::ptr();
|
||||
lp_io.[< pin $gpionum >]().modify(|_, w| {
|
||||
w.wakeup_enable().bit(wakeup).int_type().bits(level)
|
||||
});
|
||||
}
|
||||
|
||||
fn rtcio_pad_hold(&mut self, enable: bool) {
|
||||
let mask = 1 << $gpionum;
|
||||
unsafe {
|
||||
let lp_aon = &*$crate::peripherals::LP_AON::ptr();
|
||||
|
||||
lp_aon.gpio_hold0().modify(|r, w| {
|
||||
if enable {
|
||||
w.gpio_hold0().bits(r.gpio_hold0().bits() | mask)
|
||||
} else {
|
||||
w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the LP properties of the pin. If `mux` is true then then pin is
|
||||
/// routed to LP_IO, when false it is routed to IO_MUX.
|
||||
fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) {
|
||||
let mask = 1 << $gpionum;
|
||||
unsafe {
|
||||
// Select LP_IO
|
||||
let lp_aon = &*$crate::peripherals::LP_AON::ptr();
|
||||
lp_aon
|
||||
.gpio_mux()
|
||||
.modify(|r, w| {
|
||||
if mux {
|
||||
w.sel().bits(r.sel().bits() | mask)
|
||||
} else {
|
||||
w.sel().bits(r.sel().bits() & !mask)
|
||||
}
|
||||
});
|
||||
|
||||
// Configure input, function and select normal operation registers
|
||||
let lp_io = &*$crate::peripherals::LP_IO::ptr();
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| {
|
||||
w
|
||||
.slp_sel().bit(false)
|
||||
.fun_ie().bit(input_enable)
|
||||
.mcu_sel().bits(func as u8)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $crate::gpio::RTCPinWithResistors for GpioPin<MODE, $gpionum> {
|
||||
fn rtcio_pullup(&mut self, enable: bool) {
|
||||
let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpu().bit(enable));
|
||||
}
|
||||
|
||||
fn rtcio_pulldown(&mut self, enable: bool) {
|
||||
let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpd().bit(enable));
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lp_gpio;
|
||||
@ -37,6 +37,13 @@ use crate::{
|
||||
peripherals::{GPIO, IO_MUX},
|
||||
};
|
||||
|
||||
#[cfg(soc_etm)]
|
||||
pub mod etm;
|
||||
#[cfg(lp_io)]
|
||||
pub mod lp_io;
|
||||
#[cfg(all(rtc_io, not(esp32)))]
|
||||
pub mod rtc_io;
|
||||
|
||||
/// Convenience type-alias for a no-pin / don't care - pin
|
||||
pub type NoPinType = Gpio0<Unknown>;
|
||||
|
||||
@ -2247,780 +2254,6 @@ macro_rules! analog {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(soc_etm)]
|
||||
pub mod etm {
|
||||
//! # Event Task Matrix Function
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! GPIO supports ETM function, that is, the ETM task of GPIO can be
|
||||
//! triggered by the ETM event of any peripheral, or the ETM task of any
|
||||
//! peripheral can be triggered by the ETM event of GPIO.
|
||||
//!
|
||||
//! The GPIO ETM provides eight task channels. The ETM tasks that each task
|
||||
//! channel can receive are:
|
||||
//! - SET: GPIO goes high when triggered
|
||||
//! - CLEAR: GPIO goes low when triggered
|
||||
//! - TOGGLE: GPIO toggle level when triggered.
|
||||
//!
|
||||
//! GPIO has eight event channels, and the ETM events that each event
|
||||
//! channel can generate are:
|
||||
//! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO
|
||||
//! has a rising edge
|
||||
//! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO
|
||||
//! has a falling edge
|
||||
//! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO
|
||||
//! is reversed
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```no_run
|
||||
//! let led_task = gpio_ext.channel0_task.toggle(&mut led);
|
||||
//! let button_event = gpio_ext.channel0_event.falling_edge(button);
|
||||
//! ```
|
||||
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
/// All the GPIO ETM channels
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmChannels<'d> {
|
||||
_gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>,
|
||||
pub channel0_task: GpioEtmTaskChannel<0>,
|
||||
pub channel0_event: GpioEtmEventChannel<0>,
|
||||
pub channel1_task: GpioEtmTaskChannel<1>,
|
||||
pub channel1_event: GpioEtmEventChannel<1>,
|
||||
pub channel2_task: GpioEtmTaskChannel<2>,
|
||||
pub channel2_event: GpioEtmEventChannel<2>,
|
||||
pub channel3_task: GpioEtmTaskChannel<3>,
|
||||
pub channel3_event: GpioEtmEventChannel<3>,
|
||||
pub channel4_task: GpioEtmTaskChannel<4>,
|
||||
pub channel4_event: GpioEtmEventChannel<4>,
|
||||
pub channel5_task: GpioEtmTaskChannel<5>,
|
||||
pub channel5_event: GpioEtmEventChannel<5>,
|
||||
pub channel6_task: GpioEtmTaskChannel<6>,
|
||||
pub channel6_event: GpioEtmEventChannel<6>,
|
||||
pub channel7_task: GpioEtmTaskChannel<7>,
|
||||
pub channel7_event: GpioEtmEventChannel<7>,
|
||||
}
|
||||
|
||||
impl<'d> GpioEtmChannels<'d> {
|
||||
pub fn new(peripheral: impl Peripheral<P = crate::peripherals::GPIO_SD> + 'd) -> Self {
|
||||
crate::into_ref!(peripheral);
|
||||
|
||||
Self {
|
||||
_gpio_sd: peripheral,
|
||||
channel0_task: GpioEtmTaskChannel {},
|
||||
channel0_event: GpioEtmEventChannel {},
|
||||
channel1_task: GpioEtmTaskChannel {},
|
||||
channel1_event: GpioEtmEventChannel {},
|
||||
channel2_task: GpioEtmTaskChannel {},
|
||||
channel2_event: GpioEtmEventChannel {},
|
||||
channel3_task: GpioEtmTaskChannel {},
|
||||
channel3_event: GpioEtmEventChannel {},
|
||||
channel4_task: GpioEtmTaskChannel {},
|
||||
channel4_event: GpioEtmEventChannel {},
|
||||
channel5_task: GpioEtmTaskChannel {},
|
||||
channel5_event: GpioEtmEventChannel {},
|
||||
channel6_task: GpioEtmTaskChannel {},
|
||||
channel6_event: GpioEtmEventChannel {},
|
||||
channel7_task: GpioEtmTaskChannel {},
|
||||
channel7_event: GpioEtmEventChannel {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ETM controlled GPIO event
|
||||
pub struct GpioEtmEventChannel<const C: u8> {}
|
||||
|
||||
impl<const C: u8> GpioEtmEventChannel<C> {
|
||||
/// Trigger at rising edge
|
||||
pub fn rising_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelRising<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelRising { _pin: pin }
|
||||
}
|
||||
|
||||
/// Trigger at falling edge
|
||||
pub fn falling_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelFalling<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelFalling { _pin: pin }
|
||||
}
|
||||
|
||||
/// Trigger at any edge
|
||||
pub fn any_edge<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmEventChannelAny<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_event_channel(C, pin.number());
|
||||
GpioEtmEventChannelAny { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for rising edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
1 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for falling edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
9 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for any edge
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
17 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// An ETM controlled GPIO task
|
||||
pub struct GpioEtmTaskChannel<const C: u8> {}
|
||||
|
||||
impl<const C: u8> GpioEtmTaskChannel<C> {
|
||||
// In theory we could have multiple pins assigned to the same task. Not sure how
|
||||
// useful that would be. If we want to support it, the easiest would be
|
||||
// to offer additional functions like `set2`, `set3` etc. where the
|
||||
// number is the pin-count
|
||||
|
||||
/// Task to set a high level
|
||||
pub fn set<'d, PIN>(self, pin: impl Peripheral<P = PIN> + 'd) -> GpioEtmTaskSet<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskSet { _pin: pin }
|
||||
}
|
||||
|
||||
/// Task to set a low level
|
||||
pub fn clear<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmTaskClear<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskClear { _pin: pin }
|
||||
}
|
||||
|
||||
/// Task to toggle the level
|
||||
pub fn toggle<'d, PIN>(
|
||||
self,
|
||||
pin: impl Peripheral<P = PIN> + 'd,
|
||||
) -> GpioEtmTaskToggle<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
crate::into_ref!(pin);
|
||||
enable_task_channel(C, pin.number());
|
||||
GpioEtmTaskToggle { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for set operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskSet<'d, PIN, const C: u8>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskSet<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
1 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for clear operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskClear<'d, PIN, const C: u8> {
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskClear<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
9 + C
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for toggle operation
|
||||
#[non_exhaustive]
|
||||
pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> {
|
||||
_pin: PeripheralRef<'d, PIN>,
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where
|
||||
PIN: super::Pin
|
||||
{
|
||||
}
|
||||
|
||||
impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C>
|
||||
where
|
||||
PIN: super::Pin,
|
||||
{
|
||||
fn id(&self) -> u8 {
|
||||
17 + C
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_task_channel(channel: u8, pin: u8) {
|
||||
let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
|
||||
let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
|
||||
let shift = 8 * (pin as usize % 4);
|
||||
// bit 0 = en, bit 1-3 = channel
|
||||
unsafe {
|
||||
ptr.write_volatile(
|
||||
ptr.read_volatile() & !(0xf << shift)
|
||||
| 1 << shift
|
||||
| (channel as u32) << (shift + 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_event_channel(channel: u8, pin: u8) {
|
||||
let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_en().clear_bit());
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_sel().variant(pin));
|
||||
gpio_sd
|
||||
.etm_event_ch_cfg(channel as usize)
|
||||
.modify(|_, w| w.etm_ch0_event_en().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(rtc_io, not(esp32)))]
|
||||
pub mod rtc_io {
|
||||
//! RTC IO
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The hardware provides a couple of GPIO pins with low power (LP)
|
||||
//! capabilities and analog functions. These pins can be controlled by
|
||||
//! either IO MUX or RTC IO.
|
||||
//!
|
||||
//! If controlled by RTC IO, these pins will bypass IO MUX and GPIO
|
||||
//! matrix for the use by ULP and peripherals in RTC system.
|
||||
//!
|
||||
//! When configured as RTC GPIOs, the pins can still be controlled by ULP or
|
||||
//! the peripherals in RTC system during chip Deep-sleep, and wake up the
|
||||
//! chip from Deep-sleep.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
//! // configure GPIO 1 as ULP output pin
|
||||
//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
|
||||
//! ```
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
use super::OpenDrain;
|
||||
use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
|
||||
|
||||
/// A GPIO pin configured for low power operation
|
||||
pub struct LowPowerPin<MODE, const PIN: u8> {
|
||||
pub(crate) private: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
/// Configures a pin for use as a low power pin
|
||||
pub trait IntoLowPowerPin<const PIN: u8> {
|
||||
fn into_low_power(self) -> LowPowerPin<Unknown, { PIN }>;
|
||||
}
|
||||
|
||||
impl<MODE, const PIN: u8> LowPowerPin<MODE, PIN> {
|
||||
#[doc(hidden)]
|
||||
pub fn output_enable(&self, enable: bool) {
|
||||
let rtc_io = unsafe { crate::peripherals::RTC_IO::steal() };
|
||||
if enable {
|
||||
// TODO align PAC
|
||||
#[cfg(esp32s2)]
|
||||
rtc_io
|
||||
.rtc_gpio_enable_w1ts()
|
||||
.write(|w| w.reg_rtcio_reg_gpio_enable_w1ts().variant(1 << PIN));
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
rtc_io
|
||||
.rtc_gpio_enable_w1ts()
|
||||
.write(|w| w.rtc_gpio_enable_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.enable_w1tc()
|
||||
.write(|w| w.enable_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
fn input_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
|
||||
}
|
||||
|
||||
fn pullup_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.rue().bit(enable));
|
||||
}
|
||||
|
||||
fn pulldown_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.rde().bit(enable));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_level(&mut self, level: bool) {
|
||||
let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
|
||||
|
||||
// TODO align PACs
|
||||
#[cfg(esp32s2)]
|
||||
if level {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1ts()
|
||||
.write(|w| w.gpio_out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1tc()
|
||||
.write(|w| w.gpio_out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
if level {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1ts()
|
||||
.write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1tc()
|
||||
.write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn get_level(&self) -> bool {
|
||||
let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
|
||||
(rtc_io.rtc_gpio_in().read().bits() & 1 << PIN) != 0
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-up resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_up_input(self) -> LowPowerPin<Input<PullUp>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(true);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-down resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_down_input(self) -> LowPowerPin<Input<PullDown>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as a floating input pin.
|
||||
pub fn into_floating_input(self) -> LowPowerPin<Input<Floating>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an output pin.
|
||||
pub fn into_push_pull_output(self) -> LowPowerPin<Output<PushPull>, PIN> {
|
||||
self.output_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
/// Configures the pin as an pullup input and a push pull output pin.
|
||||
pub fn into_open_drain_output(self) -> LowPowerPin<OpenDrain, PIN> {
|
||||
self.into_pull_up_input();
|
||||
self.into_push_pull_output();
|
||||
use crate::peripherals::GPIO;
|
||||
|
||||
let gpio = unsafe { &*GPIO::PTR };
|
||||
|
||||
gpio.pin(PIN).modify(|_, w| w.pad_driver().bit(true));
|
||||
self.pulldown_enable(false);
|
||||
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 {
|
||||
unsafe {
|
||||
let rtc_io = &*crate::peripherals::RTC_IO::PTR;
|
||||
let pin_ptr = (rtc_io.touch_pad0().as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr
|
||||
as *const esp32s3::generic::Reg<esp32s3::rtc_io::touch_pad0::TOUCH_PAD0_SPEC>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD {
|
||||
unsafe {
|
||||
let rtc_io = &*crate::peripherals::RTC_IO::PTR;
|
||||
let pin_ptr = (rtc_io.touch_pad(0).as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr as *const esp32s2::generic::Reg<esp32s2::rtc_io::touch_pad::TOUCH_PAD_SPEC>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(lp_io)]
|
||||
pub mod lp_gpio {
|
||||
//! Low Power IO (LP_IO)
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The hardware provides a couple of GPIO pins with low power (LP)
|
||||
//! capabilities and analog functions. These pins can be controlled by
|
||||
//! either IO MUX or LP IO MUX.
|
||||
//!
|
||||
//! If controlled by LP IO MUX, these pins will bypass IO MUX and GPIO
|
||||
//! matrix for the use by ULP and peripherals in LP system.
|
||||
//!
|
||||
//! When configured as LP GPIOs, the pins can still be controlled by ULP or
|
||||
//! the peripherals in LP system during chip Deep-sleep, and wake up the
|
||||
//! chip from Deep-sleep.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
//! // configure GPIO 1 as LP output pin
|
||||
//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
|
||||
//! ```
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
use super::OpenDrain;
|
||||
use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
|
||||
|
||||
/// A GPIO pin configured for low power operation
|
||||
pub struct LowPowerPin<MODE, const PIN: u8> {
|
||||
pub(crate) private: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE, const PIN: u8> LowPowerPin<MODE, PIN> {
|
||||
#[doc(hidden)]
|
||||
pub fn output_enable(&self, enable: bool) {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
if enable {
|
||||
lp_io
|
||||
.out_enable_w1ts()
|
||||
.write(|w| w.enable_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
lp_io
|
||||
.out_enable_w1tc()
|
||||
.write(|w| w.enable_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
fn input_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
|
||||
}
|
||||
|
||||
fn pullup_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_wpu().bit(enable));
|
||||
}
|
||||
|
||||
fn pulldown_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_wpd().bit(enable));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_level(&mut self, level: bool) {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
if level {
|
||||
lp_io
|
||||
.out_data_w1ts()
|
||||
.write(|w| w.out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
lp_io
|
||||
.out_data_w1tc()
|
||||
.write(|w| w.out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn get_level(&self) -> bool {
|
||||
let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
|
||||
(lp_io.in_().read().data_next().bits() & 1 << PIN) != 0
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-up resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_up_input(self) -> LowPowerPin<Input<PullUp>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(true);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-down resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_down_input(self) -> LowPowerPin<Input<PullDown>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as a floating input pin.
|
||||
pub fn into_floating_input(self) -> LowPowerPin<Input<Floating>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an output pin.
|
||||
pub fn into_push_pull_output(self) -> LowPowerPin<Output<PushPull>, PIN> {
|
||||
self.output_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_open_drain_output(self) -> LowPowerPin<OpenDrain, PIN> {
|
||||
use crate::peripherals::GPIO;
|
||||
|
||||
let gpio = unsafe { &*GPIO::PTR };
|
||||
|
||||
gpio.pin(PIN as usize)
|
||||
.modify(|_, w| w.pad_driver().bit(true));
|
||||
self.pulldown_enable(false);
|
||||
self.into_pull_up_input().into_push_pull_output();
|
||||
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_low_power_pin(pin: u8) {
|
||||
let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR };
|
||||
|
||||
lp_aon
|
||||
.gpio_mux()
|
||||
.modify(|r, w| w.sel().variant(r.sel().bits() | 1 << pin));
|
||||
|
||||
get_pin_reg(pin).modify(|_, w| w.mcu_sel().variant(0));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::lp_io::GPIO0 {
|
||||
// ideally we should change the SVD and make the GPIOx registers into an
|
||||
// array
|
||||
unsafe {
|
||||
let lp_io = &*crate::peripherals::LP_IO::PTR;
|
||||
let pin_ptr = (lp_io.gpio0().as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr as *const esp32c6::generic::Reg<esp32c6::lp_io::gpio0::GPIO0_SPEC>)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures a pin for use as a low power pin
|
||||
pub trait IntoLowPowerPin<const PIN: u8> {
|
||||
fn into_low_power(self) -> LowPowerPin<Unknown, { PIN }>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! lp_gpio {
|
||||
(
|
||||
$($gpionum:literal)+
|
||||
) => {
|
||||
paste::paste!{
|
||||
$(
|
||||
impl<MODE> $crate::gpio::lp_gpio::IntoLowPowerPin<$gpionum> for GpioPin<MODE, $gpionum> {
|
||||
fn into_low_power(self) -> $crate::gpio::lp_gpio::LowPowerPin<Unknown, $gpionum> {
|
||||
$crate::gpio::lp_gpio::init_low_power_pin($gpionum);
|
||||
$crate::gpio::lp_gpio::LowPowerPin {
|
||||
private: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $crate::gpio::RTCPin for GpioPin<MODE, $gpionum> {
|
||||
unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) {
|
||||
let lp_io = &*$crate::peripherals::LP_IO::ptr();
|
||||
lp_io.[< pin $gpionum >]().modify(|_, w| {
|
||||
w.wakeup_enable().bit(wakeup).int_type().bits(level)
|
||||
});
|
||||
}
|
||||
|
||||
fn rtcio_pad_hold(&mut self, enable: bool) {
|
||||
let mask = 1 << $gpionum;
|
||||
unsafe {
|
||||
let lp_aon = &*$crate::peripherals::LP_AON::ptr();
|
||||
|
||||
lp_aon.gpio_hold0().modify(|r, w| {
|
||||
if enable {
|
||||
w.gpio_hold0().bits(r.gpio_hold0().bits() | mask)
|
||||
} else {
|
||||
w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the LP properties of the pin. If `mux` is true then then pin is
|
||||
/// routed to LP_IO, when false it is routed to IO_MUX.
|
||||
fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) {
|
||||
let mask = 1 << $gpionum;
|
||||
unsafe {
|
||||
// Select LP_IO
|
||||
let lp_aon = &*$crate::peripherals::LP_AON::ptr();
|
||||
lp_aon
|
||||
.gpio_mux()
|
||||
.modify(|r, w| {
|
||||
if mux {
|
||||
w.sel().bits(r.sel().bits() | mask)
|
||||
} else {
|
||||
w.sel().bits(r.sel().bits() & !mask)
|
||||
}
|
||||
});
|
||||
|
||||
// Configure input, function and select normal operation registers
|
||||
let lp_io = &*$crate::peripherals::LP_IO::ptr();
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| {
|
||||
w
|
||||
.slp_sel().bit(false)
|
||||
.fun_ie().bit(input_enable)
|
||||
.mcu_sel().bits(func as u8)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $crate::gpio::RTCPinWithResistors for GpioPin<MODE, $gpionum> {
|
||||
fn rtcio_pullup(&mut self, enable: bool) {
|
||||
let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpu().bit(enable));
|
||||
}
|
||||
|
||||
fn rtcio_pulldown(&mut self, enable: bool) {
|
||||
let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
|
||||
lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpd().bit(enable));
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lp_gpio;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
mod asynch {
|
||||
use core::task::{Context, Poll};
|
||||
185
esp-hal/src/gpio/rtc_io.rs
Normal file
185
esp-hal/src/gpio/rtc_io.rs
Normal file
@ -0,0 +1,185 @@
|
||||
//! RTC IO
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The hardware provides a couple of GPIO pins with low power (LP)
|
||||
//! capabilities and analog functions. These pins can be controlled by
|
||||
//! either IO MUX or RTC IO.
|
||||
//!
|
||||
//! If controlled by RTC IO, these pins will bypass IO MUX and GPIO
|
||||
//! matrix for the use by ULP and peripherals in RTC system.
|
||||
//!
|
||||
//! When configured as RTC GPIOs, the pins can still be controlled by ULP or
|
||||
//! the peripherals in RTC system during chip Deep-sleep, and wake up the
|
||||
//! chip from Deep-sleep.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
//! // configure GPIO 1 as ULP output pin
|
||||
//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
|
||||
//! ```
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
use super::OpenDrain;
|
||||
use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
|
||||
|
||||
/// A GPIO pin configured for low power operation
|
||||
pub struct LowPowerPin<MODE, const PIN: u8> {
|
||||
pub(crate) private: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
/// Configures a pin for use as a low power pin
|
||||
pub trait IntoLowPowerPin<const PIN: u8> {
|
||||
fn into_low_power(self) -> LowPowerPin<Unknown, { PIN }>;
|
||||
}
|
||||
|
||||
impl<MODE, const PIN: u8> LowPowerPin<MODE, PIN> {
|
||||
#[doc(hidden)]
|
||||
pub fn output_enable(&self, enable: bool) {
|
||||
let rtc_io = unsafe { crate::peripherals::RTC_IO::steal() };
|
||||
if enable {
|
||||
// TODO align PAC
|
||||
#[cfg(esp32s2)]
|
||||
rtc_io
|
||||
.rtc_gpio_enable_w1ts()
|
||||
.write(|w| w.reg_rtcio_reg_gpio_enable_w1ts().variant(1 << PIN));
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
rtc_io
|
||||
.rtc_gpio_enable_w1ts()
|
||||
.write(|w| w.rtc_gpio_enable_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.enable_w1tc()
|
||||
.write(|w| w.enable_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
fn input_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
|
||||
}
|
||||
|
||||
fn pullup_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.rue().bit(enable));
|
||||
}
|
||||
|
||||
fn pulldown_enable(&self, enable: bool) {
|
||||
get_pin_reg(PIN).modify(|_, w| w.rde().bit(enable));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_level(&mut self, level: bool) {
|
||||
let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
|
||||
|
||||
// TODO align PACs
|
||||
#[cfg(esp32s2)]
|
||||
if level {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1ts()
|
||||
.write(|w| w.gpio_out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1tc()
|
||||
.write(|w| w.gpio_out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
if level {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1ts()
|
||||
.write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN));
|
||||
} else {
|
||||
rtc_io
|
||||
.rtc_gpio_out_w1tc()
|
||||
.write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN));
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn get_level(&self) -> bool {
|
||||
let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
|
||||
(rtc_io.rtc_gpio_in().read().bits() & 1 << PIN) != 0
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-up resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_up_input(self) -> LowPowerPin<Input<PullUp>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(true);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an input with the internal pull-down resistor
|
||||
/// enabled.
|
||||
pub fn into_pull_down_input(self) -> LowPowerPin<Input<PullDown>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as a floating input pin.
|
||||
pub fn into_floating_input(self) -> LowPowerPin<Input<Floating>, PIN> {
|
||||
self.input_enable(true);
|
||||
self.pullup_enable(false);
|
||||
self.pulldown_enable(false);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the pin as an output pin.
|
||||
pub fn into_push_pull_output(self) -> LowPowerPin<Output<PushPull>, PIN> {
|
||||
self.output_enable(true);
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
/// Configures the pin as an pullup input and a push pull output pin.
|
||||
pub fn into_open_drain_output(self) -> LowPowerPin<OpenDrain, PIN> {
|
||||
self.into_pull_up_input();
|
||||
self.into_push_pull_output();
|
||||
use crate::peripherals::GPIO;
|
||||
|
||||
let gpio = unsafe { &*GPIO::PTR };
|
||||
|
||||
gpio.pin(PIN).modify(|_, w| w.pad_driver().bit(true));
|
||||
self.pulldown_enable(false);
|
||||
|
||||
LowPowerPin {
|
||||
private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 {
|
||||
unsafe {
|
||||
let rtc_io = &*crate::peripherals::RTC_IO::PTR;
|
||||
let pin_ptr = (rtc_io.touch_pad0().as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr as *const esp32s3::generic::Reg<esp32s3::rtc_io::touch_pad0::TOUCH_PAD0_SPEC>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
#[inline(always)]
|
||||
fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD {
|
||||
unsafe {
|
||||
let rtc_io = &*crate::peripherals::RTC_IO::PTR;
|
||||
let pin_ptr = (rtc_io.touch_pad(0).as_ptr()).add(pin as usize);
|
||||
|
||||
&*(pin_ptr as *const esp32s2::generic::Reg<esp32s2::rtc_io::touch_pad::TOUCH_PAD_SPEC>)
|
||||
}
|
||||
}
|
||||
@ -1764,7 +1764,7 @@ pub mod lp_i2c {
|
||||
use fugit::HertzU32;
|
||||
|
||||
use crate::{
|
||||
gpio::{lp_gpio::LowPowerPin, OpenDrain},
|
||||
gpio::{lp_io::LowPowerPin, OpenDrain},
|
||||
peripherals::{LP_CLKRST, LP_I2C0},
|
||||
};
|
||||
|
||||
|
||||
@ -321,7 +321,7 @@ crate::gpio::analog! {
|
||||
7
|
||||
}
|
||||
|
||||
crate::gpio::lp_gpio::lp_gpio! {
|
||||
crate::gpio::lp_io::lp_gpio! {
|
||||
0
|
||||
1
|
||||
2
|
||||
|
||||
@ -458,7 +458,7 @@ crate::gpio::gpio! {
|
||||
// crate::gpio::analog! {
|
||||
// }
|
||||
|
||||
// crate::gpio::lp_gpio::lp_gpio! {
|
||||
// crate::gpio::lp_io::lp_gpio! {
|
||||
// 0
|
||||
// 1
|
||||
// 2
|
||||
|
||||
@ -2012,7 +2012,7 @@ mod asynch {
|
||||
#[cfg(lp_uart)]
|
||||
pub mod lp_uart {
|
||||
use crate::{
|
||||
gpio::{lp_gpio::LowPowerPin, Floating, Input, Output, PushPull},
|
||||
gpio::{lp_io::LowPowerPin, Floating, Input, Output, PushPull},
|
||||
peripherals::{LP_CLKRST, LP_UART},
|
||||
uart::{config, config::Config},
|
||||
};
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
gpio::{lp_gpio::IntoLowPowerPin, IO},
|
||||
gpio::{lp_io::IntoLowPowerPin, IO},
|
||||
lp_core::{LpCore, LpCoreWakeupSource},
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
gpio::{lp_gpio::IntoLowPowerPin, IO},
|
||||
gpio::{lp_io::IntoLowPowerPin, IO},
|
||||
i2c::lp_i2c::LpI2c,
|
||||
lp_core::{LpCore, LpCoreWakeupSource},
|
||||
peripherals::Peripherals,
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
gpio::{lp_gpio::IntoLowPowerPin, IO},
|
||||
gpio::{lp_io::IntoLowPowerPin, IO},
|
||||
lp_core::{LpCore, LpCoreWakeupSource},
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user