diff --git a/esp-hal-async/Cargo.toml b/esp-hal-async/Cargo.toml index 2b731a43368..6041925c783 100644 --- a/esp-hal-async/Cargo.toml +++ b/esp-hal-async/Cargo.toml @@ -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? diff --git a/esp-hal-async/examples/embassy_hello.rs b/esp-hal-async/examples/embassy_hello.rs index 8bd295efdf2..d33a0a61a04 100644 --- a/esp-hal-async/examples/embassy_hello.rs +++ b/esp-hal-async/examples/embassy_hello.rs @@ -9,14 +9,25 @@ 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; } } @@ -24,7 +35,15 @@ async fn run1() { 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>>) { + loop { + pin.wait_for_rising_edge().await.unwrap(); + esp_println::println!("Button Pressed!"); } } @@ -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; diff --git a/esp-hal-async/src/gpio.rs b/esp-hal-async/src/gpio.rs new file mode 100644 index 00000000000..76755200e4d --- /dev/null +++ b/esp-hal-async/src/gpio.rs @@ -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(pub T); // TODO remove pub and instead make the async hal emit these pins already + // wrapped + +impl Deref for AsyncPin { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for AsyncPin { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl embedded_hal::digital::ErrorType for AsyncPin +where + T: embedded_hal::digital::ErrorType, +{ + type Error = T::Error; +} + +impl Wait for AsyncPin +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 { + 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); + } +} diff --git a/esp-hal-async/src/lib.rs b/esp-hal-async/src/lib.rs index d351ff7402f..6c211e4eba6 100644 --- a/esp-hal-async/src/lib.rs +++ b/esp-hal-async/src/lib.rs @@ -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::*; diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index 60a8cff04bf..366a1647f67 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -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); @@ -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); }