Skip to content

Commit

Permalink
async: gpio Wait implementation
Browse files Browse the repository at this point in the history
- Wrap hal gpio with Async wrapper that implements `Wait`
- One extra trait method on `gpio::Pin` to tell if a pin is in listening
  state
  • Loading branch information
MabezDev committed Jul 28, 2022
1 parent 014e35b commit 7c9adcb
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 6 deletions.
7 changes: 5 additions & 2 deletions esp-hal-async/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ edition = "2021"
[dependencies]
embassy = { git = "https://github.com/embassy-rs/embassy", rev = "84cffc751ac0a38e6736959e68d441b99138d6d0", features = ["nightly", "time-tick-16mhz"] } # TODO make optional
critical-section = "0.2"
esp32c3-hal = { version = "0.1.0", path = "../esp32c3-hal", optional= true }
esp32s3-hal = { version = "0.1.0", path = "../esp32s3-hal", optional= true }
esp-hal-common = { version = "0.1.0", path = "../esp-hal-common", features = ["eh1"] }
embedded-hal-async = "0.1.0-alpha.1"
embedded-hal = { version = "=1.0.0-alpha.8"}
esp32c3-hal = { version = "0.1.0", path = "../esp32c3-hal", optional= true, features = ["eh1"] }
esp32s3-hal = { version = "0.1.0", path = "../esp32s3-hal", optional= true, features = ["eh1"] }

riscv-rt = { version = "0.9", optional = true } # TODO reexport from esp-hal-common?
xtensa-lx-rt = { version = "0.13", optional = true } # TODO reexport from esp-hal-common?
Expand Down
40 changes: 36 additions & 4 deletions esp-hal-async/examples/embassy_hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,41 @@ use embassy::{
time::{Duration, Timer},
util::Forever,
};
use esp_hal_async::{clock::ClockControl, prelude::*, timer::TimerGroup, RtcCntl};
use embedded_hal_async::digital::Wait;
use esp32c3_hal::gpio::Gpio1;
use esp_backtrace as _;
use esp_hal_async::{
clock::ClockControl,
gpio::AsyncPin,
interrupt,
prelude::*,
timer::TimerGroup,
RtcCntl,
IO,
};
use esp_hal_common::{Input, PullDown};

#[embassy::task]
async fn run1() {
loop {
esp_println::println!("Hello world from embassy using esp-hal-async!");
Timer::after(Duration::from_millis(1000)).await;
Timer::after(Duration::from_millis(10_000)).await;
}
}

#[embassy::task]
async fn run2() {
loop {
esp_println::println!("Bing!");
Timer::after(Duration::from_millis(3000)).await;
Timer::after(Duration::from_millis(30_000)).await;
}
}

#[embassy::task]
async fn run3(mut pin: AsyncPin<Gpio1<Input<PullDown>>>) {
loop {
pin.wait_for_rising_edge().await.unwrap();
esp_println::println!("Button Pressed!");
}
}

Expand Down Expand Up @@ -55,14 +74,27 @@ fn main() -> ! {
wdt0.disable();
wdt1.disable();

let io = IO::new(p.GPIO, p.IO_MUX);

// Set GPIO1 as an input
let button = io.pins.gpio1.into_pull_down_input();

interrupt::enable(
esp_hal_async::pac::Interrupt::GPIO,
crate::interrupt::Priority::Priority1,
)
.unwrap();

let async_button = AsyncPin(button);

let executor = EXECUTOR.put(Executor::new());
executor.run(|spawner| {
spawner.spawn(run1()).ok();
spawner.spawn(run2()).ok();
spawner.spawn(run3(async_button)).ok();
});
}


#[cfg(feature = "esp32s3")]
mod cs {
struct CriticalSection;
Expand Down
137 changes: 137 additions & 0 deletions esp-hal-async/src/gpio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use core::{
ops::{Deref, DerefMut},
task::{Context, Poll},
};

use embassy::waitqueue::AtomicWaker;
use embedded_hal_async::digital::Wait;
use esp_hal_common::Event;

use crate::{pac, prelude::*};

#[allow(clippy::declare_interior_mutable_const)]
const NEW_AW: AtomicWaker = AtomicWaker::new();
#[cfg(feature = "esp32c3")]
const PIN_COUNT: usize = 26; // TODO cfg for each chip
static PIN_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT];

