Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract inner modules of gpio module into their own files #1397

Merged
merged 4 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion esp-hal-procmacros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,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)

### Removed

Expand Down
322 changes: 322 additions & 0 deletions esp-hal/src/gpio/etm.rs
Original file line number Diff line number Diff line change
@@ -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());
}
Loading
Loading