diff --git a/Cargo.toml b/Cargo.toml index ec451bf..55fa288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ proc-macro2 = "1.0" quote = "1.0" [features] -default = ["embassy", "rt"] +default = ["embassy", "rt", "exti"] rt = ["dep:qingke-rt"] highcode = ["qingke-rt/highcode"] embassy = [ @@ -66,6 +66,7 @@ embassy = [ ] defmt = ["dep:defmt"] memory-x = ["ch32-metapac/memory-x"] +exti = [] # Features starting with `_` are for internal use only. They're not intended diff --git a/src/exti.rs b/src/exti.rs index a2458d1..3d613ea 100644 --- a/src/exti.rs +++ b/src/exti.rs @@ -1,37 +1,11 @@ -use core::future::Future; -use core::marker::PhantomData; -use core::pin::Pin; -use core::task::{Context, Poll}; - use embassy_sync::waitqueue::AtomicWaker; -use qingke_rt::interrupt; -use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; -use crate::{impl_peripheral, into_ref, peripherals, Peripheral}; +use crate::{impl_peripheral, peripherals}; const EXTI_COUNT: usize = 24; const NEW_AW: AtomicWaker = AtomicWaker::new(); static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT]; -pub unsafe fn on_irq() { - let exti = &crate::pac::EXTI; - - let bits = exti.intfr().read(); - - // We don't handle or change any EXTI lines above 24. - let bits = bits.0 & 0x00FFFFFF; - - // Clear pending - Clears the EXTI's line pending bits. - exti.intfr().write(|w| w.0 = bits); - - exti.intenr().modify(|w| w.0 = w.0 & !bits); - - // Wake the tasks - for pin in BitIter(bits) { - EXTI_WAKERS[pin as usize].wake(); - } -} - struct BitIter(u32); impl Iterator for BitIter { @@ -48,150 +22,6 @@ impl Iterator for BitIter { } } -/// EXTI input driver -pub struct ExtiInput<'d> { - pin: Input<'d>, -} - -impl<'d> Unpin for ExtiInput<'d> {} - -impl<'d> ExtiInput<'d> { - pub fn new( - pin: impl Peripheral

+ 'd, - ch: impl Peripheral

+ 'd, - pull: Pull, - ) -> Self { - into_ref!(pin, ch); - // Needed if using AnyPin+AnyChannel. - assert_eq!(pin.pin(), ch.number()); - - Self { - pin: Input::new(pin, pull), - } - } - - pub fn is_high(&self) -> bool { - self.pin.is_high() - } - - pub fn is_low(&self) -> bool { - self.pin.is_low() - } - - pub fn get_level(&self) -> Level { - self.pin.get_level() - } - - pub async fn wait_for_high<'a>(&'a mut self) { - let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); - if self.is_high() { - return; - } - fut.await - } - - pub async fn wait_for_low<'a>(&'a mut self) { - let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); - if self.is_low() { - return; - } - fut.await - } - - pub async fn wait_for_rising_edge<'a>(&'a mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await - } - - pub async fn wait_for_falling_edge<'a>(&'a mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await - } - - pub async fn wait_for_any_edge<'a>(&'a mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await - } -} - -#[must_use = "futures do nothing unless you `.await` or poll them"] -struct ExtiInputFuture<'a> { - pin: u8, - phantom: PhantomData<&'a mut AnyPin>, -} - -// EXTI0-EXTI23 Px0-Px23(x=A/B/C) -impl<'a> ExtiInputFuture<'a> { - fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { - critical_section::with(|_| { - let exti = &crate::pac::EXTI; - let afio = &crate::pac::AFIO; - - let port = port as u8; - let pin = pin as usize; - - #[cfg(afio_v0)] - { - // AFIO_EXTICR - // stride: 2, len: 15, 8 lines - afio.exticr().modify(|w| w.set_exti(pin, port)); - } - // V1, V2, V3, L1 - #[cfg(any(afio_v3, afio_l1))] - { - // AFIO_EXTICRx - // stride: 4, len: 4, 16 lines - afio.exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); - } - #[cfg(afio_x0)] - { - // stride: 2, len: 15, 24 lines - afio.exticr(pin / 16).modify(|w| w.set_exti(pin % 16, port)); - } - #[cfg(afio_ch641)] - { - // single register - afio.exticr().modify(|w| w.set_exti(pin, port != 0)); - } - - // See-also: 7.4.3 - exti.intenr().modify(|w| w.set_mr(pin, true)); // enable interrupt - - exti.rtenr().modify(|w| w.set_tr(pin, rising)); - exti.ftenr().modify(|w| w.set_tr(pin, falling)); - }); - - Self { - pin, - phantom: PhantomData, - } - } -} - -impl<'a> Drop for ExtiInputFuture<'a> { - fn drop(&mut self) { - critical_section::with(|_| { - let exti = &crate::pac::EXTI; - let pin = self.pin; - exti.intenr().modify(|w| w.0 = w.0 & !(1 << pin)); - }); - } -} - -impl<'a> Future for ExtiInputFuture<'a> { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let exti = &crate::pac::EXTI; - - EXTI_WAKERS[self.pin as usize].register(cx.waker()); - - if exti.intenr().read().mr(self.pin as _) == false { - // intenr cleared by on_irq, then we can assume it is triggered - Poll::Ready(()) - } else { - Poll::Pending - } - } -} - trait SealedChannel {} #[allow(private_bounds)] @@ -207,6 +37,7 @@ pub trait Channel: SealedChannel + Sized { pub struct AnyChannel { number: u8, } + impl_peripheral!(AnyChannel); impl SealedChannel for AnyChannel {} impl Channel for AnyChannel { @@ -267,128 +98,305 @@ mod _exti_24lines { impl_exti!(EXTI23, 23); } -/* -EXTI0 -EXTI1 -EXTI2 -EXTI3 -EXTI4 -EXTI9_5 -EXTI15_10 -EXTI7_0 -EXTI15_8 -EXTI25_16 -*/ - -/// safety: must be called only once -#[cfg(gpio_x0)] -mod irq_impl { - use super::*; +#[cfg(feature = "exti")] +pub use exti_inner::*; + +#[cfg(feature = "exti")] +mod exti_inner { + use super::{BitIter, Channel, EXTI_WAKERS}; + use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; + use crate::{into_ref, Peripheral}; + use core::future::Future; + use core::marker::PhantomData; + use core::pin::Pin; + use core::task::{Context, Poll}; + use qingke_rt::interrupt; + + /// EXTI input driver + pub struct ExtiInput<'d> { + pin: Input<'d>, + } + + impl<'d> Unpin for ExtiInput<'d> {} + + impl<'d> ExtiInput<'d> { + pub fn new( + pin: impl Peripheral

+ 'd, + ch: impl Peripheral

+ 'd, + pull: Pull, + ) -> Self { + into_ref!(pin, ch); + // Needed if using AnyPin+AnyChannel. + assert_eq!(pin.pin(), ch.number()); + + Self { + pin: Input::new(pin, pull), + } + } - #[interrupt] - unsafe fn EXTI7_0() { - on_irq(); - } - #[interrupt] - unsafe fn EXTI15_8() { - on_irq(); - } - #[interrupt] - unsafe fn EXTI25_16() { - on_irq(); - } + pub fn is_high(&self) -> bool { + self.pin.is_high() + } - pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { - use crate::pac::Interrupt; + pub fn is_low(&self) -> bool { + self.pin.is_low() + } - qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI25_16 as u8); - } -} + pub fn get_level(&self) -> Level { + self.pin.get_level() + } -#[cfg(all(gpio_v3, not(ch641)))] -mod irq_impl { - use super::*; + pub async fn wait_for_high<'a>(&'a mut self) { + let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); + if self.is_high() { + return; + } + fut.await + } - #[interrupt] - unsafe fn EXTI0() { - on_irq(); - } - #[interrupt] - unsafe fn EXTI1() { - on_irq(); - } - #[interrupt] - unsafe fn EXTI2() { - on_irq(); - } - #[interrupt] - unsafe fn EXTI3() { - on_irq(); + pub async fn wait_for_low<'a>(&'a mut self) { + let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); + if self.is_low() { + return; + } + fut.await + } + + pub async fn wait_for_rising_edge<'a>(&'a mut self) { + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await + } + + pub async fn wait_for_falling_edge<'a>(&'a mut self) { + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await + } + + pub async fn wait_for_any_edge<'a>(&'a mut self) { + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await + } } - #[interrupt] - unsafe fn EXTI4() { - on_irq(); + + pub unsafe fn on_irq() { + let exti = &crate::pac::EXTI; + + let bits = exti.intfr().read(); + + // We don't handle or change any EXTI lines above 24. + let bits = bits.0 & 0x00FFFFFF; + + // Clear pending - Clears the EXTI's line pending bits. + exti.intfr().write(|w| w.0 = bits); + + exti.intenr().modify(|w| w.0 = w.0 & !bits); + + // Wake the tasks + for pin in BitIter(bits) { + EXTI_WAKERS[pin as usize].wake(); + } } - #[interrupt] - unsafe fn EXTI9_5() { - on_irq(); + + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct ExtiInputFuture<'a> { + pin: u8, + phantom: PhantomData<&'a mut AnyPin>, + } + + // EXTI0-EXTI23 Px0-Px23(x=A/B/C) + impl<'a> ExtiInputFuture<'a> { + fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { + critical_section::with(|_| { + let exti = &crate::pac::EXTI; + let afio = &crate::pac::AFIO; + + let port = port as u8; + let pin = pin as usize; + + #[cfg(afio_v0)] + { + // AFIO_EXTICR + // stride: 2, len: 15, 8 lines + afio.exticr().modify(|w| w.set_exti(pin, port)); + } + // V1, V2, V3, L1 + #[cfg(any(afio_v3, afio_l1))] + { + // AFIO_EXTICRx + // stride: 4, len: 4, 16 lines + afio.exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); + } + #[cfg(afio_x0)] + { + // stride: 2, len: 15, 24 lines + afio.exticr(pin / 16).modify(|w| w.set_exti(pin % 16, port)); + } + #[cfg(afio_ch641)] + { + // single register + afio.exticr().modify(|w| w.set_exti(pin, port != 0)); + } + + // See-also: 7.4.3 + exti.intenr().modify(|w| w.set_mr(pin, true)); // enable interrupt + + exti.rtenr().modify(|w| w.set_tr(pin, rising)); + exti.ftenr().modify(|w| w.set_tr(pin, falling)); + }); + + Self { + pin, + phantom: PhantomData, + } + } } - #[interrupt] - unsafe fn EXTI15_10() { - on_irq(); + + impl<'a> Drop for ExtiInputFuture<'a> { + fn drop(&mut self) { + critical_section::with(|_| { + let exti = &crate::pac::EXTI; + let pin = self.pin; + exti.intenr().modify(|w| w.0 = w.0 & !(1 << pin)); + }); + } } - pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { - use crate::pac::Interrupt; + impl<'a> Future for ExtiInputFuture<'a> { + type Output = (); - qingke::pfic::enable_interrupt(Interrupt::EXTI0 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI1 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI2 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI3 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI4 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI9_5 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI15_10 as u8); - } -} + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let exti = &crate::pac::EXTI; -#[cfg(gpio_v0)] -mod irq_impl { - use super::*; + EXTI_WAKERS[self.pin as usize].register(cx.waker()); - #[interrupt] - unsafe fn EXTI7_0() { - on_irq(); + if exti.intenr().read().mr(self.pin as _) == false { + // intenr cleared by on_irq, then we can assume it is triggered + Poll::Ready(()) + } else { + Poll::Pending + } + } } - pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { - use crate::pac::Interrupt; + /* + EXTI0 + EXTI1 + EXTI2 + EXTI3 + EXTI4 + EXTI9_5 + EXTI15_10 + EXTI7_0 + EXTI15_8 + EXTI25_16 + */ + + /// safety: must be called only once + #[cfg(gpio_x0)] + mod irq_impl { + use super::*; + + #[interrupt] + unsafe fn EXTI7_0() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI15_8() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI25_16() { + on_irq(); + } + + pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { + use crate::pac::Interrupt; - qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI25_16 as u8); + } } -} -#[cfg(all(gpio_v3, ch641))] -mod irq_impl { - use super::*; + #[cfg(all(gpio_v3, not(ch641)))] + mod irq_impl { + use super::*; + + #[interrupt] + unsafe fn EXTI0() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI1() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI2() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI3() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI4() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI9_5() { + on_irq(); + } + #[interrupt] + unsafe fn EXTI15_10() { + on_irq(); + } - #[interrupt] - unsafe fn EXTI7_0() { - on_irq(); + pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { + use crate::pac::Interrupt; + + qingke::pfic::enable_interrupt(Interrupt::EXTI0 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI1 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI2 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI3 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI4 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI9_5 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI15_10 as u8); + } } - #[interrupt] - unsafe fn EXTI15_8() { - on_irq(); + #[cfg(gpio_v0)] + mod irq_impl { + use super::*; + + #[interrupt] + unsafe fn EXTI7_0() { + on_irq(); + } + + pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { + use crate::pac::Interrupt; + + qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); + } } - pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { - use crate::pac::Interrupt; + #[cfg(all(gpio_v3, ch641))] + mod irq_impl { + use super::*; + + #[interrupt] + unsafe fn EXTI7_0() { + on_irq(); + } + + #[interrupt] + unsafe fn EXTI15_8() { + on_irq(); + } + + pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { + use crate::pac::Interrupt; - qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); - qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); + qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); + } } + pub(crate) use irq_impl::*; } - -pub(crate) use irq_impl::*; diff --git a/src/lib.rs b/src/lib.rs index 997da34..b451a55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,6 +140,7 @@ pub fn init(config: Config) -> Peripherals { ::critical_section::with(|cs| unsafe { gpio::init(cs); dma::init(cs, config.dma_interrupt_priority); + #[cfg(feature = "exti")] exti::init(cs); });