pub struct AsyncPin<T>(pub T); // TODO remove pub and instead make the async hal emit these pins already
// wrapped

impl<T> Deref for AsyncPin<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> DerefMut for AsyncPin<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T> embedded_hal::digital::ErrorType for AsyncPin<T>
where
T: embedded_hal::digital::ErrorType,
{
type Error = T::Error;
}

impl<T> Wait for AsyncPin<T>
where
T: esp_hal_common::gpio::Pin + embedded_hal::digital::ErrorType,
{
type WaitForHighFuture<'a> = PinFuture<'a, T>
where
Self: 'a;

fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.listen(Event::HighLevel);
PinFuture::new(&mut self.0)
}

type WaitForLowFuture<'a> = PinFuture<'a, T>
where
Self: 'a;

fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.listen(Event::LowLevel);
PinFuture::new(&mut self.0)
}

type WaitForRisingEdgeFuture<'a> = PinFuture<'a, T>
where
Self: 'a;

fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.listen(Event::RisingEdge);
PinFuture::new(&mut self.0)
}

type WaitForFallingEdgeFuture<'a> = PinFuture<'a, T>
where
Self: 'a;

fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.listen(Event::FallingEdge);
PinFuture::new(&mut self.0)
}

type WaitForAnyEdgeFuture<'a> = PinFuture<'a, T>
where
Self: 'a;

fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.listen(Event::AnyEdge);
PinFuture::new(&mut self.0)
}
}

pub struct PinFuture<'a, P> {
pin: &'a P,
}

impl<'a, P> PinFuture<'a, P>
where
P: esp_hal_common::gpio::Pin + embedded_hal::digital::ErrorType,
{
pub fn new(pin: &'a P) -> Self {
Self { pin }
}
}

impl<'a, P> core::future::Future for PinFuture<'a, P>
where
P: esp_hal_common::gpio::Pin + embedded_hal::digital::ErrorType,
{
type Output = Result<(), P::Error>;

fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
PIN_WAKERS[self.pin.number() as usize].register(cx.waker());

// if pin is no longer listening its been triggered
// therefore the future has resolved
if !self.pin.is_listening() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}

#[interrupt]
unsafe fn GPIO() {
let gpio = crate::pac::GPIO::PTR;
let mut intrs = (*gpio).pcpu_int.read().bits();
(*gpio).status_w1tc.write(|w| w.bits(intrs)); // clear interrupts

while intrs != 0 {
let pin_nr = intrs.trailing_zeros();
// TODO in the future we could conjure a pin and reuse code in esp-hal
(*gpio).pin[pin_nr as usize].modify(|_, w| w.pin_int_ena().bits(0)); // stop listening, this is the signal that the future is ready
PIN_WAKERS[pin_nr as usize].wake(); // wake task
intrs &= !(1u32 << pin_nr);
}
}
3 changes: 3 additions & 0 deletions esp-hal-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
//! [esp32s3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s3-hal

#![no_std]
#![feature(into_future)]
#![feature(generic_associated_types)]

pub mod embassy;
pub mod gpio;

#[cfg(feature = "esp32")]
pub use esp32_hal::*;
Expand Down
7 changes: 7 additions & 0 deletions esp-hal-common/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ pub trait Pin {
wake_up_from_light_sleep: bool,
);

fn is_listening(&self) -> bool;

fn unlisten(&mut self);

fn clear_interrupt(&mut self);
Expand Down Expand Up @@ -689,6 +691,11 @@ macro_rules! impl_input {
}
}

fn is_listening(&self) -> bool {
let bits = unsafe { (&*GPIO::PTR) }.pin[$pin_num].read().pin_int_ena().bits();
bits != 0
}

fn clear_interrupt(&mut self) {
self.write_interrupt_status_clear(1 << $bit);
}
Expand Down

0 comments on commit 7c9adcb

Please sign in to comment.